commit
391782253f
|
@ -21,16 +21,19 @@ function print_success {
|
||||||
|
|
||||||
function print_error {
|
function print_error {
|
||||||
tput setaf 1; echo " ERROR: $1"
|
tput setaf 1; echo " ERROR: $1"
|
||||||
|
tput setaf 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function print_ok {
|
function print_ok {
|
||||||
tput setaf 2; echo " OK: $1"
|
tput setaf 2; echo " OK: $1"
|
||||||
echo
|
echo
|
||||||
|
tput setaf 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function print_message {
|
function print_message {
|
||||||
tput setaf 3; echo "-------- $1"
|
tput setaf 3; echo "-------- $1"
|
||||||
echo
|
echo
|
||||||
|
tput setaf 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function print_title {
|
function print_title {
|
||||||
|
@ -38,4 +41,5 @@ function print_title {
|
||||||
echo
|
echo
|
||||||
tput setaf 5; echo "$stepnumber $1"
|
tput setaf 5; echo "$stepnumber $1"
|
||||||
tput setaf 5; echo '=================='
|
tput setaf 5; echo '=================='
|
||||||
|
tput setaf 0
|
||||||
}
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script for converting a PHP WS structure to a TS type.
|
||||||
|
*
|
||||||
|
* The first parameter (required) is the path to the Moodle installation to use.
|
||||||
|
* The second parameter (required) is the name to the WS to convert.
|
||||||
|
* The third parameter (optional) is a number: 1 to convert the params structure,
|
||||||
|
* 0 to convert the returns structure. Defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!isset($argv[1])) {
|
||||||
|
echo "ERROR: Please pass the path to the folder containing the Moodle installations as the first parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($argv[2])) {
|
||||||
|
echo "ERROR: Please pass the WS name as the second parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
require_once('ws_to_ts_functions.php');
|
||||||
|
|
||||||
|
$versions = array('master', '37', '36', '35', '34', '33', '32', '31');
|
||||||
|
|
||||||
|
$moodlespath = $argv[1];
|
||||||
|
$wsname = $argv[2];
|
||||||
|
$useparams = !!(isset($argv[3]) && $argv[3]);
|
||||||
|
$pathseparator = '/';
|
||||||
|
|
||||||
|
// Get the path to the script.
|
||||||
|
$index = strrpos(__FILE__, $pathseparator);
|
||||||
|
if ($index === false) {
|
||||||
|
$pathseparator = '\\';
|
||||||
|
$index = strrpos(__FILE__, $pathseparator);
|
||||||
|
}
|
||||||
|
$scriptfolder = substr(__FILE__, 0, $index);
|
||||||
|
$scriptpath = concatenate_paths($scriptfolder, 'get_ws_structure.php', $pathseparator);
|
||||||
|
|
||||||
|
$previousstructure = null;
|
||||||
|
$previousversion = null;
|
||||||
|
$libsloaded = false;
|
||||||
|
|
||||||
|
foreach ($versions as $version) {
|
||||||
|
$moodlepath = concatenate_paths($moodlespath, 'stable_' . $version, $pathseparator);
|
||||||
|
|
||||||
|
if (!$libsloaded) {
|
||||||
|
$libsloaded = true;
|
||||||
|
|
||||||
|
require($moodlepath . '/config.php');
|
||||||
|
require($CFG->dirroot . '/webservice/lib.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the structure in this Moodle version.
|
||||||
|
$structure = shell_exec("php $scriptpath $moodlepath $wsname " . ($useparams ? 'true' : ''));
|
||||||
|
|
||||||
|
if (strpos($structure, 'ERROR:') === 0) {
|
||||||
|
echo "WS not found in version $version. Stop.\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$structure = unserialize($structure);
|
||||||
|
|
||||||
|
if ($previousstructure != null) {
|
||||||
|
echo "*** Check changes from version $version to $previousversion ***\n";
|
||||||
|
|
||||||
|
$messages = detect_ws_changes($previousstructure, $structure);
|
||||||
|
|
||||||
|
if (count($messages) > 0) {
|
||||||
|
$haschanged = true;
|
||||||
|
|
||||||
|
foreach($messages as $message) {
|
||||||
|
echo "$message\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "No changes found.\n";
|
||||||
|
}
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$previousstructure = $structure;
|
||||||
|
$previousversion = $version;
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script for getting the PHP structure of a WS returns or params.
|
||||||
|
*
|
||||||
|
* The first parameter (required) is the path to the Moodle installation to use.
|
||||||
|
* The second parameter (required) is the name to the WS to convert.
|
||||||
|
* The third parameter (optional) is a number: 1 to convert the params structure,
|
||||||
|
* 0 to convert the returns structure. Defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!isset($argv[1])) {
|
||||||
|
echo "ERROR: Please pass the Moodle path as the first parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($argv[2])) {
|
||||||
|
echo "ERROR: Please pass the WS name as the second parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$moodlepath = $argv[1];
|
||||||
|
$wsname = $argv[2];
|
||||||
|
$useparams = !!(isset($argv[3]) && $argv[3]);
|
||||||
|
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
|
||||||
|
require($moodlepath . '/config.php');
|
||||||
|
require($CFG->dirroot . '/webservice/lib.php');
|
||||||
|
require_once('ws_to_ts_functions.php');
|
||||||
|
|
||||||
|
$structure = get_ws_structure($wsname, $useparams);
|
||||||
|
|
||||||
|
if ($structure === false) {
|
||||||
|
echo "ERROR: The WS wasn't found in this Moodle installation.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_default_closures($structure);
|
||||||
|
echo serialize($structure);
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script for converting a PHP WS structure to a TS type.
|
||||||
|
*
|
||||||
|
* The first parameter (required) is the path to the Moodle installation to use.
|
||||||
|
* The second parameter (required) is the name to the WS to convert.
|
||||||
|
* The third parameter (optional) is the name to put to the TS type. Defaults to "TypeName".
|
||||||
|
* The fourth parameter (optional) is a number: 1 to convert the params structure,
|
||||||
|
* 0 to convert the returns structure. Defaults to 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!isset($argv[1])) {
|
||||||
|
echo "ERROR: Please pass the Moodle path as the first parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($argv[2])) {
|
||||||
|
echo "ERROR: Please pass the WS name as the second parameter.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$moodlepath = $argv[1];
|
||||||
|
$wsname = $argv[2];
|
||||||
|
$typename = isset($argv[3]) ? $argv[3] : 'TypeName';
|
||||||
|
$useparams = !!(isset($argv[4]) && $argv[4]);
|
||||||
|
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
|
||||||
|
require($moodlepath . '/config.php');
|
||||||
|
require($CFG->dirroot . '/webservice/lib.php');
|
||||||
|
require_once('ws_to_ts_functions.php');
|
||||||
|
|
||||||
|
$structure = get_ws_structure($wsname, $useparams);
|
||||||
|
|
||||||
|
if ($structure === false) {
|
||||||
|
echo "ERROR: The WS wasn't found in this Moodle installation.\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($useparams) {
|
||||||
|
$description = "Params of WS $wsname.";
|
||||||
|
} else {
|
||||||
|
$description = "Result of WS $wsname.";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo get_ts_doc(null, $description, '') . "export type $typename = " . convert_to_ts(null, $structure, $useparams) . ";\n";
|
|
@ -0,0 +1,244 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions for converting a Moodle WS structure to a TS type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the structure of a WS params or returns.
|
||||||
|
*/
|
||||||
|
function get_ws_structure($wsname, $useparams) {
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
// get all the function descriptions
|
||||||
|
$functions = $DB->get_records('external_functions', array(), 'name');
|
||||||
|
$functiondescs = array();
|
||||||
|
foreach ($functions as $function) {
|
||||||
|
$functiondescs[$function->name] = external_api::external_function_info($function);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($functiondescs[$wsname])) {
|
||||||
|
return false;
|
||||||
|
} else if ($useparams) {
|
||||||
|
return $functiondescs[$wsname]->parameters_desc;
|
||||||
|
} else {
|
||||||
|
return $functiondescs[$wsname]->returns_desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix a comment: make sure first letter is uppercase and add a dot at the end if needed.
|
||||||
|
*/
|
||||||
|
function fix_comment($desc) {
|
||||||
|
$desc = trim($desc);
|
||||||
|
$desc = ucfirst($desc);
|
||||||
|
|
||||||
|
if (substr($desc, -1) !== '.') {
|
||||||
|
$desc .= '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an inline comment based on a certain text.
|
||||||
|
*/
|
||||||
|
function get_inline_comment($desc) {
|
||||||
|
if (empty($desc)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' // ' . fix_comment($desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the TS documentation of a certain element.
|
||||||
|
*/
|
||||||
|
function get_ts_doc($type, $desc, $indentation) {
|
||||||
|
if (empty($desc)) {
|
||||||
|
// If no key, it's probably in an array. We only document object properties.
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $indentation . "/**\n" .
|
||||||
|
$indentation . " * " . fix_comment($desc) . "\n" .
|
||||||
|
(!empty($type) ? ($indentation . " * @type {" . $type . "}\n") : '') .
|
||||||
|
$indentation . " */\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a certain type, with or without a key.
|
||||||
|
*/
|
||||||
|
function convert_key_type($key, $type, $required, $indentation) {
|
||||||
|
if ($key) {
|
||||||
|
// It has a key, it's inside an object.
|
||||||
|
return $indentation . "$key" . ($required == VALUE_OPTIONAL ? '?' : '') . ": $type";
|
||||||
|
} else {
|
||||||
|
// No key, it's probably in an array. Just include the type.
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a certain element into a TS structure.
|
||||||
|
*/
|
||||||
|
function convert_to_ts($key, $value, $boolisnumber = false, $indentation = '', $arraydesc = '') {
|
||||||
|
if ($value instanceof external_value || $value instanceof external_warnings || $value instanceof external_files) {
|
||||||
|
// It's a basic field or a pre-defined type like warnings.
|
||||||
|
$type = 'string';
|
||||||
|
|
||||||
|
if ($value instanceof external_warnings) {
|
||||||
|
$type = 'CoreWSExternalWarning[]';
|
||||||
|
} else if ($value instanceof external_files) {
|
||||||
|
$type = 'CoreWSExternalFile[]';
|
||||||
|
} else if ($value->type == PARAM_BOOL && !$boolisnumber) {
|
||||||
|
$type = 'boolean';
|
||||||
|
} else if (($value->type == PARAM_BOOL && $boolisnumber) || $value->type == PARAM_INT || $value->type == PARAM_FLOAT ||
|
||||||
|
$value->type == PARAM_LOCALISEDFLOAT || $value->type == PARAM_PERMISSION || $value->type == PARAM_INTEGER ||
|
||||||
|
$value->type == PARAM_NUMBER) {
|
||||||
|
$type = 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = convert_key_type($key, $type, $value->required, $indentation);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} else if ($value instanceof external_single_structure) {
|
||||||
|
// It's an object.
|
||||||
|
$result = convert_key_type($key, '{', $value->required, $indentation);
|
||||||
|
|
||||||
|
if ($arraydesc) {
|
||||||
|
// It's an array of objects. Print the array description now.
|
||||||
|
$result .= get_inline_comment($arraydesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= "\n";
|
||||||
|
|
||||||
|
foreach ($value->keys as $key => $value) {
|
||||||
|
$result .= convert_to_ts($key, $value, $boolisnumber, $indentation . ' ') . ';';
|
||||||
|
|
||||||
|
if (!$value instanceof external_multiple_structure || !$value->content instanceof external_single_structure) {
|
||||||
|
// Add inline comments after the field, except for arrays of objects where it's added at the start.
|
||||||
|
$result .= get_inline_comment($value->desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= "$indentation}";
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} else if ($value instanceof external_multiple_structure) {
|
||||||
|
// It's an array.
|
||||||
|
$result = convert_key_type($key, '', $value->required, $indentation);
|
||||||
|
|
||||||
|
$result .= convert_to_ts(null, $value->content, $boolisnumber, $indentation, $value->desc);
|
||||||
|
|
||||||
|
$result .= "[]";
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
} else {
|
||||||
|
echo "WARNING: Unknown structure: $key " . get_class($value) . " \n";
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate two paths.
|
||||||
|
*/
|
||||||
|
function concatenate_paths($left, $right, $separator = '/') {
|
||||||
|
if (!is_string($left) || $left == '') {
|
||||||
|
return $right;
|
||||||
|
} else if (!is_string($right) || $right == '') {
|
||||||
|
return $left;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastCharLeft = substr($left, -1);
|
||||||
|
$firstCharRight = $right[0];
|
||||||
|
|
||||||
|
if ($lastCharLeft === $separator && $firstCharRight === $separator) {
|
||||||
|
return $left . substr($right, 1);
|
||||||
|
} else if ($lastCharLeft !== $separator && $firstCharRight !== '/') {
|
||||||
|
return $left . '/' . $right;
|
||||||
|
} else {
|
||||||
|
return $left . $right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect changes between 2 WS structures. We only detect fields that have been added or modified, not removed fields.
|
||||||
|
*/
|
||||||
|
function detect_ws_changes($new, $old, $key = '', $path = '') {
|
||||||
|
$messages = [];
|
||||||
|
|
||||||
|
if (gettype($new) != gettype($old)) {
|
||||||
|
// The type has changed.
|
||||||
|
$messages[] = "Property '$key' has changed type, from '" . gettype($old) . "' to '" . gettype($new) .
|
||||||
|
($path != '' ? "' inside $path." : "'.");
|
||||||
|
|
||||||
|
} else if ($new instanceof external_value && $new->type != $old->type) {
|
||||||
|
// The type has changed.
|
||||||
|
$messages[] = "Property '$key' has changed type, from '" . $old->type . "' to '" . $new->type .
|
||||||
|
($path != '' ? "' inside $path." : "'.");
|
||||||
|
|
||||||
|
} else if ($new instanceof external_warnings || $new instanceof external_files) {
|
||||||
|
// Ignore these types.
|
||||||
|
|
||||||
|
} else if ($new instanceof external_single_structure) {
|
||||||
|
// Check each subproperty.
|
||||||
|
$newpath = ($path != '' ? "$path." : '') . $key;
|
||||||
|
|
||||||
|
foreach ($new->keys as $subkey => $value) {
|
||||||
|
if (!isset($old->keys[$subkey])) {
|
||||||
|
// New property.
|
||||||
|
$messages[] = "New property '$subkey' found" . ($newpath != '' ? " inside '$newpath'." : '.');
|
||||||
|
} else {
|
||||||
|
$messages = array_merge($messages, detect_ws_changes($value, $old->keys[$subkey], $subkey, $newpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ($new instanceof external_multiple_structure) {
|
||||||
|
// Recursive call with the content.
|
||||||
|
$messages = array_merge($messages, detect_ws_changes($new->content, $old->content, $key, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all closures (anonymous functions) in the default values so the object can be serialized.
|
||||||
|
*/
|
||||||
|
function remove_default_closures($value) {
|
||||||
|
if ($value instanceof external_warnings || $value instanceof external_files) {
|
||||||
|
// Ignore these types.
|
||||||
|
|
||||||
|
} else if ($value instanceof external_value) {
|
||||||
|
if ($value->default instanceof Closure) {
|
||||||
|
$value->default = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ($value instanceof external_single_structure) {
|
||||||
|
|
||||||
|
foreach ($value->keys as $key => $subvalue) {
|
||||||
|
remove_default_closures($subvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ($value instanceof external_multiple_structure) {
|
||||||
|
remove_default_closures($value->content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title>{{badge.name}}</ion-title>
|
<ion-title>{{badge && badge.name}}</ion-title>
|
||||||
</ion-navbar>
|
</ion-navbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<core-loading [hideUntil]="badgeLoaded">
|
<core-loading [hideUntil]="badgeLoaded">
|
||||||
|
|
||||||
<ion-item-group>
|
<ion-item-group *ngIf="badge">
|
||||||
<ion-item text-wrap class="item-avatar-center">
|
<ion-item text-wrap class="item-avatar-center">
|
||||||
<img *ngIf="badge.badgeurl" class="avatar" [src]="badge.badgeurl" core-external-content [alt]="badge.name">
|
<img *ngIf="badge.badgeurl" class="avatar" [src]="badge.badgeurl" core-external-content [alt]="badge.name">
|
||||||
<ion-badge color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire">
|
<ion-badge color="danger" *ngIf="badge.dateexpire && currentTime >= badge.dateexpire">
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<ion-item-group>
|
<ion-item-group *ngIf="badge">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.issuerdetails' | translate}}</h2>
|
<h2>{{ 'addon.badges.issuerdetails' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<ion-item-group>
|
<ion-item-group *ngIf="badge">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.badgedetails' | translate}}</h2>
|
<h2>{{ 'addon.badges.badgedetails' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
<!-- Criteria (not yet avalaible) -->
|
<!-- Criteria (not yet avalaible) -->
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<ion-item-group>
|
<ion-item-group *ngIf="badge">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.issuancedetails' | translate}}</h2>
|
<h2>{{ 'addon.badges.issuancedetails' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<!-- Endorsement -->
|
<!-- Endorsement -->
|
||||||
<ion-item-group *ngIf="badge.endorsement">
|
<ion-item-group *ngIf="badge && badge.endorsement">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.bendorsement' | translate}}</h2>
|
<h2>{{ 'addon.badges.bendorsement' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
@ -159,7 +159,7 @@
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<!-- Related badges -->
|
<!-- Related badges -->
|
||||||
<ion-item-group *ngIf="badge.relatedbadges">
|
<ion-item-group *ngIf="badge && badge.relatedbadges">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.relatedbages' | translate}}</h2>
|
<h2>{{ 'addon.badges.relatedbages' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
@ -172,14 +172,14 @@
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
||||||
<!-- Competencies alignment -->
|
<!-- Competencies alignment -->
|
||||||
<ion-item-group *ngIf="badge.competencies">
|
<ion-item-group *ngIf="badge && badge.alignment">
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.badges.alignment' | translate}}</h2>
|
<h2>{{ 'addon.badges.alignment' | translate}}</h2>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<a ion-item text-wrap *ngFor="let competency of badge.competencies" [href]="competency.targeturl" core-link auto-login="no">
|
<a ion-item text-wrap *ngFor="let alignment of badge.alignment" [href]="alignment.targeturl" core-link auto-login="no">
|
||||||
<h2><core-format-text [text]="competency.targetname"></core-format-text></h2>
|
<h2><core-format-text [text]="alignment.targetname"></core-format-text></h2>
|
||||||
</a>
|
</a>
|
||||||
<ion-item text-wrap *ngIf="badge.competencies.length == 0">
|
<ion-item text-wrap *ngIf="badge.alignment.length == 0">
|
||||||
<h2>{{ 'addon.badges.noalignment' | translate}}</h2>
|
<h2>{{ 'addon.badges.noalignment' | translate}}</h2>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-item-group>
|
</ion-item-group>
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
import { AddonBadgesProvider } from '../../providers/badges';
|
import { AddonBadgesProvider, AddonBadgesUserBadge } from '../../providers/badges';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of calendar events.
|
* Page that displays the list of calendar events.
|
||||||
|
@ -38,7 +38,7 @@ export class AddonBadgesIssuedBadgePage {
|
||||||
|
|
||||||
user: any = {};
|
user: any = {};
|
||||||
course: any = {};
|
course: any = {};
|
||||||
badge: any = {};
|
badge: AddonBadgesUserBadge;
|
||||||
|
|
||||||
badgeLoaded = false;
|
badgeLoaded = false;
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { IonicPage, Content, NavParams } from 'ionic-angular';
|
import { IonicPage, Content, NavParams } from 'ionic-angular';
|
||||||
import { AddonBadgesProvider } from '../../providers/badges';
|
import { AddonBadgesProvider, AddonBadgesUserBadge } from '../../providers/badges';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
@ -36,7 +36,7 @@ export class AddonBadgesUserBadgesPage {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
|
||||||
badgesLoaded = false;
|
badgesLoaded = false;
|
||||||
badges = [];
|
badges: AddonBadgesUserBadge[] = [];
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
badgeHash: string;
|
badgeHash: string;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { CoreWSExternalWarning } from '@providers/ws';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +71,7 @@ export class AddonBadgesProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when the badges are retrieved.
|
* @return Promise to be resolved when the badges are retrieved.
|
||||||
*/
|
*/
|
||||||
getUserBadges(courseId: number, userId: number, siteId?: string): Promise<any> {
|
getUserBadges(courseId: number, userId: number, siteId?: string): Promise<AddonBadgesUserBadge[]> {
|
||||||
|
|
||||||
this.logger.debug('Get badges for course ' + courseId);
|
this.logger.debug('Get badges for course ' + courseId);
|
||||||
|
|
||||||
|
@ -85,8 +86,13 @@ export class AddonBadgesProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_badges_get_user_badges', data, preSets).then((response) => {
|
return site.read('core_badges_get_user_badges', data, preSets).then((response: AddonBadgesGetUserBadgesResult): any => {
|
||||||
if (response && response.badges) {
|
if (response && response.badges) {
|
||||||
|
// In 3.7, competencies was renamed to alignment. Rename the property in 3.6 too.
|
||||||
|
response.badges.forEach((badge) => {
|
||||||
|
badge.alignment = badge.alignment || badge.competencies;
|
||||||
|
});
|
||||||
|
|
||||||
return response.badges;
|
return response.badges;
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
|
@ -110,3 +116,85 @@ export class AddonBadgesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_badges_get_user_badges.
|
||||||
|
*/
|
||||||
|
export type AddonBadgesGetUserBadgesResult = {
|
||||||
|
badges: AddonBadgesUserBadge[]; // List of badges.
|
||||||
|
warnings?: CoreWSExternalWarning[]; // List of warnings.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Badge data returned by WS core_badges_get_user_badges.
|
||||||
|
*/
|
||||||
|
export type AddonBadgesUserBadge = {
|
||||||
|
id?: number; // Badge id.
|
||||||
|
name: string; // Badge name.
|
||||||
|
description: string; // Badge description.
|
||||||
|
timecreated?: number; // Time created.
|
||||||
|
timemodified?: number; // Time modified.
|
||||||
|
usercreated?: number; // User created.
|
||||||
|
usermodified?: number; // User modified.
|
||||||
|
issuername: string; // Issuer name.
|
||||||
|
issuerurl: string; // Issuer URL.
|
||||||
|
issuercontact: string; // Issuer contact.
|
||||||
|
expiredate?: number; // Expire date.
|
||||||
|
expireperiod?: number; // Expire period.
|
||||||
|
type?: number; // Type.
|
||||||
|
courseid?: number; // Course id.
|
||||||
|
message?: string; // Message.
|
||||||
|
messagesubject?: string; // Message subject.
|
||||||
|
attachment?: number; // Attachment.
|
||||||
|
notification?: number; // @since 3.6. Whether to notify when badge is awarded.
|
||||||
|
nextcron?: number; // @since 3.6. Next cron.
|
||||||
|
status?: number; // Status.
|
||||||
|
issuedid?: number; // Issued id.
|
||||||
|
uniquehash: string; // Unique hash.
|
||||||
|
dateissued: number; // Date issued.
|
||||||
|
dateexpire: number; // Date expire.
|
||||||
|
visible?: number; // Visible.
|
||||||
|
email?: string; // @since 3.6. User email.
|
||||||
|
version?: string; // @since 3.6. Version.
|
||||||
|
language?: string; // @since 3.6. Language.
|
||||||
|
imageauthorname?: string; // @since 3.6. Name of the image author.
|
||||||
|
imageauthoremail?: string; // @since 3.6. Email of the image author.
|
||||||
|
imageauthorurl?: string; // @since 3.6. URL of the image author.
|
||||||
|
imagecaption?: string; // @since 3.6. Caption of the image.
|
||||||
|
badgeurl: string; // Badge URL.
|
||||||
|
endorsement?: { // @since 3.6.
|
||||||
|
id: number; // Endorsement id.
|
||||||
|
badgeid: number; // Badge id.
|
||||||
|
issuername: string; // Endorsement issuer name.
|
||||||
|
issuerurl: string; // Endorsement issuer URL.
|
||||||
|
issueremail: string; // Endorsement issuer email.
|
||||||
|
claimid: string; // Claim URL.
|
||||||
|
claimcomment: string; // Claim comment.
|
||||||
|
dateissued: number; // Date issued.
|
||||||
|
};
|
||||||
|
alignment?: { // @since 3.7. Calculated by the app for 3.6 sites. Badge alignments.
|
||||||
|
id?: number; // Alignment id.
|
||||||
|
badgeid?: number; // Badge id.
|
||||||
|
targetName?: string; // Target name.
|
||||||
|
targetUrl?: string; // Target URL.
|
||||||
|
targetDescription?: string; // Target description.
|
||||||
|
targetFramework?: string; // Target framework.
|
||||||
|
targetCode?: string; // Target code.
|
||||||
|
}[];
|
||||||
|
competencies?: { // @deprecated from 3.7. @since 3.6. In 3.7 it was renamed to alignment.
|
||||||
|
id?: number; // Alignment id.
|
||||||
|
badgeid?: number; // Badge id.
|
||||||
|
targetName?: string; // Target name.
|
||||||
|
targetUrl?: string; // Target URL.
|
||||||
|
targetDescription?: string; // Target description.
|
||||||
|
targetFramework?: string; // Target framework.
|
||||||
|
targetCode?: string; // Target code.
|
||||||
|
}[];
|
||||||
|
relatedbadges?: { // @since 3.6. Related badges.
|
||||||
|
id: number; // Badge id.
|
||||||
|
name: string; // Badge name.
|
||||||
|
version?: string; // Version.
|
||||||
|
language?: string; // Language.
|
||||||
|
type?: number; // Type.
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,9 @@ import { Component, OnInit, Injector, Optional } from '@angular/core';
|
||||||
import { NavController } from 'ionic-angular';
|
import { NavController } from 'ionic-angular';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
||||||
import { AddonBlockRecentlyAccessedItemsProvider } from '../../providers/recentlyaccesseditems';
|
import {
|
||||||
|
AddonBlockRecentlyAccessedItemsProvider, AddonBlockRecentlyAccessedItemsItem
|
||||||
|
} from '../../providers/recentlyaccesseditems';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/hel
|
||||||
templateUrl: 'addon-block-recentlyaccesseditems.html'
|
templateUrl: 'addon-block-recentlyaccesseditems.html'
|
||||||
})
|
})
|
||||||
export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit {
|
export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit {
|
||||||
items = [];
|
items: AddonBlockRecentlyAccessedItemsItem[] = [];
|
||||||
|
|
||||||
protected fetchContentDefaultError = 'Error getting recently accessed items data.';
|
protected fetchContentDefaultError = 'Error getting recently accessed items data.';
|
||||||
|
|
||||||
|
|
|
@ -42,14 +42,16 @@ export class AddonBlockRecentlyAccessedItemsProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved when the info is retrieved.
|
* @return Promise resolved when the info is retrieved.
|
||||||
*/
|
*/
|
||||||
getRecentItems(siteId?: string): Promise<any[]> {
|
getRecentItems(siteId?: string): Promise<AddonBlockRecentlyAccessedItemsItem[]> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const preSets = {
|
const preSets = {
|
||||||
cacheKey: this.getRecentItemsCacheKey()
|
cacheKey: this.getRecentItemsCacheKey()
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets).then((items) => {
|
return site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets)
|
||||||
|
.then((items: AddonBlockRecentlyAccessedItemsItem[]) => {
|
||||||
|
|
||||||
return items.map((item) => {
|
return items.map((item) => {
|
||||||
const modicon = item.icon && this.domUtils.getHTMLElementAttribute(item.icon, 'src');
|
const modicon = item.icon && this.domUtils.getHTMLElementAttribute(item.icon, 'src');
|
||||||
item.iconUrl = this.courseProvider.getModuleIconSrc(item.modname, modicon);
|
item.iconUrl = this.courseProvider.getModuleIconSrc(item.modname, modicon);
|
||||||
|
@ -72,3 +74,27 @@ export class AddonBlockRecentlyAccessedItemsProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS block_recentlyaccesseditems_get_recent_items.
|
||||||
|
*/
|
||||||
|
export type AddonBlockRecentlyAccessedItemsItem = {
|
||||||
|
id: number; // Id.
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
cmid: number; // Cmid.
|
||||||
|
userid: number; // Userid.
|
||||||
|
modname: string; // Modname.
|
||||||
|
name: string; // Name.
|
||||||
|
coursename: string; // Coursename.
|
||||||
|
timeaccess: number; // Timeaccess.
|
||||||
|
viewurl: string; // Viewurl.
|
||||||
|
courseviewurl: string; // Courseviewurl.
|
||||||
|
icon: string; // Icon.
|
||||||
|
} & AddonBlockRecentlyAccessedItemsItemCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for recently accessed item.
|
||||||
|
*/
|
||||||
|
export type AddonBlockRecentlyAccessedItemsItemCalculatedData = {
|
||||||
|
iconUrl: string; // Icon URL. Calculated by the app.
|
||||||
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreCoursesHelperProvider } from '@core/courses/providers/helper';
|
||||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||||
import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
||||||
import { AddonBlockTimelineProvider } from '../../providers/timeline';
|
import { AddonBlockTimelineProvider } from '../../providers/timeline';
|
||||||
|
import { AddonCalendarEvent } from '@addon/calendar/providers/calendar';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to render a timeline block.
|
* Component to render a timeline block.
|
||||||
|
@ -34,9 +35,9 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen
|
||||||
filter = 'next30days';
|
filter = 'next30days';
|
||||||
currentSite: any;
|
currentSite: any;
|
||||||
timeline = {
|
timeline = {
|
||||||
events: [],
|
events: <AddonCalendarEvent[]> [],
|
||||||
loaded: false,
|
loaded: false,
|
||||||
canLoadMore: undefined
|
canLoadMore: <number> undefined
|
||||||
};
|
};
|
||||||
timelineCourses = {
|
timelineCourses = {
|
||||||
courses: [],
|
courses: [],
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreCoursesDashboardProvider } from '@core/courses/providers/dashboard';
|
import { CoreCoursesDashboardProvider } from '@core/courses/providers/dashboard';
|
||||||
|
import { AddonCalendarEvents, AddonCalendarEventsGroupedByCourse, AddonCalendarEvent } from '@addon/calendar/providers/calendar';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +39,7 @@ export class AddonBlockTimelineProvider {
|
||||||
* @return Promise resolved when the info is retrieved.
|
* @return Promise resolved when the info is retrieved.
|
||||||
*/
|
*/
|
||||||
getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string):
|
getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string):
|
||||||
Promise<{ events: any[], canLoadMore: number }> {
|
Promise<{ events: AddonCalendarEvent[], canLoadMore: number }> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||||
|
@ -55,7 +56,9 @@ export class AddonBlockTimelineProvider {
|
||||||
data.aftereventid = afterEventId;
|
data.aftereventid = afterEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_action_events_by_course', data, preSets).then((courseEvents): any => {
|
return site.read('core_calendar_get_action_events_by_course', data, preSets)
|
||||||
|
.then((courseEvents: AddonCalendarEvents): any => {
|
||||||
|
|
||||||
if (courseEvents && courseEvents.events) {
|
if (courseEvents && courseEvents.events) {
|
||||||
return this.treatCourseEvents(courseEvents, time);
|
return this.treatCourseEvents(courseEvents, time);
|
||||||
}
|
}
|
||||||
|
@ -82,8 +85,9 @@ export class AddonBlockTimelineProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved when the info is retrieved.
|
* @return Promise resolved when the info is retrieved.
|
||||||
*/
|
*/
|
||||||
getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [s: string]:
|
getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [courseId: string]:
|
||||||
{ events: any[], canLoadMore: number } }> {
|
{ events: AddonCalendarEvent[], canLoadMore: number } }> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||||
data = {
|
data = {
|
||||||
|
@ -95,7 +99,9 @@ export class AddonBlockTimelineProvider {
|
||||||
cacheKey: this.getActionEventsByCoursesCacheKey()
|
cacheKey: this.getActionEventsByCoursesCacheKey()
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_calendar_get_action_events_by_courses', data, preSets).then((events): any => {
|
return site.read('core_calendar_get_action_events_by_courses', data, preSets)
|
||||||
|
.then((events: AddonCalendarEventsGroupedByCourse): any => {
|
||||||
|
|
||||||
if (events && events.groupedbycourse) {
|
if (events && events.groupedbycourse) {
|
||||||
const courseEvents = {};
|
const courseEvents = {};
|
||||||
|
|
||||||
|
@ -127,7 +133,9 @@ export class AddonBlockTimelineProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved when the info is retrieved.
|
* @return Promise resolved when the info is retrieved.
|
||||||
*/
|
*/
|
||||||
getActionEventsByTimesort(afterEventId: number, siteId?: string): Promise<{ events: any[], canLoadMore: number }> {
|
getActionEventsByTimesort(afterEventId: number, siteId?: string):
|
||||||
|
Promise<{ events: AddonCalendarEvent[], canLoadMore: number }> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||||
data: any = {
|
data: any = {
|
||||||
|
@ -144,12 +152,14 @@ export class AddonBlockTimelineProvider {
|
||||||
data.aftereventid = afterEventId;
|
data.aftereventid = afterEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_action_events_by_timesort', data, preSets).then((events): any => {
|
return site.read('core_calendar_get_action_events_by_timesort', data, preSets)
|
||||||
if (events && events.events) {
|
.then((result: AddonCalendarEvents): any => {
|
||||||
const canLoadMore = events.events.length >= data.limitnum ? events.lastid : undefined;
|
|
||||||
|
if (result && result.events) {
|
||||||
|
const canLoadMore = result.events.length >= data.limitnum ? result.lastid : undefined;
|
||||||
|
|
||||||
// Filter events by time in case it uses cache.
|
// Filter events by time in case it uses cache.
|
||||||
events = events.events.filter((element) => {
|
const events = result.events.filter((element) => {
|
||||||
return element.timesort >= time;
|
return element.timesort >= time;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -236,7 +246,9 @@ export class AddonBlockTimelineProvider {
|
||||||
* @param timeFrom Current time to filter events from.
|
* @param timeFrom Current time to filter events from.
|
||||||
* @return Object with course events and last loaded event id if more can be loaded.
|
* @return Object with course events and last loaded event id if more can be loaded.
|
||||||
*/
|
*/
|
||||||
protected treatCourseEvents(course: any, timeFrom: number): { events: any[], canLoadMore: number } {
|
protected treatCourseEvents(course: AddonCalendarEvents, timeFrom: number):
|
||||||
|
{ events: AddonCalendarEvent[], canLoadMore: number } {
|
||||||
|
|
||||||
const canLoadMore: number =
|
const canLoadMore: number =
|
||||||
course.events.length >= AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined;
|
course.events.length >= AddonBlockTimelineProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonBlogProvider } from '../../providers/blog';
|
import { AddonBlogProvider, AddonBlogPost } from '../../providers/blog';
|
||||||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||||
import { CoreTagProvider } from '@core/tag/providers/tag';
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
loaded = false;
|
loaded = false;
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
entries = [];
|
entries: AddonBlogPostFormatted[] = [];
|
||||||
currentUserId: number;
|
currentUserId: number;
|
||||||
showMyEntriesToggle = false;
|
showMyEntriesToggle = false;
|
||||||
onlyMyEntries = false;
|
onlyMyEntries = false;
|
||||||
|
@ -118,7 +118,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
|
const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
|
||||||
|
|
||||||
return this.blogProvider.getEntries(this.filter, loadPage).then((result) => {
|
return this.blogProvider.getEntries(this.filter, loadPage).then((result) => {
|
||||||
const promises = result.entries.map((entry) => {
|
const promises = result.entries.map((entry: AddonBlogPostFormatted) => {
|
||||||
switch (entry.publishstate) {
|
switch (entry.publishstate) {
|
||||||
case 'draft':
|
case 'draft':
|
||||||
entry.publishTranslated = 'publishtonoone';
|
entry.publishTranslated = 'publishtonoone';
|
||||||
|
@ -237,5 +237,12 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blog post with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonBlogPostFormatted = AddonBlogPost & {
|
||||||
|
publishTranslated?: string; // Calculated in the app. Key of the string to translate the publish state of the post.
|
||||||
|
user?: any; // Calculated in the app. Data of the user that wrote the post.
|
||||||
|
};
|
||||||
|
|
|
@ -18,6 +18,8 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle blog entries.
|
* Service to handle blog entries.
|
||||||
|
@ -68,7 +70,7 @@ export class AddonBlogProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when the entries are retrieved.
|
* @return Promise to be resolved when the entries are retrieved.
|
||||||
*/
|
*/
|
||||||
getEntries(filter: any = {}, page: number = 0, siteId?: string): Promise<any> {
|
getEntries(filter: any = {}, page: number = 0, siteId?: string): Promise<AddonBlogGetEntriesResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value'),
|
filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value'),
|
||||||
|
@ -105,7 +107,7 @@ export class AddonBlogProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when done.
|
* @return Promise to be resolved when done.
|
||||||
*/
|
*/
|
||||||
logView(filter: any = {}, siteId?: string): Promise<any> {
|
logView(filter: any = {}, siteId?: string): Promise<AddonBlogViewEntriesResult> {
|
||||||
this.pushNotificationsProvider.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId);
|
this.pushNotificationsProvider.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId);
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -117,3 +119,48 @@ export class AddonBlogProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by blog's post_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonBlogPost = {
|
||||||
|
id: number; // Post/entry id.
|
||||||
|
module: string; // Where it was published the post (blog, blog_external...).
|
||||||
|
userid: number; // Post author.
|
||||||
|
courseid: number; // Course where the post was created.
|
||||||
|
groupid: number; // Group post was created for.
|
||||||
|
moduleid: number; // Module id where the post was created (not used anymore).
|
||||||
|
coursemoduleid: number; // Course module id where the post was created.
|
||||||
|
subject: string; // Post subject.
|
||||||
|
summary: string; // Post summary.
|
||||||
|
summaryformat: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
content: string; // Post content.
|
||||||
|
uniquehash: string; // Post unique hash.
|
||||||
|
rating: number; // Post rating.
|
||||||
|
format: number; // Post content format.
|
||||||
|
attachment: string; // Post atachment.
|
||||||
|
publishstate: string; // Post publish state.
|
||||||
|
lastmodified: number; // When it was last modified.
|
||||||
|
created: number; // When it was created.
|
||||||
|
usermodified: number; // User that updated the post.
|
||||||
|
summaryfiles: CoreWSExternalFile[]; // Summaryfiles.
|
||||||
|
attachmentfiles?: CoreWSExternalFile[]; // Attachmentfiles.
|
||||||
|
tags?: CoreTagItem[]; // @since 3.7. Tags.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_blog_get_entries.
|
||||||
|
*/
|
||||||
|
export type AddonBlogGetEntriesResult = {
|
||||||
|
entries: AddonBlogPost[];
|
||||||
|
totalentries: number; // The total number of entries found.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_blog_view_entries.
|
||||||
|
*/
|
||||||
|
export type AddonBlogViewEntriesResult = {
|
||||||
|
status: boolean; // Status: true if success.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||||
import { AddonBlogEntriesComponent } from '../components/entries/entries';
|
import { AddonBlogEntriesComponent } from '../components/entries/entries';
|
||||||
import { AddonBlogProvider } from './blog';
|
import { AddonBlogProvider } from './blog';
|
||||||
|
import { CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Course nav handler.
|
* Course nav handler.
|
||||||
|
@ -100,7 +101,7 @@ export class AddonBlogCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||||
|
|
||||||
return this.blogProvider.getEntries({courseid: course.id}).then((result) => {
|
return this.blogProvider.getEntries({courseid: course.id}).then((result) => {
|
||||||
return result.entries.map((entry) => {
|
return result.entries.map((entry) => {
|
||||||
let files = [];
|
let files: CoreWSExternalFile[] = [];
|
||||||
|
|
||||||
if (entry.attachmentfiles && entry.attachmentfiles.length) {
|
if (entry.attachmentfiles && entry.attachmentfiles.length) {
|
||||||
files = entry.attachmentfiles;
|
files = entry.attachmentfiles;
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarWeek } from '../../providers/calendar';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
|
@ -44,7 +44,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
|
||||||
|
|
||||||
periodName: string;
|
periodName: string;
|
||||||
weekDays: any[];
|
weekDays: any[];
|
||||||
weeks: any[];
|
weeks: AddonCalendarWeek[];
|
||||||
loaded = false;
|
loaded = false;
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
isCurrentMonth: boolean;
|
isCurrentMonth: boolean;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<ng-container *ngFor="let event of filteredEvents">
|
<ng-container *ngFor="let event of filteredEvents">
|
||||||
<a ion-item text-wrap [title]="event.name" (click)="eventClicked(event)" [class.core-split-item-selected]="event.id == eventId" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
<a ion-item text-wrap [title]="event.name" (click)="eventClicked(event)" [class.core-split-item-selected]="event.id == eventId" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
||||||
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||||
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
||||||
<ion-note *ngIf="event.offline && !event.deleted" item-end>
|
<ion-note *ngIf="event.offline && !event.deleted" item-end>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
|
@ -43,8 +43,8 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges,
|
||||||
protected categoriesRetrieved = false;
|
protected categoriesRetrieved = false;
|
||||||
protected categories = {};
|
protected categories = {};
|
||||||
protected currentSiteId: string;
|
protected currentSiteId: string;
|
||||||
protected events = []; // Events (both online and offline).
|
protected events: AddonCalendarCalendarEvent[] = []; // Events (both online and offline).
|
||||||
protected onlineEvents = [];
|
protected onlineEvents: AddonCalendarCalendarEvent[] = [];
|
||||||
protected offlineEvents = []; // Offline events.
|
protected offlineEvents = []; // Offline events.
|
||||||
protected deletedEvents = []; // Events deleted in offline.
|
protected deletedEvents = []; // Events deleted in offline.
|
||||||
protected lookAhead: number;
|
protected lookAhead: number;
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<ng-container *ngFor="let event of filteredEvents">
|
<ng-container *ngFor="let event of filteredEvents">
|
||||||
<ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
<ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
||||||
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||||
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
||||||
<ion-note *ngIf="event.offline && !event.deleted" item-end>
|
<ion-note *ngIf="event.offline && !event.deleted" item-end>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||||
|
@ -45,7 +45,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
||||||
protected day: number;
|
protected day: number;
|
||||||
protected categories = {};
|
protected categories = {};
|
||||||
protected events = []; // Events (both online and offline).
|
protected events = []; // Events (both online and offline).
|
||||||
protected onlineEvents = [];
|
protected onlineEvents: AddonCalendarCalendarEvent[] = [];
|
||||||
protected offlineEvents = {}; // Offline events.
|
protected offlineEvents = {}; // Offline events.
|
||||||
protected offlineEditedEventsIds = []; // IDs of events edited in offline.
|
protected offlineEditedEventsIds = []; // IDs of events edited in offline.
|
||||||
protected deletedEvents = []; // Events deleted in offline.
|
protected deletedEvents = []; // Events deleted in offline.
|
||||||
|
@ -287,7 +287,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
||||||
return this.calendarProvider.getDayEvents(this.year, this.month, this.day).catch((error) => {
|
return this.calendarProvider.getDayEvents(this.year, this.month, this.day).catch((error) => {
|
||||||
if (!this.appProvider.isOnline()) {
|
if (!this.appProvider.isOnline()) {
|
||||||
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
||||||
return Promise.resolve({ events: [] });
|
return Promise.resolve({ events: <AddonCalendarCalendarEvent[]> [] });
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
<div *ngIf="event && event.repeatid" text-wrap radio-group [formControlName]="'repeateditall'" class="addon-calendar-radio-container">
|
<div *ngIf="event && event.repeatid" text-wrap radio-group [formControlName]="'repeateditall'" class="addon-calendar-radio-container">
|
||||||
<ion-item class="addon-calendar-radio-title"><h2>{{ 'addon.calendar.repeatedevents' | translate }}</h2></ion-item>
|
<ion-item class="addon-calendar-radio-title"><h2>{{ 'addon.calendar.repeatedevents' | translate }}</h2></ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>{{ 'addon.calendar.repeateditall' | translate:{$a: event.othereventscount} }}</ion-label>
|
<ion-label>{{ 'addon.calendar.repeateditall' | translate:{$a: otherEventsCount} }}</ion-label>
|
||||||
<ion-radio [value]="1"></ion-radio>
|
<ion-radio [value]="1"></ion-radio>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
|
|
|
@ -27,7 +27,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts';
|
import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts';
|
||||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||||
|
@ -58,7 +58,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
courseGroupSet = false;
|
courseGroupSet = false;
|
||||||
advanced = false;
|
advanced = false;
|
||||||
errors: any;
|
errors: any;
|
||||||
event: any; // The event object (when editing an event).
|
event: AddonCalendarEvent; // The event object (when editing an event).
|
||||||
|
otherEventsCount: number;
|
||||||
|
|
||||||
// Form variables.
|
// Form variables.
|
||||||
eventForm: FormGroup;
|
eventForm: FormGroup;
|
||||||
|
@ -70,7 +71,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected originalData: any;
|
protected originalData: any;
|
||||||
protected currentSite: CoreSite;
|
protected currentSite: CoreSite;
|
||||||
protected types: any; // Object with the supported types.
|
protected types: {[name: string]: boolean}; // Object with the supported types.
|
||||||
protected showAll: boolean;
|
protected showAll: boolean;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected error = false;
|
protected error = false;
|
||||||
|
@ -152,7 +153,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchData(refresh?: boolean): Promise<any> {
|
protected fetchData(refresh?: boolean): Promise<any> {
|
||||||
let accessInfo;
|
let accessInfo: AddonCalendarGetAccessInfoResult;
|
||||||
|
|
||||||
this.error = false;
|
this.error = false;
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
promises.push(this.calendarProvider.getEventById(this.eventId).then((event) => {
|
promises.push(this.calendarProvider.getEventById(this.eventId).then((event) => {
|
||||||
this.event = event;
|
this.event = event;
|
||||||
if (event && event.repeatid) {
|
if (event && event.repeatid) {
|
||||||
event.othereventscount = event.eventcount ? event.eventcount - 1 : '';
|
this.otherEventsCount = event.eventcount ? event.eventcount - 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
|
@ -489,7 +490,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Send the data.
|
// Send the data.
|
||||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||||
let event;
|
let event: AddonCalendarEvent;
|
||||||
|
|
||||||
this.calendarProvider.submitEvent(this.eventId, data).then((result) => {
|
this.calendarProvider.submitEvent(this.eventId, data).then((result) => {
|
||||||
event = result.event;
|
event = result.event;
|
||||||
|
@ -497,7 +498,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
||||||
if (result.sent) {
|
if (result.sent) {
|
||||||
// Event created or edited, invalidate right days & months.
|
// Event created or edited, invalidate right days & months.
|
||||||
const numberOfRepetitions = formData.repeat ? formData.repeats :
|
const numberOfRepetitions = formData.repeat ? formData.repeats :
|
||||||
(data.repeateditall && this.event.othereventscount ? this.event.othereventscount + 1 : 1);
|
(data.repeateditall && this.otherEventsCount ? this.otherEventsCount + 1 : 1);
|
||||||
|
|
||||||
return this.calendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions).catch(() => {
|
return this.calendarHelper.refreshAfterChangeEvent(result.event, numberOfRepetitions).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title>
|
<ion-title>
|
||||||
<img *ngIf="event && event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon">
|
<img *ngIf="event && event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon">
|
||||||
<core-icon *ngIf="event && event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event && event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<core-format-text *ngIf="event" [text]="event.name"></core-format-text>
|
<core-format-text *ngIf="event" [text]="event.name"></core-format-text>
|
||||||
</ion-title>
|
</ion-title>
|
||||||
<ion-buttons end>
|
<ion-buttons end>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
<ion-card-content *ngIf="event">
|
<ion-card-content *ngIf="event">
|
||||||
<ion-item text-wrap *ngIf="isSplitViewOn">
|
<ion-item text-wrap *ngIf="isSplitViewOn">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start alt="" role="presentation" class="core-module-icon">
|
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start alt="" role="presentation" class="core-module-icon">
|
||||||
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<h2>{{ 'addon.calendar.eventname' | translate }}</h2>
|
<h2>{{ 'addon.calendar.eventname' | translate }}</h2>
|
||||||
<p><core-format-text [text]="event.name"></core-format-text></p>
|
<p><core-format-text [text]="event.name"></core-format-text></p>
|
||||||
<ion-note item-end *ngIf="event.deleted">
|
<ion-note item-end *ngIf="event.deleted">
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
<a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId" class="addon-calendar-event" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon">
|
||||||
<core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon>
|
<core-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" item-start></core-icon>
|
||||||
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||||
<p>
|
<p>
|
||||||
{{ event.timestart * 1000 | coreFormatDate: "strftimetime" }}
|
{{ event.timestart * 1000 | coreFormatDate: "strftimetime" }}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core';
|
import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core';
|
||||||
import { IonicPage, Content, NavParams, NavController } from 'ionic-angular';
|
import { IonicPage, Content, NavParams, NavController } from 'ionic-angular';
|
||||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
import { AddonCalendarProvider, AddonCalendarGetEventsEvent } from '../../providers/calendar';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||||
|
@ -62,7 +62,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
||||||
protected manualSyncObserver: any;
|
protected manualSyncObserver: any;
|
||||||
protected onlineObserver: any;
|
protected onlineObserver: any;
|
||||||
protected currentSiteId: string;
|
protected currentSiteId: string;
|
||||||
protected onlineEvents = [];
|
protected onlineEvents: AddonCalendarGetEventsEvent[] = [];
|
||||||
protected offlineEvents = [];
|
protected offlineEvents = [];
|
||||||
protected deletedEvents = [];
|
protected deletedEvents = [];
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
||||||
eventsLoaded = false;
|
eventsLoaded = false;
|
||||||
events = []; // Events (both online and offline).
|
events = []; // Events (both online and offline).
|
||||||
notificationsEnabled = false;
|
notificationsEnabled = false;
|
||||||
filteredEvents = [];
|
filteredEvents: AddonCalendarGetEventsEvent[] = [];
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
courseId: number;
|
courseId: number;
|
||||||
|
@ -402,7 +402,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Filtered events.
|
* @return Filtered events.
|
||||||
*/
|
*/
|
||||||
protected getFilteredEvents(): any[] {
|
protected getFilteredEvents(): AddonCalendarGetEventsEvent[] {
|
||||||
if (!this.courseId) {
|
if (!this.courseId) {
|
||||||
// No filter, display everything.
|
// No filter, display everything.
|
||||||
return this.events;
|
return this.events;
|
||||||
|
@ -581,7 +581,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
||||||
* @param event Event info.
|
* @param event Event info.
|
||||||
* @return If date has changed and should be shown.
|
* @return If date has changed and should be shown.
|
||||||
*/
|
*/
|
||||||
protected endsSameDay(event: any): boolean {
|
protected endsSameDay(event: AddonCalendarGetEventsEvent): boolean {
|
||||||
if (!event.timeduration) {
|
if (!event.timeduration) {
|
||||||
// No duration.
|
// No duration.
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { SQLiteDB } from '@classes/sqlitedb';
|
||||||
import { AddonCalendarOfflineProvider } from './calendar-offline';
|
import { AddonCalendarOfflineProvider } from './calendar-offline';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreWSExternalWarning, CoreWSDate } from '@providers/ws';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -489,7 +490,7 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
deleteEventOnline(eventId: number, deleteAll?: boolean, siteId?: string): Promise<any> {
|
deleteEventOnline(eventId: number, deleteAll?: boolean, siteId?: string): Promise<null> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -535,22 +536,6 @@ export class AddonCalendarProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if event ends the same day or not.
|
|
||||||
*
|
|
||||||
* @param event Event info.
|
|
||||||
* @return If the .
|
|
||||||
*/
|
|
||||||
endsSameDay(event: any): boolean {
|
|
||||||
if (!event.timeduration) {
|
|
||||||
// No duration.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if day has changed.
|
|
||||||
return moment(event.timestart * 1000).isSame((event.timestart + event.timeduration) * 1000, 'day');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format event time. Similar to calendar_format_event_time.
|
* Format event time. Similar to calendar_format_event_time.
|
||||||
*
|
*
|
||||||
|
@ -562,8 +547,8 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the formatted event time.
|
* @return Promise resolved with the formatted event time.
|
||||||
*/
|
*/
|
||||||
formatEventTime(event: any, format: string, useCommonWords: boolean = true, seenDay?: number, showTime: number = 0,
|
formatEventTime(event: AddonCalendarAnyEvent, format: string, useCommonWords: boolean = true, seenDay?: number,
|
||||||
siteId?: string): Promise<string> {
|
showTime: number = 0, siteId?: string): Promise<string> {
|
||||||
|
|
||||||
const start = event.timestart * 1000,
|
const start = event.timestart * 1000,
|
||||||
end = (event.timestart + event.timeduration) * 1000;
|
end = (event.timestart + event.timeduration) * 1000;
|
||||||
|
@ -635,7 +620,7 @@ export class AddonCalendarProvider {
|
||||||
* @return Promise resolved with object with access information.
|
* @return Promise resolved with object with access information.
|
||||||
* @since 3.7
|
* @since 3.7
|
||||||
*/
|
*/
|
||||||
getAccessInformation(courseId?: number, siteId?: string): Promise<any> {
|
getAccessInformation(courseId?: number, siteId?: string): Promise<AddonCalendarGetAccessInfoResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params: any = {},
|
const params: any = {},
|
||||||
preSets = {
|
preSets = {
|
||||||
|
@ -680,7 +665,7 @@ export class AddonCalendarProvider {
|
||||||
* @return Promise resolved with an object indicating the types.
|
* @return Promise resolved with an object indicating the types.
|
||||||
* @since 3.7
|
* @since 3.7
|
||||||
*/
|
*/
|
||||||
getAllowedEventTypes(courseId?: number, siteId?: string): Promise<any> {
|
getAllowedEventTypes(courseId?: number, siteId?: string): Promise<{[name: string]: boolean}> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params: any = {},
|
const params: any = {},
|
||||||
preSets = {
|
preSets = {
|
||||||
|
@ -691,7 +676,8 @@ export class AddonCalendarProvider {
|
||||||
params.courseid = courseId;
|
params.courseid = courseId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_allowed_event_types', params, preSets).then((response) => {
|
return site.read('core_calendar_get_allowed_event_types', params, preSets)
|
||||||
|
.then((response: AddonCalendarGetAllowedEventTypesResult) => {
|
||||||
// Convert the array to an object.
|
// Convert the array to an object.
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
|
@ -812,11 +798,10 @@ export class AddonCalendarProvider {
|
||||||
* Get a calendar event. If the server request fails and data is not cached, try to get it from local DB.
|
* Get a calendar event. If the server request fails and data is not cached, try to get it from local DB.
|
||||||
*
|
*
|
||||||
* @param id Event ID.
|
* @param id Event ID.
|
||||||
* @param refresh True when we should update the event data.
|
|
||||||
* @param siteId ID of the site. If not defined, use current site.
|
* @param siteId ID of the site. If not defined, use current site.
|
||||||
* @return Promise resolved when the event data is retrieved.
|
* @return Promise resolved when the event data is retrieved.
|
||||||
*/
|
*/
|
||||||
getEvent(id: number, siteId?: string): Promise<any> {
|
getEvent(id: number, siteId?: string): Promise<AddonCalendarGetEventsEvent> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const preSets = {
|
const preSets = {
|
||||||
cacheKey: this.getEventCacheKey(id),
|
cacheKey: this.getEventCacheKey(id),
|
||||||
|
@ -834,7 +819,8 @@ export class AddonCalendarProvider {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_events', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_events', data, preSets)
|
||||||
|
.then((response: AddonCalendarGetEventsResult) => {
|
||||||
// The WebService returns all category events. Check the response to search for the event we want.
|
// The WebService returns all category events. Check the response to search for the event we want.
|
||||||
const event = response.events.find((e) => { return e.id == id; });
|
const event = response.events.find((e) => { return e.id == id; });
|
||||||
|
|
||||||
|
@ -849,12 +835,11 @@ export class AddonCalendarProvider {
|
||||||
* Get a calendar event by ID. This function returns more data than getEvent, but it isn't available in all Moodles.
|
* Get a calendar event by ID. This function returns more data than getEvent, but it isn't available in all Moodles.
|
||||||
*
|
*
|
||||||
* @param id Event ID.
|
* @param id Event ID.
|
||||||
* @param refresh True when we should update the event data.
|
|
||||||
* @param siteId ID of the site. If not defined, use current site.
|
* @param siteId ID of the site. If not defined, use current site.
|
||||||
* @return Promise resolved when the event data is retrieved.
|
* @return Promise resolved when the event data is retrieved.
|
||||||
* @since 3.4
|
* @since 3.4
|
||||||
*/
|
*/
|
||||||
getEventById(id: number, siteId?: string): Promise<any> {
|
getEventById(id: number, siteId?: string): Promise<AddonCalendarEvent> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const preSets = {
|
const preSets = {
|
||||||
cacheKey: this.getEventCacheKey(id),
|
cacheKey: this.getEventCacheKey(id),
|
||||||
|
@ -864,7 +849,8 @@ export class AddonCalendarProvider {
|
||||||
eventid: id
|
eventid: id
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_event_by_id', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_event_by_id', data, preSets)
|
||||||
|
.then((response: AddonCalendarGetEventByIdResult) => {
|
||||||
return response.event;
|
return response.event;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
return this.getEventFromLocalDb(id).catch(() => {
|
return this.getEventFromLocalDb(id).catch(() => {
|
||||||
|
@ -918,7 +904,7 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId ID of the site the event belongs to. If not defined, use current site.
|
* @param siteId ID of the site the event belongs to. If not defined, use current site.
|
||||||
* @return Promise resolved when the notification is updated.
|
* @return Promise resolved when the notification is updated.
|
||||||
*/
|
*/
|
||||||
addEventReminder(event: any, time: number, siteId?: string): Promise<any> {
|
addEventReminder(event: AddonCalendarAnyEvent, time: number, siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const reminder = {
|
const reminder = {
|
||||||
eventid: event.id,
|
eventid: event.id,
|
||||||
|
@ -976,7 +962,7 @@ export class AddonCalendarProvider {
|
||||||
* @return Promise resolved with the response.
|
* @return Promise resolved with the response.
|
||||||
*/
|
*/
|
||||||
getDayEvents(year: number, month: number, day: number, courseId?: number, categoryId?: number, ignoreCache?: boolean,
|
getDayEvents(year: number, month: number, day: number, courseId?: number, categoryId?: number, ignoreCache?: boolean,
|
||||||
siteId?: string): Promise<any> {
|
siteId?: string): Promise<AddonCalendarCalendarDay> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
@ -1003,7 +989,7 @@ export class AddonCalendarProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_day_view', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_day_view', data, preSets).then((response: AddonCalendarCalendarDay) => {
|
||||||
this.storeEventsInLocalDB(response.events, siteId);
|
this.storeEventsInLocalDB(response.events, siteId);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -1071,10 +1057,10 @@ export class AddonCalendarProvider {
|
||||||
* @param daysToStart Number of days from now to start getting events.
|
* @param daysToStart Number of days from now to start getting events.
|
||||||
* @param daysInterval Number of days between timestart and timeend.
|
* @param daysInterval Number of days between timestart and timeend.
|
||||||
* @param siteId Site to get the events from. If not defined, use current site.
|
* @param siteId Site to get the events from. If not defined, use current site.
|
||||||
* @return Promise to be resolved when the participants are retrieved.
|
* @return Promise to be resolved when the events are retrieved.
|
||||||
*/
|
*/
|
||||||
getEventsList(initialTime?: number, daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL,
|
getEventsList(initialTime?: number, daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL,
|
||||||
siteId?: string): Promise<any[]> {
|
siteId?: string): Promise<AddonCalendarGetEventsEvent[]> {
|
||||||
|
|
||||||
initialTime = initialTime || this.timeUtils.timestamp();
|
initialTime = initialTime || this.timeUtils.timestamp();
|
||||||
|
|
||||||
|
@ -1122,7 +1108,9 @@ export class AddonCalendarProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_events', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_events', data, preSets)
|
||||||
|
.then((response: AddonCalendarGetEventsResult) => {
|
||||||
|
|
||||||
if (!this.canViewMonthInSite(site)) {
|
if (!this.canViewMonthInSite(site)) {
|
||||||
// Store events only in 3.1-3.3. In 3.4+ we'll use the new WS that return more info.
|
// Store events only in 3.1-3.3. In 3.4+ we'll use the new WS that return more info.
|
||||||
this.storeEventsInLocalDB(response.events, siteId);
|
this.storeEventsInLocalDB(response.events, siteId);
|
||||||
|
@ -1178,7 +1166,7 @@ export class AddonCalendarProvider {
|
||||||
* @return Promise resolved with the response.
|
* @return Promise resolved with the response.
|
||||||
*/
|
*/
|
||||||
getMonthlyEvents(year: number, month: number, courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string)
|
getMonthlyEvents(year: number, month: number, courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string)
|
||||||
: Promise<any> {
|
: Promise<AddonCalendarMonth> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
@ -1210,7 +1198,9 @@ export class AddonCalendarProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_monthly_view', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_monthly_view', data, preSets)
|
||||||
|
.then((response: AddonCalendarMonth) => {
|
||||||
|
|
||||||
response.weeks.forEach((week) => {
|
response.weeks.forEach((week) => {
|
||||||
week.days.forEach((day) => {
|
week.days.forEach((day) => {
|
||||||
this.storeEventsInLocalDB(day.events, siteId);
|
this.storeEventsInLocalDB(day.events, siteId);
|
||||||
|
@ -1270,7 +1260,8 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the response.
|
* @return Promise resolved with the response.
|
||||||
*/
|
*/
|
||||||
getUpcomingEvents(courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getUpcomingEvents(courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string)
|
||||||
|
: Promise<AddonCalendarUpcoming> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
@ -1293,7 +1284,7 @@ export class AddonCalendarProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_calendar_get_calendar_upcoming_view', data, preSets).then((response) => {
|
return site.read('core_calendar_get_calendar_upcoming_view', data, preSets).then((response: AddonCalendarUpcoming) => {
|
||||||
this.storeEventsInLocalDB(response.events, siteId);
|
this.storeEventsInLocalDB(response.events, siteId);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -1604,11 +1595,14 @@ export class AddonCalendarProvider {
|
||||||
* If local notification plugin is not enabled, resolve the promise.
|
* If local notification plugin is not enabled, resolve the promise.
|
||||||
*
|
*
|
||||||
* @param event Event to schedule.
|
* @param event Event to schedule.
|
||||||
|
* @param reminderId The reminder ID.
|
||||||
* @param time Notification setting time (in minutes). E.g. 10 means "notificate 10 minutes before start".
|
* @param time Notification setting time (in minutes). E.g. 10 means "notificate 10 minutes before start".
|
||||||
* @param siteId Site ID the event belongs to. If not defined, use current site.
|
* @param siteId Site ID the event belongs to. If not defined, use current site.
|
||||||
* @return Promise resolved when the notification is scheduled.
|
* @return Promise resolved when the notification is scheduled.
|
||||||
*/
|
*/
|
||||||
protected scheduleEventNotification(event: any, reminderId: number, time: number, siteId?: string): Promise<void> {
|
protected scheduleEventNotification(event: AddonCalendarAnyEvent, reminderId: number, time: number, siteId?: string)
|
||||||
|
: Promise<void> {
|
||||||
|
|
||||||
if (this.localNotificationsProvider.isAvailable()) {
|
if (this.localNotificationsProvider.isAvailable()) {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
@ -1672,7 +1666,7 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId ID of the site the events belong to. If not defined, use current site.
|
* @param siteId ID of the site the events belong to. If not defined, use current site.
|
||||||
* @return Promise resolved when all the notifications have been scheduled.
|
* @return Promise resolved when all the notifications have been scheduled.
|
||||||
*/
|
*/
|
||||||
scheduleEventsNotifications(events: any[], siteId?: string): Promise<any[]> {
|
scheduleEventsNotifications(events: AddonCalendarAnyEvent[], siteId?: string): Promise<any[]> {
|
||||||
|
|
||||||
if (this.localNotificationsProvider.isAvailable()) {
|
if (this.localNotificationsProvider.isAvailable()) {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
@ -1803,11 +1797,10 @@ export class AddonCalendarProvider {
|
||||||
* @param timeCreated The time the event was created. Only if modifying a new offline event.
|
* @param timeCreated The time the event was created. Only if modifying a new offline event.
|
||||||
* @param forceOffline True to always save it in offline.
|
* @param forceOffline True to always save it in offline.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the event and a boolean indicating if data was
|
* @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline.
|
||||||
* sent to server or stored in offline.
|
|
||||||
*/
|
*/
|
||||||
submitEvent(eventId: number, formData: any, timeCreated?: number, forceOffline?: boolean, siteId?: string):
|
submitEvent(eventId: number, formData: any, timeCreated?: number, forceOffline?: boolean, siteId?: string):
|
||||||
Promise<{sent: boolean, event: any}> {
|
Promise<{sent: boolean, event: AddonCalendarEvent}> {
|
||||||
|
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
@ -1847,7 +1840,7 @@ export class AddonCalendarProvider {
|
||||||
* @param siteId Site ID. If not provided, current site.
|
* @param siteId Site ID. If not provided, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
submitEventOnline(eventId: number, formData: any, siteId?: string): Promise<any> {
|
submitEventOnline(eventId: number, formData: any, siteId?: string): Promise<AddonCalendarEvent> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
// Add data that is "hidden" in web.
|
// Add data that is "hidden" in web.
|
||||||
formData.id = eventId || 0;
|
formData.id = eventId || 0;
|
||||||
|
@ -1865,10 +1858,12 @@ export class AddonCalendarProvider {
|
||||||
formdata: this.utils.objectToGetParams(formData)
|
formdata: this.utils.objectToGetParams(formData)
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('core_calendar_submit_create_update_form', params).then((result) => {
|
return site.write('core_calendar_submit_create_update_form', params)
|
||||||
|
.then((result: AddonCalendarSubmitCreateUpdateFormResult): AddonCalendarEvent => {
|
||||||
|
|
||||||
if (result.validationerror) {
|
if (result.validationerror) {
|
||||||
// Simulate a WS error.
|
// Simulate a WS error.
|
||||||
return Promise.reject({
|
return <any> Promise.reject({
|
||||||
message: this.translate.instant('core.invalidformdata'),
|
message: this.translate.instant('core.invalidformdata'),
|
||||||
errorcode: 'validationerror'
|
errorcode: 'validationerror'
|
||||||
});
|
});
|
||||||
|
@ -1879,3 +1874,337 @@ export class AddonCalendarProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's events_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarEvents = {
|
||||||
|
events: AddonCalendarEvent[]; // Events.
|
||||||
|
firstid: number; // Firstid.
|
||||||
|
lastid: number; // Lastid.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's events_grouped_by_course_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarEventsGroupedByCourse = {
|
||||||
|
groupedbycourse: AddonCalendarEventsSameCourse[]; // Groupped by course.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's events_same_course_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarEventsSameCourse = AddonCalendarEvents & {
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's event_exporter_base.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarEventBase = {
|
||||||
|
id: number; // Id.
|
||||||
|
name: string; // Name.
|
||||||
|
description?: string; // Description.
|
||||||
|
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
location?: string; // @since 3.6. Location.
|
||||||
|
categoryid?: number; // Categoryid.
|
||||||
|
groupid?: number; // Groupid.
|
||||||
|
userid?: number; // Userid.
|
||||||
|
repeatid?: number; // Repeatid.
|
||||||
|
eventcount?: number; // Eventcount.
|
||||||
|
modulename?: string; // Modulename.
|
||||||
|
instance?: number; // Instance.
|
||||||
|
eventtype: string; // Eventtype.
|
||||||
|
timestart: number; // Timestart.
|
||||||
|
timeduration: number; // Timeduration.
|
||||||
|
timesort: number; // Timesort.
|
||||||
|
visible: number; // Visible.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
icon: {
|
||||||
|
key: string; // Key.
|
||||||
|
component: string; // Component.
|
||||||
|
alttext: string; // Alttext.
|
||||||
|
};
|
||||||
|
category?: {
|
||||||
|
id: number; // Id.
|
||||||
|
name: string; // Name.
|
||||||
|
idnumber: string; // Idnumber.
|
||||||
|
description?: string; // Description.
|
||||||
|
parent: number; // Parent.
|
||||||
|
coursecount: number; // Coursecount.
|
||||||
|
visible: number; // Visible.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
depth: number; // Depth.
|
||||||
|
nestedname: string; // Nestedname.
|
||||||
|
url: string; // Url.
|
||||||
|
};
|
||||||
|
course?: {
|
||||||
|
id: number; // Id.
|
||||||
|
fullname: string; // Fullname.
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
idnumber: string; // Idnumber.
|
||||||
|
summary: string; // Summary.
|
||||||
|
summaryformat: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
startdate: number; // Startdate.
|
||||||
|
enddate: number; // Enddate.
|
||||||
|
visible: boolean; // @since 3.8. Visible.
|
||||||
|
fullnamedisplay: string; // Fullnamedisplay.
|
||||||
|
viewurl: string; // Viewurl.
|
||||||
|
courseimage: string; // @since 3.6. Courseimage.
|
||||||
|
progress?: number; // @since 3.6. Progress.
|
||||||
|
hasprogress: boolean; // @since 3.6. Hasprogress.
|
||||||
|
isfavourite: boolean; // @since 3.6. Isfavourite.
|
||||||
|
hidden: boolean; // @since 3.6. Hidden.
|
||||||
|
timeaccess?: number; // @since 3.6. Timeaccess.
|
||||||
|
showshortname: boolean; // @since 3.6. Showshortname.
|
||||||
|
coursecategory: string; // @since 3.7. Coursecategory.
|
||||||
|
};
|
||||||
|
subscription?: {
|
||||||
|
displayeventsource: boolean; // Displayeventsource.
|
||||||
|
subscriptionname?: string; // Subscriptionname.
|
||||||
|
subscriptionurl?: string; // Subscriptionurl.
|
||||||
|
};
|
||||||
|
canedit: boolean; // Canedit.
|
||||||
|
candelete: boolean; // Candelete.
|
||||||
|
deleteurl: string; // Deleteurl.
|
||||||
|
editurl: string; // Editurl.
|
||||||
|
viewurl: string; // Viewurl.
|
||||||
|
formattedtime: string; // Formattedtime.
|
||||||
|
isactionevent: boolean; // Isactionevent.
|
||||||
|
iscourseevent: boolean; // Iscourseevent.
|
||||||
|
iscategoryevent: boolean; // Iscategoryevent.
|
||||||
|
groupname?: string; // Groupname.
|
||||||
|
normalisedeventtype: string; // @since 3.7. Normalisedeventtype.
|
||||||
|
normalisedeventtypetext: string; // @since 3.7. Normalisedeventtypetext.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's event_exporter. Don't confuse it with AddonCalendarCalendarEvent.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarEvent = AddonCalendarEventBase & {
|
||||||
|
url: string; // Url.
|
||||||
|
action?: {
|
||||||
|
name: string; // Name.
|
||||||
|
url: string; // Url.
|
||||||
|
itemcount: number; // Itemcount.
|
||||||
|
actionable: boolean; // Actionable.
|
||||||
|
showitemcount: boolean; // Showitemcount.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's calendar_event_exporter. Don't confuse it with AddonCalendarEvent.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarCalendarEvent = AddonCalendarEventBase & {
|
||||||
|
url: string; // Url.
|
||||||
|
islastday: boolean; // Islastday.
|
||||||
|
popupname: string; // Popupname.
|
||||||
|
mindaytimestamp?: number; // Mindaytimestamp.
|
||||||
|
mindayerror?: string; // Mindayerror.
|
||||||
|
maxdaytimestamp?: number; // Maxdaytimestamp.
|
||||||
|
maxdayerror?: string; // Maxdayerror.
|
||||||
|
draggable: boolean; // Draggable.
|
||||||
|
} & AddonCalendarCalendarEventCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any of the possible types of events.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarAnyEvent = AddonCalendarGetEventsEvent | AddonCalendarEvent | AddonCalendarCalendarEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's calendar_day_exporter. Don't confuse it with AddonCalendarDay.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarCalendarDay = {
|
||||||
|
events: AddonCalendarCalendarEvent[]; // Events.
|
||||||
|
defaulteventcontext: number; // Defaulteventcontext.
|
||||||
|
filter_selector: string; // Filter_selector.
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
categoryid?: number; // Categoryid.
|
||||||
|
neweventtimestamp: number; // Neweventtimestamp.
|
||||||
|
date: CoreWSDate;
|
||||||
|
periodname: string; // Periodname.
|
||||||
|
previousperiod: CoreWSDate;
|
||||||
|
previousperiodlink: string; // Previousperiodlink.
|
||||||
|
previousperiodname: string; // Previousperiodname.
|
||||||
|
nextperiod: CoreWSDate;
|
||||||
|
nextperiodname: string; // Nextperiodname.
|
||||||
|
nextperiodlink: string; // Nextperiodlink.
|
||||||
|
larrow: string; // Larrow.
|
||||||
|
rarrow: string; // Rarrow.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's month_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarMonth = {
|
||||||
|
url: string; // Url.
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
categoryid?: number; // Categoryid.
|
||||||
|
filter_selector?: string; // Filter_selector.
|
||||||
|
weeks: AddonCalendarWeek[]; // Weeks.
|
||||||
|
daynames: AddonCalendarDayName[]; // Daynames.
|
||||||
|
view: string; // View.
|
||||||
|
date: CoreWSDate;
|
||||||
|
periodname: string; // Periodname.
|
||||||
|
includenavigation: boolean; // Includenavigation.
|
||||||
|
initialeventsloaded: boolean; // @since 3.5. Initialeventsloaded.
|
||||||
|
previousperiod: CoreWSDate;
|
||||||
|
previousperiodlink: string; // Previousperiodlink.
|
||||||
|
previousperiodname: string; // Previousperiodname.
|
||||||
|
nextperiod: CoreWSDate;
|
||||||
|
nextperiodname: string; // Nextperiodname.
|
||||||
|
nextperiodlink: string; // Nextperiodlink.
|
||||||
|
larrow: string; // Larrow.
|
||||||
|
rarrow: string; // Rarrow.
|
||||||
|
defaulteventcontext: number; // Defaulteventcontext.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's week_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarWeek = {
|
||||||
|
prepadding: number[]; // Prepadding.
|
||||||
|
postpadding: number[]; // Postpadding.
|
||||||
|
days: AddonCalendarWeekDay[]; // Days.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's week_day_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarWeekDay = AddonCalendarDay & {
|
||||||
|
istoday: boolean; // Istoday.
|
||||||
|
isweekend: boolean; // Isweekend.
|
||||||
|
popovertitle: string; // Popovertitle.
|
||||||
|
ispast?: boolean; // Calculated in the app. Whether the day is in the past.
|
||||||
|
filteredEvents?: AddonCalendarCalendarEvent[]; // Calculated in the app. Filtered events.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's day_exporter. Don't confuse it with AddonCalendarCalendarDay.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarDay = {
|
||||||
|
seconds: number; // Seconds.
|
||||||
|
minutes: number; // Minutes.
|
||||||
|
hours: number; // Hours.
|
||||||
|
mday: number; // Mday.
|
||||||
|
wday: number; // Wday.
|
||||||
|
year: number; // Year.
|
||||||
|
yday: number; // Yday.
|
||||||
|
timestamp: number; // Timestamp.
|
||||||
|
neweventtimestamp: number; // Neweventtimestamp.
|
||||||
|
viewdaylink?: string; // Viewdaylink.
|
||||||
|
events: AddonCalendarCalendarEvent[]; // Events.
|
||||||
|
hasevents: boolean; // Hasevents.
|
||||||
|
calendareventtypes: string[]; // Calendareventtypes.
|
||||||
|
previousperiod: number; // Previousperiod.
|
||||||
|
nextperiod: number; // Nextperiod.
|
||||||
|
navigation: string; // Navigation.
|
||||||
|
haslastdayofevent: boolean; // Haslastdayofevent.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's day_name_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarDayName = {
|
||||||
|
dayno: number; // Dayno.
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
fullname: string; // Fullname.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by calendar's calendar_upcoming_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarUpcoming = {
|
||||||
|
events: AddonCalendarCalendarEvent[]; // Events.
|
||||||
|
defaulteventcontext: number; // Defaulteventcontext.
|
||||||
|
filter_selector: string; // Filter_selector.
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
categoryid?: number; // Categoryid.
|
||||||
|
isloggedin: boolean; // Isloggedin.
|
||||||
|
date: CoreWSDate; // @since 3.8. Date.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_calendar_get_calendar_access_information.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarGetAccessInfoResult = {
|
||||||
|
canmanageentries: boolean; // Whether the user can manage entries.
|
||||||
|
canmanageownentries: boolean; // Whether the user can manage its own entries.
|
||||||
|
canmanagegroupentries: boolean; // Whether the user can manage group entries.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_calendar_get_allowed_event_types.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarGetAllowedEventTypesResult = {
|
||||||
|
allowedeventtypes: string[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_calendar_get_calendar_events.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarGetEventsResult = {
|
||||||
|
events: AddonCalendarGetEventsEvent[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event data returned by WS core_calendar_get_calendar_events.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarGetEventsEvent = {
|
||||||
|
id: number; // Event id.
|
||||||
|
name: string; // Event name.
|
||||||
|
description?: string; // Description.
|
||||||
|
format: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
courseid: number; // Course id.
|
||||||
|
categoryid?: number; // @since 3.4. Category id (only for category events).
|
||||||
|
groupid: number; // Group id.
|
||||||
|
userid: number; // User id.
|
||||||
|
repeatid: number; // Repeat id.
|
||||||
|
modulename?: string; // Module name.
|
||||||
|
instance: number; // Instance id.
|
||||||
|
eventtype: string; // Event type.
|
||||||
|
timestart: number; // Timestart.
|
||||||
|
timeduration: number; // Time duration.
|
||||||
|
visible: number; // Visible.
|
||||||
|
uuid?: string; // Unique id of ical events.
|
||||||
|
sequence: number; // Sequence.
|
||||||
|
timemodified: number; // Time modified.
|
||||||
|
subscriptionid?: number; // Subscription id.
|
||||||
|
showDate?: boolean; // Calculated in the app. Whether date should be shown before this event.
|
||||||
|
endsSameDay?: boolean; // Calculated in the app. Whether the event finishes the same day it starts.
|
||||||
|
deleted?: boolean; // Calculated in the app. Whether it has been deleted in offline.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_calendar_get_calendar_event_by_id.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarGetEventByIdResult = {
|
||||||
|
event: AddonCalendarEvent; // Event.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_calendar_submit_create_update_form.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarSubmitCreateUpdateFormResult = {
|
||||||
|
event?: AddonCalendarEvent; // Event.
|
||||||
|
validationerror: boolean; // Invalid form data.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for AddonCalendarCalendarEvent.
|
||||||
|
*/
|
||||||
|
export type AddonCalendarCalendarEventCalculatedData = {
|
||||||
|
eventIcon?: string; // Calculated in the app. Event icon.
|
||||||
|
moduleIcon?: string; // Calculated in the app. Module icon.
|
||||||
|
formattedType?: string; // Calculated in the app. Formatted type.
|
||||||
|
duration?: number; // Calculated in the app. Duration of offline event.
|
||||||
|
format?: number; // Calculated in the app. Format of offline event.
|
||||||
|
timedurationuntil?: number; // Calculated in the app. Time duration until of offline event.
|
||||||
|
timedurationminutes?: number; // Calculated in the app. Time duration in minutes of offline event.
|
||||||
|
deleted?: boolean; // Calculated in the app. Whether it has been deleted in offline.
|
||||||
|
ispast?: boolean; // Calculated in the app. Whether the event is in the past.
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { AddonCalendarProvider } from './calendar';
|
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from './calendar';
|
||||||
import { CoreConstants } from '@core/constants';
|
import { CoreConstants } from '@core/constants';
|
||||||
import { CoreConfigProvider } from '@providers/config';
|
import { CoreConfigProvider } from '@providers/config';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
@ -130,11 +130,11 @@ export class AddonCalendarHelperProvider {
|
||||||
*
|
*
|
||||||
* @param e Event to format.
|
* @param e Event to format.
|
||||||
*/
|
*/
|
||||||
formatEventData(e: any): void {
|
formatEventData(e: AddonCalendarCalendarEvent): void {
|
||||||
e.icon = this.EVENTICONS[e.eventtype] || false;
|
e.eventIcon = this.EVENTICONS[e.eventtype] || '';
|
||||||
if (!e.icon) {
|
if (!e.eventIcon) {
|
||||||
e.icon = this.courseProvider.getModuleIconSrc(e.modulename);
|
e.eventIcon = this.courseProvider.getModuleIconSrc(e.modulename);
|
||||||
e.moduleIcon = e.icon;
|
e.moduleIcon = e.eventIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.formattedType = this.calendarProvider.getEventType(e);
|
e.formattedType = this.calendarProvider.getEventType(e);
|
||||||
|
@ -160,7 +160,7 @@ export class AddonCalendarHelperProvider {
|
||||||
* @param eventTypes Result of getAllowedEventTypes.
|
* @param eventTypes Result of getAllowedEventTypes.
|
||||||
* @return Options.
|
* @return Options.
|
||||||
*/
|
*/
|
||||||
getEventTypeOptions(eventTypes: any): {name: string, value: string}[] {
|
getEventTypeOptions(eventTypes: {[name: string]: boolean}): {name: string, value: string}[] {
|
||||||
const options = [];
|
const options = [];
|
||||||
|
|
||||||
if (eventTypes.user) {
|
if (eventTypes.user) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, ViewChild, Input } from '@angular/core';
|
||||||
import { Content, NavController } from 'ionic-angular';
|
import { Content, NavController } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import { AddonCompetencyProvider, AddonCompetencyDataForCourseCompetenciesPageResult } from '../../providers/competency';
|
||||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ export class AddonCompetencyCourseComponent {
|
||||||
@Input() userId: number;
|
@Input() userId: number;
|
||||||
|
|
||||||
competenciesLoaded = false;
|
competenciesLoaded = false;
|
||||||
competencies: any;
|
competencies: AddonCompetencyDataForCourseCompetenciesPageResult;
|
||||||
user: any;
|
user: any;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider,
|
constructor(private navCtrl: NavController, private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
|
|
@ -17,7 +17,10 @@ import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import {
|
||||||
|
AddonCompetencyProvider, AddonCompetencyDataForCourseCompetenciesPageResult, AddonCompetencyDataForPlanPageResult,
|
||||||
|
AddonCompetencyDataForPlanPageCompetency, AddonCompetencyDataForCourseCompetenciesPageCompetency
|
||||||
|
} from '../../providers/competency';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of competencies of a learning plan.
|
* Page that displays the list of competencies of a learning plan.
|
||||||
|
@ -36,7 +39,7 @@ export class AddonCompetencyCompetenciesPage {
|
||||||
protected userId: number;
|
protected userId: number;
|
||||||
|
|
||||||
competenciesLoaded = false;
|
competenciesLoaded = false;
|
||||||
competencies = [];
|
competencies: AddonCompetencyDataForPlanPageCompetency[] | AddonCompetencyDataForCourseCompetenciesPageCompetency[] = [];
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
constructor(navParams: NavParams, private translate: TranslateService, private domUtils: CoreDomUtilsProvider,
|
constructor(navParams: NavParams, private translate: TranslateService, private domUtils: CoreDomUtilsProvider,
|
||||||
|
@ -59,7 +62,7 @@ export class AddonCompetencyCompetenciesPage {
|
||||||
this.fetchCompetencies().then(() => {
|
this.fetchCompetencies().then(() => {
|
||||||
if (!this.competencyId && this.splitviewCtrl.isOn() && this.competencies.length > 0) {
|
if (!this.competencyId && this.splitviewCtrl.isOn() && this.competencies.length > 0) {
|
||||||
// Take first and load it.
|
// Take first and load it.
|
||||||
this.openCompetency(this.competencies[0].id);
|
this.openCompetency(this.competencies[0].competency.id);
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.competenciesLoaded = true;
|
this.competenciesLoaded = true;
|
||||||
|
@ -72,7 +75,7 @@ export class AddonCompetencyCompetenciesPage {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchCompetencies(): Promise<void> {
|
protected fetchCompetencies(): Promise<void> {
|
||||||
let promise;
|
let promise: Promise<AddonCompetencyDataForPlanPageResult | AddonCompetencyDataForCourseCompetenciesPageResult>;
|
||||||
|
|
||||||
if (this.planId) {
|
if (this.planId) {
|
||||||
promise = this.competencyProvider.getLearningPlan(this.planId);
|
promise = this.competencyProvider.getLearningPlan(this.planId);
|
||||||
|
@ -83,13 +86,16 @@ export class AddonCompetencyCompetenciesPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then((response) => {
|
return promise.then((response) => {
|
||||||
if (response.competencycount <= 0) {
|
|
||||||
|
if (this.planId) {
|
||||||
|
const resp = <AddonCompetencyDataForPlanPageResult> response;
|
||||||
|
|
||||||
|
if (resp.competencycount <= 0) {
|
||||||
return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound'));
|
return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.planId) {
|
this.title = resp.plan.name;
|
||||||
this.title = response.plan.name;
|
this.userId = resp.plan.userid;
|
||||||
this.userId = response.plan.userid;
|
|
||||||
} else {
|
} else {
|
||||||
this.title = this.translate.instant('addon.competency.coursecompetencies');
|
this.title = this.translate.instant('addon.competency.coursecompetencies');
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,22 +51,22 @@
|
||||||
<core-format-text [text]="activity.name"></core-format-text>
|
<core-format-text [text]="activity.name"></core-format-text>
|
||||||
</a>
|
</a>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap *ngIf="competency.usercompetency.status">
|
<ion-item text-wrap *ngIf="userCompetency.status">
|
||||||
<strong>{{ 'addon.competency.reviewstatus' | translate }}</strong>
|
<strong>{{ 'addon.competency.reviewstatus' | translate }}</strong>
|
||||||
{{ competency.usercompetency.statusname }}
|
{{ userCompetency.statusname }}
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<strong>{{ 'addon.competency.proficient' | translate }}</strong>
|
<strong>{{ 'addon.competency.proficient' | translate }}</strong>
|
||||||
<ion-badge color="success" *ngIf="competency.usercompetency.proficiency">
|
<ion-badge color="success" *ngIf="userCompetency.proficiency">
|
||||||
{{ 'core.yes' | translate }}
|
{{ 'core.yes' | translate }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
<ion-badge color="danger" *ngIf="!competency.usercompetency.proficiency">
|
<ion-badge color="danger" *ngIf="!userCompetency.proficiency">
|
||||||
{{ 'core.no' | translate }}
|
{{ 'core.no' | translate }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<strong>{{ 'addon.competency.rating' | translate }}</strong>
|
<strong>{{ 'addon.competency.rating' | translate }}</strong>
|
||||||
<ion-badge color="dark">{{ competency.usercompetency.gradename }}</ion-badge>
|
<ion-badge color="dark">{{ userCompetency.gradename }}</ion-badge>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,14 @@ import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import {
|
||||||
|
AddonCompetencyProvider, AddonCompetencyUserCompetencySummary, AddonCompetencyUserCompetencySummaryInPlan,
|
||||||
|
AddonCompetencyUserCompetencySummaryInCourse, AddonCompetencyUserCompetencyPlan,
|
||||||
|
AddonCompetencyUserCompetency, AddonCompetencyUserCompetencyCourse
|
||||||
|
} from '../../providers/competency';
|
||||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
import { CoreUserSummary } from '@core/user/providers/user';
|
||||||
|
import { CoreCourseModuleSummary } from '@core/course/providers/course';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a learning plan.
|
* Page that displays a learning plan.
|
||||||
|
@ -36,9 +42,10 @@ export class AddonCompetencyCompetencyPage {
|
||||||
courseId: number;
|
courseId: number;
|
||||||
userId: number;
|
userId: number;
|
||||||
planStatus: number;
|
planStatus: number;
|
||||||
coursemodules: any;
|
coursemodules: CoreCourseModuleSummary[];
|
||||||
user: any;
|
user: CoreUserSummary;
|
||||||
competency: any;
|
competency: AddonCompetencyUserCompetencySummary;
|
||||||
|
userCompetency: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||||
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
@ -79,7 +86,8 @@ export class AddonCompetencyCompetencyPage {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchCompetency(): Promise<void> {
|
protected fetchCompetency(): Promise<void> {
|
||||||
let promise;
|
let promise: Promise<AddonCompetencyUserCompetencySummaryInPlan | AddonCompetencyUserCompetencySummaryInCourse>;
|
||||||
|
|
||||||
if (this.planId) {
|
if (this.planId) {
|
||||||
this.planStatus = null;
|
this.planStatus = null;
|
||||||
promise = this.competencyProvider.getCompetencyInPlan(this.planId, this.competencyId);
|
promise = this.competencyProvider.getCompetencyInPlan(this.planId, this.competencyId);
|
||||||
|
@ -90,23 +98,21 @@ export class AddonCompetencyCompetencyPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then((competency) => {
|
return promise.then((competency) => {
|
||||||
competency.usercompetencysummary.usercompetency = competency.usercompetencysummary.usercompetencyplan ||
|
|
||||||
competency.usercompetencysummary.usercompetency;
|
|
||||||
this.competency = competency.usercompetencysummary;
|
this.competency = competency.usercompetencysummary;
|
||||||
|
this.userCompetency = this.competency.usercompetencyplan || this.competency.usercompetency;
|
||||||
|
|
||||||
if (this.planId) {
|
if (this.planId) {
|
||||||
this.planStatus = competency.plan.status;
|
this.planStatus = (<AddonCompetencyUserCompetencySummaryInPlan> competency).plan.status;
|
||||||
this.competency.usercompetency.statusname =
|
this.competency.usercompetency.statusname =
|
||||||
this.competencyHelperProvider.getCompetencyStatusName(this.competency.usercompetency.status);
|
this.competencyHelperProvider.getCompetencyStatusName(this.competency.usercompetency.status);
|
||||||
} else {
|
} else {
|
||||||
this.competency.usercompetency = this.competency.usercompetencycourse;
|
this.userCompetency = this.competency.usercompetencycourse;
|
||||||
this.coursemodules = competency.coursemodules;
|
this.coursemodules = (<AddonCompetencyUserCompetencySummaryInCourse> competency).coursemodules;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.competency.user.id != this.sitesProvider.getCurrentSiteUserId()) {
|
if (this.competency.user.id != this.sitesProvider.getCurrentSiteUserId()) {
|
||||||
this.competency.user.profileimageurl = this.competency.user.profileimageurl || true;
|
// Get the user profile from the returned object.
|
||||||
|
|
||||||
// Get the user profile image from the returned object.
|
|
||||||
this.user = this.competency.user;
|
this.user = this.competency.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Optional } from '@angular/core';
|
||||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import { AddonCompetencyProvider, AddonCompetencySummary } from '../../providers/competency';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a learning plan.
|
* Page that displays a learning plan.
|
||||||
|
@ -29,7 +29,7 @@ import { AddonCompetencyProvider } from '../../providers/competency';
|
||||||
export class AddonCompetencyCompetencySummaryPage {
|
export class AddonCompetencyCompetencySummaryPage {
|
||||||
competencyLoaded = false;
|
competencyLoaded = false;
|
||||||
competencyId: number;
|
competencyId: number;
|
||||||
competency: any;
|
competency: AddonCompetencySummary;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, navParams: NavParams, private domUtils: CoreDomUtilsProvider,
|
constructor(private navCtrl: NavController, navParams: NavParams, private domUtils: CoreDomUtilsProvider,
|
||||||
@Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider) {
|
@Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider) {
|
||||||
|
@ -41,8 +41,7 @@ export class AddonCompetencyCompetencySummaryPage {
|
||||||
*/
|
*/
|
||||||
ionViewDidLoad(): void {
|
ionViewDidLoad(): void {
|
||||||
this.fetchCompetency().then(() => {
|
this.fetchCompetency().then(() => {
|
||||||
const name = this.competency.competency && this.competency.competency.competency &&
|
const name = this.competency.competency && this.competency.competency.shortname;
|
||||||
this.competency.competency.competency.shortname;
|
|
||||||
|
|
||||||
this.competencyProvider.logCompetencyView(this.competencyId, name).catch(() => {
|
this.competencyProvider.logCompetencyView(this.competencyId, name).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
|
|
@ -46,7 +46,8 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<a ion-item text-wrap *ngFor="let competency of plan.competencies" (click)="openCompetency(competency.competency.id)" [title]="competency.competency.shortname">
|
<a ion-item text-wrap *ngFor="let competency of plan.competencies" (click)="openCompetency(competency.competency.id)" [title]="competency.competency.shortname">
|
||||||
<h2>{{competency.competency.shortname}} <em>{{competency.competency.idnumber}}</em></h2>
|
<h2>{{competency.competency.shortname}} <em>{{competency.competency.idnumber}}</em></h2>
|
||||||
<ion-badge item-end [color]="competency.usercompetency.proficiency ? 'success' : 'danger'">{{ competency.usercompetency.gradename }}</ion-badge>
|
<ion-badge *ngIf="competency.usercompetencyplan" item-end [color]="competency.usercompetencyplan.proficiency ? 'success' : 'danger'">{{ competency.usercompetencyplan.gradename }}</ion-badge>
|
||||||
|
<ion-badge *ngIf="!competency.usercompetencyplan" item-end [color]="competency.usercompetency.proficiency ? 'success' : 'danger'">{{ competency.usercompetency.gradename }}</ion-badge>
|
||||||
</a>
|
</a>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import { AddonCompetencyProvider, AddonCompetencyDataForPlanPageResult } from '../../providers/competency';
|
||||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
export class AddonCompetencyPlanPage {
|
export class AddonCompetencyPlanPage {
|
||||||
protected planId: number;
|
protected planId: number;
|
||||||
planLoaded = false;
|
planLoaded = false;
|
||||||
plan: any;
|
plan: AddonCompetencyDataForPlanPageResult;
|
||||||
user: any;
|
user: any;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, navParams: NavParams, private appProvider: CoreAppProvider,
|
constructor(private navCtrl: NavController, navParams: NavParams, private appProvider: CoreAppProvider,
|
||||||
|
@ -62,9 +62,6 @@ export class AddonCompetencyPlanPage {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
});
|
});
|
||||||
|
|
||||||
plan.competencies.forEach((competency) => {
|
|
||||||
competency.usercompetency = competency.usercompetencyplan || competency.usercompetency;
|
|
||||||
});
|
|
||||||
this.plan = plan;
|
this.plan = plan;
|
||||||
}).catch((message) => {
|
}).catch((message) => {
|
||||||
this.domUtils.showErrorModalDefault(message, 'Error getting learning plan data.');
|
this.domUtils.showErrorModalDefault(message, 'Error getting learning plan data.');
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, ViewChild } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
import { AddonCompetencyProvider, AddonCompetencyPlan } from '../../providers/competency';
|
||||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ export class AddonCompetencyPlanListPage {
|
||||||
protected userId: number;
|
protected userId: number;
|
||||||
protected planId: number;
|
protected planId: number;
|
||||||
plansLoaded = false;
|
plansLoaded = false;
|
||||||
plans = [];
|
plans: AddonCompetencyPlan[] = [];
|
||||||
|
|
||||||
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private competencyProvider: AddonCompetencyProvider,
|
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private competencyProvider: AddonCompetencyProvider,
|
||||||
private competencyHelperProvider: AddonCompetencyHelperProvider) {
|
private competencyHelperProvider: AddonCompetencyHelperProvider) {
|
||||||
|
@ -66,7 +66,7 @@ export class AddonCompetencyPlanListPage {
|
||||||
*/
|
*/
|
||||||
protected fetchLearningPlans(): Promise<void> {
|
protected fetchLearningPlans(): Promise<void> {
|
||||||
return this.competencyProvider.getLearningPlans(this.userId).then((plans) => {
|
return this.competencyProvider.getLearningPlans(this.userId).then((plans) => {
|
||||||
plans.forEach((plan) => {
|
plans.forEach((plan: AddonCompetencyPlanFormatted) => {
|
||||||
plan.statusname = this.competencyHelperProvider.getPlanStatusName(plan.status);
|
plan.statusname = this.competencyHelperProvider.getPlanStatusName(plan.status);
|
||||||
switch (plan.status) {
|
switch (plan.status) {
|
||||||
case AddonCompetencyProvider.STATUS_ACTIVE:
|
case AddonCompetencyProvider.STATUS_ACTIVE:
|
||||||
|
@ -109,3 +109,10 @@ export class AddonCompetencyPlanListPage {
|
||||||
this.splitviewCtrl.push('AddonCompetencyPlanPage', { planId });
|
this.splitviewCtrl.push('AddonCompetencyPlanPage', { planId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Competency plan with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonCompetencyPlanFormatted = AddonCompetencyPlan & {
|
||||||
|
statuscolor?: string; // Calculated in the app. Color of the plan's status.
|
||||||
|
};
|
||||||
|
|
|
@ -17,6 +17,9 @@ import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreCommentsArea } from '@core/comments/providers/comments';
|
||||||
|
import { CoreUserSummary } from '@core/user/providers/user';
|
||||||
|
import { CoreCourseSummary, CoreCourseModuleSummary } from '@core/course/providers/course';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle caompetency learning plans.
|
* Service to handle caompetency learning plans.
|
||||||
|
@ -147,7 +150,7 @@ export class AddonCompetencyProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when the plans are retrieved.
|
* @return Promise to be resolved when the plans are retrieved.
|
||||||
*/
|
*/
|
||||||
getLearningPlans(userId?: number, siteId?: string): Promise<any> {
|
getLearningPlans(userId?: number, siteId?: string): Promise<AddonCompetencyPlan[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
@ -161,7 +164,9 @@ export class AddonCompetencyProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_plans_page', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_plans_page', params, preSets)
|
||||||
|
.then((response: AddonCompetencyDataForPlansPageResult): any => {
|
||||||
|
|
||||||
if (response.plans) {
|
if (response.plans) {
|
||||||
return response.plans;
|
return response.plans;
|
||||||
}
|
}
|
||||||
|
@ -176,9 +181,9 @@ export class AddonCompetencyProvider {
|
||||||
*
|
*
|
||||||
* @param planId ID of the plan.
|
* @param planId ID of the plan.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when the plans are retrieved.
|
* @return Promise to be resolved when the plan is retrieved.
|
||||||
*/
|
*/
|
||||||
getLearningPlan(planId: number, siteId?: string): Promise<any> {
|
getLearningPlan(planId: number, siteId?: string): Promise<AddonCompetencyDataForPlanPageResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
this.logger.debug('Get plan ' + planId);
|
this.logger.debug('Get plan ' + planId);
|
||||||
|
@ -191,7 +196,9 @@ export class AddonCompetencyProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_plan_page', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_plan_page', params, preSets)
|
||||||
|
.then((response: AddonCompetencyDataForPlanPageResult): any => {
|
||||||
|
|
||||||
if (response.plan) {
|
if (response.plan) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -207,9 +214,11 @@ export class AddonCompetencyProvider {
|
||||||
* @param planId ID of the plan.
|
* @param planId ID of the plan.
|
||||||
* @param competencyId ID of the competency.
|
* @param competencyId ID of the competency.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise to be resolved when the plans are retrieved.
|
* @return Promise to be resolved when the competency is retrieved.
|
||||||
*/
|
*/
|
||||||
getCompetencyInPlan(planId: number, competencyId: number, siteId?: string): Promise<any> {
|
getCompetencyInPlan(planId: number, competencyId: number, siteId?: string)
|
||||||
|
: Promise<AddonCompetencyUserCompetencySummaryInPlan> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
this.logger.debug('Get competency ' + competencyId + ' in plan ' + planId);
|
this.logger.debug('Get competency ' + competencyId + ' in plan ' + planId);
|
||||||
|
@ -223,7 +232,9 @@ export class AddonCompetencyProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_user_competency_summary_in_plan', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_user_competency_summary_in_plan', params, preSets)
|
||||||
|
.then((response: AddonCompetencyUserCompetencySummaryInPlan): any => {
|
||||||
|
|
||||||
if (response.usercompetencysummary) {
|
if (response.usercompetencysummary) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -241,10 +252,10 @@ export class AddonCompetencyProvider {
|
||||||
* @param userId ID of the user. If not defined, current user.
|
* @param userId ID of the user. If not defined, current user.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
||||||
* @return Promise to be resolved when the plans are retrieved.
|
* @return Promise to be resolved when the competency is retrieved.
|
||||||
*/
|
*/
|
||||||
getCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean)
|
getCompetencyInCourse(courseId: number, competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean)
|
||||||
: Promise<any> {
|
: Promise<AddonCompetencyUserCompetencySummaryInCourse> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -266,7 +277,9 @@ export class AddonCompetencyProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_user_competency_summary_in_course', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_user_competency_summary_in_course', params, preSets)
|
||||||
|
.then((response: AddonCompetencyUserCompetencySummaryInCourse): any => {
|
||||||
|
|
||||||
if (response.usercompetencysummary) {
|
if (response.usercompetencysummary) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -283,9 +296,11 @@ export class AddonCompetencyProvider {
|
||||||
* @param userId ID of the user. If not defined, current user.
|
* @param userId ID of the user. If not defined, current user.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
||||||
* @return Promise to be resolved when the plans are retrieved.
|
* @return Promise to be resolved when the competency summary is retrieved.
|
||||||
*/
|
*/
|
||||||
getCompetencySummary(competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean): Promise<any> {
|
getCompetencySummary(competencyId: number, userId?: number, siteId?: string, ignoreCache?: boolean)
|
||||||
|
: Promise<AddonCompetencySummary> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
@ -305,7 +320,9 @@ export class AddonCompetencyProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_user_competency_summary', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_user_competency_summary', params, preSets)
|
||||||
|
.then((response: AddonCompetencyUserCompetencySummary): any => {
|
||||||
|
|
||||||
if (response.competency) {
|
if (response.competency) {
|
||||||
return response.competency;
|
return response.competency;
|
||||||
}
|
}
|
||||||
|
@ -324,7 +341,9 @@ export class AddonCompetencyProvider {
|
||||||
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
||||||
* @return Promise to be resolved when the course competencies are retrieved.
|
* @return Promise to be resolved when the course competencies are retrieved.
|
||||||
*/
|
*/
|
||||||
getCourseCompetencies(courseId: number, userId?: number, siteId?: string, ignoreCache?: boolean): Promise<any> {
|
getCourseCompetencies(courseId: number, userId?: number, siteId?: string, ignoreCache?: boolean)
|
||||||
|
: Promise<AddonCompetencyDataForCourseCompetenciesPageResult> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
this.logger.debug('Get course competencies for course ' + courseId);
|
this.logger.debug('Get course competencies for course ' + courseId);
|
||||||
|
@ -342,7 +361,9 @@ export class AddonCompetencyProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('tool_lp_data_for_course_competencies_page', params, preSets).then((response) => {
|
return site.read('tool_lp_data_for_course_competencies_page', params, preSets)
|
||||||
|
.then((response: AddonCompetencyDataForCourseCompetenciesPageResult): any => {
|
||||||
|
|
||||||
if (response.competencies) {
|
if (response.competencies) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -356,11 +377,13 @@ export class AddonCompetencyProvider {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises = response.competencies.map((competency) =>
|
let promises: Promise<AddonCompetencyUserCompetencySummaryInCourse>[];
|
||||||
|
|
||||||
|
promises = response.competencies.map((competency) =>
|
||||||
this.getCompetencyInCourse(courseId, competency.competency.id, userId, siteId)
|
this.getCompetencyInCourse(courseId, competency.competency.id, userId, siteId)
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.all(promises).then((responses: any[]) => {
|
return Promise.all(promises).then((responses: AddonCompetencyUserCompetencySummaryInCourse[]) => {
|
||||||
responses.forEach((resp, index) => {
|
responses.forEach((resp, index) => {
|
||||||
response.competencies[index].usercompetencycourse = resp.usercompetencysummary.usercompetencycourse;
|
response.competencies[index].usercompetencycourse = resp.usercompetencysummary.usercompetencycourse;
|
||||||
});
|
});
|
||||||
|
@ -486,7 +509,7 @@ export class AddonCompetencyProvider {
|
||||||
* @return Promise resolved when the WS call is successful.
|
* @return Promise resolved when the WS call is successful.
|
||||||
*/
|
*/
|
||||||
logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number,
|
logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number,
|
||||||
siteId?: string): Promise<any> {
|
siteId?: string): Promise<void> {
|
||||||
if (planId && competencyId) {
|
if (planId && competencyId) {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -509,7 +532,11 @@ export class AddonCompetencyProvider {
|
||||||
userid: userId
|
userid: userId
|
||||||
}, siteId);
|
}, siteId);
|
||||||
|
|
||||||
return site.write(wsName, params, preSets);
|
return site.write(wsName, params, preSets).then((success: boolean) => {
|
||||||
|
if (!success) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +554,7 @@ export class AddonCompetencyProvider {
|
||||||
* @return Promise resolved when the WS call is successful.
|
* @return Promise resolved when the WS call is successful.
|
||||||
*/
|
*/
|
||||||
logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string)
|
logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string)
|
||||||
: Promise<any> {
|
: Promise<void> {
|
||||||
|
|
||||||
if (courseId && competencyId) {
|
if (courseId && competencyId) {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -548,7 +575,11 @@ export class AddonCompetencyProvider {
|
||||||
userid: userId
|
userid: userId
|
||||||
}, siteId);
|
}, siteId);
|
||||||
|
|
||||||
return site.write(wsName, params, preSets);
|
return site.write(wsName, params, preSets).then((success: boolean) => {
|
||||||
|
if (!success) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,7 +594,7 @@ export class AddonCompetencyProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the WS call is successful.
|
* @return Promise resolved when the WS call is successful.
|
||||||
*/
|
*/
|
||||||
logCompetencyView(competencyId: number, name?: string, siteId?: string): Promise<any> {
|
logCompetencyView(competencyId: number, name?: string, siteId?: string): Promise<void> {
|
||||||
if (competencyId) {
|
if (competencyId) {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -576,10 +607,401 @@ export class AddonCompetencyProvider {
|
||||||
|
|
||||||
this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, {}, siteId);
|
this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, {}, siteId);
|
||||||
|
|
||||||
return site.write('core_competency_competency_viewed', params, preSets);
|
return site.write(wsName, params, preSets).then((success: boolean) => {
|
||||||
|
if (!success) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's plan_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyPlan = {
|
||||||
|
name: string; // Name.
|
||||||
|
description: string; // Description.
|
||||||
|
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
userid: number; // Userid.
|
||||||
|
templateid: number; // Templateid.
|
||||||
|
origtemplateid: number; // Origtemplateid.
|
||||||
|
status: number; // Status.
|
||||||
|
duedate: number; // Duedate.
|
||||||
|
reviewerid: number; // Reviewerid.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
statusname: string; // Statusname.
|
||||||
|
isbasedontemplate: boolean; // Isbasedontemplate.
|
||||||
|
canmanage: boolean; // Canmanage.
|
||||||
|
canrequestreview: boolean; // Canrequestreview.
|
||||||
|
canreview: boolean; // Canreview.
|
||||||
|
canbeedited: boolean; // Canbeedited.
|
||||||
|
isactive: boolean; // Isactive.
|
||||||
|
isdraft: boolean; // Isdraft.
|
||||||
|
iscompleted: boolean; // Iscompleted.
|
||||||
|
isinreview: boolean; // Isinreview.
|
||||||
|
iswaitingforreview: boolean; // Iswaitingforreview.
|
||||||
|
isreopenallowed: boolean; // Isreopenallowed.
|
||||||
|
iscompleteallowed: boolean; // Iscompleteallowed.
|
||||||
|
isunlinkallowed: boolean; // Isunlinkallowed.
|
||||||
|
isrequestreviewallowed: boolean; // Isrequestreviewallowed.
|
||||||
|
iscancelreviewrequestallowed: boolean; // Iscancelreviewrequestallowed.
|
||||||
|
isstartreviewallowed: boolean; // Isstartreviewallowed.
|
||||||
|
isstopreviewallowed: boolean; // Isstopreviewallowed.
|
||||||
|
isapproveallowed: boolean; // Isapproveallowed.
|
||||||
|
isunapproveallowed: boolean; // Isunapproveallowed.
|
||||||
|
duedateformatted: string; // Duedateformatted.
|
||||||
|
commentarea: CoreCommentsArea;
|
||||||
|
reviewer?: CoreUserSummary;
|
||||||
|
template?: AddonCompetencyTemplate;
|
||||||
|
url: string; // Url.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's template_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyTemplate = {
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
description: string; // Description.
|
||||||
|
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
duedate: number; // Duedate.
|
||||||
|
visible: boolean; // Visible.
|
||||||
|
contextid: number; // Contextid.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
duedateformatted: string; // Duedateformatted.
|
||||||
|
cohortscount: number; // Cohortscount.
|
||||||
|
planscount: number; // Planscount.
|
||||||
|
canmanage: boolean; // Canmanage.
|
||||||
|
canread: boolean; // Canread.
|
||||||
|
contextname: string; // Contextname.
|
||||||
|
contextnamenoprefix: string; // Contextnamenoprefix.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's competency_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyCompetency = {
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
idnumber: string; // Idnumber.
|
||||||
|
description: string; // Description.
|
||||||
|
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
sortorder: number; // Sortorder.
|
||||||
|
parentid: number; // Parentid.
|
||||||
|
path: string; // Path.
|
||||||
|
ruleoutcome: number; // Ruleoutcome.
|
||||||
|
ruletype: string; // Ruletype.
|
||||||
|
ruleconfig: string; // Ruleconfig.
|
||||||
|
scaleid: number; // Scaleid.
|
||||||
|
scaleconfiguration: string; // Scaleconfiguration.
|
||||||
|
competencyframeworkid: number; // Competencyframeworkid.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's competency_path_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyPath = {
|
||||||
|
ancestors: AddonCompetencyPathNode[]; // Ancestors.
|
||||||
|
framework: AddonCompetencyPathNode;
|
||||||
|
pluginbaseurl: string; // Pluginbaseurl.
|
||||||
|
pagecontextid: number; // Pagecontextid.
|
||||||
|
showlinks: boolean; // @since 3.7. Showlinks.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's path_node_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyPathNode = {
|
||||||
|
id: number; // Id.
|
||||||
|
name: string; // Name.
|
||||||
|
first: boolean; // First.
|
||||||
|
last: boolean; // Last.
|
||||||
|
position: number; // Position.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetency = {
|
||||||
|
userid: number; // Userid.
|
||||||
|
competencyid: number; // Competencyid.
|
||||||
|
status: number; // Status.
|
||||||
|
reviewerid: number; // Reviewerid.
|
||||||
|
proficiency: boolean; // Proficiency.
|
||||||
|
grade: number; // Grade.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
canrequestreview: boolean; // Canrequestreview.
|
||||||
|
canreview: boolean; // Canreview.
|
||||||
|
gradename: string; // Gradename.
|
||||||
|
isrequestreviewallowed: boolean; // Isrequestreviewallowed.
|
||||||
|
iscancelreviewrequestallowed: boolean; // Iscancelreviewrequestallowed.
|
||||||
|
isstartreviewallowed: boolean; // Isstartreviewallowed.
|
||||||
|
isstopreviewallowed: boolean; // Isstopreviewallowed.
|
||||||
|
isstatusidle: boolean; // Isstatusidle.
|
||||||
|
isstatusinreview: boolean; // Isstatusinreview.
|
||||||
|
isstatuswaitingforreview: boolean; // Isstatuswaitingforreview.
|
||||||
|
proficiencyname: string; // Proficiencyname.
|
||||||
|
reviewer?: CoreUserSummary;
|
||||||
|
statusname: string; // Statusname.
|
||||||
|
url: string; // Url.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_plan_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetencyPlan = {
|
||||||
|
userid: number; // Userid.
|
||||||
|
competencyid: number; // Competencyid.
|
||||||
|
proficiency: boolean; // Proficiency.
|
||||||
|
grade: number; // Grade.
|
||||||
|
planid: number; // Planid.
|
||||||
|
sortorder: number; // Sortorder.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
gradename: string; // Gradename.
|
||||||
|
proficiencyname: string; // Proficiencyname.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_summary_in_plan_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetencySummaryInPlan = {
|
||||||
|
usercompetencysummary: AddonCompetencyUserCompetencySummary;
|
||||||
|
plan: AddonCompetencyPlan;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_summary_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetencySummary = {
|
||||||
|
showrelatedcompetencies: boolean; // Showrelatedcompetencies.
|
||||||
|
cangrade: boolean; // Cangrade.
|
||||||
|
competency: AddonCompetencySummary;
|
||||||
|
user: CoreUserSummary;
|
||||||
|
usercompetency?: AddonCompetencyUserCompetency;
|
||||||
|
usercompetencyplan?: AddonCompetencyUserCompetencyPlan;
|
||||||
|
usercompetencycourse?: AddonCompetencyUserCompetencyCourse;
|
||||||
|
evidence: AddonCompetencyEvidence[]; // Evidence.
|
||||||
|
commentarea?: CoreCommentsArea;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's competency_summary_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencySummary = {
|
||||||
|
linkedcourses: CoreCourseSummary; // Linkedcourses.
|
||||||
|
relatedcompetencies: AddonCompetencyCompetency[]; // Relatedcompetencies.
|
||||||
|
competency: AddonCompetencyCompetency;
|
||||||
|
framework: AddonCompetencyFramework;
|
||||||
|
hascourses: boolean; // Hascourses.
|
||||||
|
hasrelatedcompetencies: boolean; // Hasrelatedcompetencies.
|
||||||
|
scaleid: number; // Scaleid.
|
||||||
|
scaleconfiguration: string; // Scaleconfiguration.
|
||||||
|
taxonomyterm: string; // Taxonomyterm.
|
||||||
|
comppath: AddonCompetencyPath;
|
||||||
|
pluginbaseurl: string; // @since 3.7. Pluginbaseurl.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's competency_framework_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyFramework = {
|
||||||
|
shortname: string; // Shortname.
|
||||||
|
idnumber: string; // Idnumber.
|
||||||
|
description: string; // Description.
|
||||||
|
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
visible: boolean; // Visible.
|
||||||
|
scaleid: number; // Scaleid.
|
||||||
|
scaleconfiguration: string; // Scaleconfiguration.
|
||||||
|
contextid: number; // Contextid.
|
||||||
|
taxonomies: string; // Taxonomies.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
canmanage: boolean; // Canmanage.
|
||||||
|
competenciescount: number; // Competenciescount.
|
||||||
|
contextname: string; // Contextname.
|
||||||
|
contextnamenoprefix: string; // Contextnamenoprefix.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_course_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetencyCourse = {
|
||||||
|
userid: number; // Userid.
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
competencyid: number; // Competencyid.
|
||||||
|
proficiency: boolean; // Proficiency.
|
||||||
|
grade: number; // Grade.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
gradename: string; // Gradename.
|
||||||
|
proficiencyname: string; // Proficiencyname.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's evidence_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyEvidence = {
|
||||||
|
usercompetencyid: number; // Usercompetencyid.
|
||||||
|
contextid: number; // Contextid.
|
||||||
|
action: number; // Action.
|
||||||
|
actionuserid: number; // Actionuserid.
|
||||||
|
descidentifier: string; // Descidentifier.
|
||||||
|
desccomponent: string; // Desccomponent.
|
||||||
|
desca: string; // Desca.
|
||||||
|
url: string; // Url.
|
||||||
|
grade: number; // Grade.
|
||||||
|
note: string; // Note.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
actionuser?: CoreUserSummary;
|
||||||
|
description: string; // Description.
|
||||||
|
gradename: string; // Gradename.
|
||||||
|
userdate: string; // Userdate.
|
||||||
|
candelete: boolean; // Candelete.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's user_competency_summary_in_course_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyUserCompetencySummaryInCourse = {
|
||||||
|
usercompetencysummary: AddonCompetencyUserCompetencySummary;
|
||||||
|
course: CoreCourseSummary;
|
||||||
|
coursemodules: CoreCourseModuleSummary[]; // Coursemodules.
|
||||||
|
plans: AddonCompetencyPlan[]; // @since 3.7. Plans.
|
||||||
|
pluginbaseurl: string; // @since 3.7. Pluginbaseurl.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's course_competency_settings_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyCourseCompetencySettings = {
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
pushratingstouserplans: boolean; // Pushratingstouserplans.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's course_competency_statistics_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyCourseCompetencyStatistics = {
|
||||||
|
competencycount: number; // Competencycount.
|
||||||
|
proficientcompetencycount: number; // Proficientcompetencycount.
|
||||||
|
proficientcompetencypercentage: number; // Proficientcompetencypercentage.
|
||||||
|
proficientcompetencypercentageformatted: string; // Proficientcompetencypercentageformatted.
|
||||||
|
leastproficient: AddonCompetencyCompetency[]; // Leastproficient.
|
||||||
|
leastproficientcount: number; // Leastproficientcount.
|
||||||
|
canbegradedincourse: boolean; // Canbegradedincourse.
|
||||||
|
canmanagecoursecompetencies: boolean; // Canmanagecoursecompetencies.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by competency's course_competency_exporter.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyCourseCompetency = {
|
||||||
|
courseid: number; // Courseid.
|
||||||
|
competencyid: number; // Competencyid.
|
||||||
|
sortorder: number; // Sortorder.
|
||||||
|
ruleoutcome: number; // Ruleoutcome.
|
||||||
|
id: number; // Id.
|
||||||
|
timecreated: number; // Timecreated.
|
||||||
|
timemodified: number; // Timemodified.
|
||||||
|
usermodified: number; // Usermodified.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS tool_lp_data_for_plans_page.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyDataForPlansPageResult = {
|
||||||
|
userid: number; // The learning plan user id.
|
||||||
|
plans: AddonCompetencyPlan[];
|
||||||
|
pluginbaseurl: string; // Url to the tool_lp plugin folder on this Moodle site.
|
||||||
|
navigation: string[];
|
||||||
|
canreaduserevidence: boolean; // Can the current user view the user's evidence.
|
||||||
|
canmanageuserplans: boolean; // Can the current user manage the user's plans.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS tool_lp_data_for_plan_page.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyDataForPlanPageResult = {
|
||||||
|
plan: AddonCompetencyPlan;
|
||||||
|
contextid: number; // Context ID.
|
||||||
|
pluginbaseurl: string; // Plugin base URL.
|
||||||
|
competencies: AddonCompetencyDataForPlanPageCompetency[];
|
||||||
|
competencycount: number; // Count of competencies.
|
||||||
|
proficientcompetencycount: number; // Count of proficientcompetencies.
|
||||||
|
proficientcompetencypercentage: number; // Percentage of competencies proficient.
|
||||||
|
proficientcompetencypercentageformatted: string; // Displayable percentage.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Competency data returned by tool_lp_data_for_plan_page.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyDataForPlanPageCompetency = {
|
||||||
|
competency: AddonCompetencyCompetency;
|
||||||
|
comppath: AddonCompetencyPath;
|
||||||
|
usercompetency?: AddonCompetencyUserCompetency;
|
||||||
|
usercompetencyplan?: AddonCompetencyUserCompetencyPlan;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS tool_lp_data_for_course_competencies_page.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyDataForCourseCompetenciesPageResult = {
|
||||||
|
courseid: number; // The current course id.
|
||||||
|
pagecontextid: number; // The current page context ID.
|
||||||
|
gradableuserid?: number; // Current user id, if the user is a gradable user.
|
||||||
|
canmanagecompetencyframeworks: boolean; // User can manage competency frameworks.
|
||||||
|
canmanagecoursecompetencies: boolean; // User can manage linked course competencies.
|
||||||
|
canconfigurecoursecompetencies: boolean; // User can configure course competency settings.
|
||||||
|
cangradecompetencies: boolean; // User can grade competencies.
|
||||||
|
settings: AddonCompetencyCourseCompetencySettings;
|
||||||
|
statistics: AddonCompetencyCourseCompetencyStatistics;
|
||||||
|
competencies: AddonCompetencyDataForCourseCompetenciesPageCompetency[];
|
||||||
|
manageurl: string; // Url to the manage competencies page.
|
||||||
|
pluginbaseurl: string; // @since 3.6. Url to the course competencies page.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Competency data returned by tool_lp_data_for_course_competencies_page.
|
||||||
|
*/
|
||||||
|
export type AddonCompetencyDataForCourseCompetenciesPageCompetency = {
|
||||||
|
competency: AddonCompetencyCompetency;
|
||||||
|
coursecompetency: AddonCompetencyCourseCompetency;
|
||||||
|
coursemodules: CoreCourseModuleSummary[];
|
||||||
|
usercompetencycourse?: AddonCompetencyUserCompetencyCourse;
|
||||||
|
ruleoutcomeoptions: {
|
||||||
|
value: number; // The option value.
|
||||||
|
text: string; // The name of the option.
|
||||||
|
selected: boolean; // If this is the currently selected option.
|
||||||
|
}[];
|
||||||
|
comppath: AddonCompetencyPath;
|
||||||
|
plans: AddonCompetencyPlan[]; // @since 3.7.
|
||||||
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<ion-card *ngIf="completion && tracked">
|
<ion-card *ngIf="completion && tracked">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
||||||
<p>{{ completion.statusText | translate }}</p>
|
<p>{{ statusText | translate }}</p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<h2>{{ 'addon.coursecompletion.required' | translate }}</h2>
|
<h2>{{ 'addon.coursecompletion.required' | translate }}</h2>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonCourseCompletionProvider } from '../../providers/coursecompletion';
|
import { AddonCourseCompletionProvider, AddonCourseCompletionCourseCompletionStatus } from '../../providers/coursecompletion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the course completion report.
|
* Component that displays the course completion report.
|
||||||
|
@ -29,9 +29,10 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
||||||
@Input() userId: number;
|
@Input() userId: number;
|
||||||
|
|
||||||
completionLoaded = false;
|
completionLoaded = false;
|
||||||
completion: any;
|
completion: AddonCourseCompletionCourseCompletionStatus;
|
||||||
showSelfComplete: boolean;
|
showSelfComplete: boolean;
|
||||||
tracked = true; // Whether completion is tracked.
|
tracked = true; // Whether completion is tracked.
|
||||||
|
statusText: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sitesProvider: CoreSitesProvider,
|
private sitesProvider: CoreSitesProvider,
|
||||||
|
@ -59,7 +60,7 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
||||||
protected fetchCompletion(): Promise<any> {
|
protected fetchCompletion(): Promise<any> {
|
||||||
return this.courseCompletionProvider.getCompletion(this.courseId, this.userId).then((completion) => {
|
return this.courseCompletionProvider.getCompletion(this.courseId, this.userId).then((completion) => {
|
||||||
|
|
||||||
completion.statusText = this.courseCompletionProvider.getCompletedStatusText(completion);
|
this.statusText = this.courseCompletionProvider.getCompletedStatusText(completion);
|
||||||
|
|
||||||
this.completion = completion;
|
this.completion = completion;
|
||||||
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle course completion.
|
* Service to handle course completion.
|
||||||
|
@ -43,7 +44,7 @@ export class AddonCourseCompletionProvider {
|
||||||
* @param completion Course completion.
|
* @param completion Course completion.
|
||||||
* @return True if user can mark course as self completed, false otherwise.
|
* @return True if user can mark course as self completed, false otherwise.
|
||||||
*/
|
*/
|
||||||
canMarkSelfCompleted(userId: number, completion: any): boolean {
|
canMarkSelfCompleted(userId: number, completion: AddonCourseCompletionCourseCompletionStatus): boolean {
|
||||||
let selfCompletionActive = false,
|
let selfCompletionActive = false,
|
||||||
alreadyMarked = false;
|
alreadyMarked = false;
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ export class AddonCourseCompletionProvider {
|
||||||
* @param completion Course completion.
|
* @param completion Course completion.
|
||||||
* @return Language code of the text to show.
|
* @return Language code of the text to show.
|
||||||
*/
|
*/
|
||||||
getCompletedStatusText(completion: any): string {
|
getCompletedStatusText(completion: AddonCourseCompletionCourseCompletionStatus): string {
|
||||||
if (completion.completed) {
|
if (completion.completed) {
|
||||||
return 'addon.coursecompletion.completed';
|
return 'addon.coursecompletion.completed';
|
||||||
} else {
|
} else {
|
||||||
|
@ -96,7 +97,9 @@ export class AddonCourseCompletionProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise to be resolved when the completion is retrieved.
|
* @return Promise to be resolved when the completion is retrieved.
|
||||||
*/
|
*/
|
||||||
getCompletion(courseId: number, userId?: number, preSets?: any, siteId?: string): Promise<any> {
|
getCompletion(courseId: number, userId?: number, preSets?: any, siteId?: string)
|
||||||
|
: Promise<AddonCourseCompletionCourseCompletionStatus> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
preSets = preSets || {};
|
preSets = preSets || {};
|
||||||
|
@ -112,7 +115,9 @@ export class AddonCourseCompletionProvider {
|
||||||
preSets.updateFrequency = preSets.updateFrequency || CoreSite.FREQUENCY_SOMETIMES;
|
preSets.updateFrequency = preSets.updateFrequency || CoreSite.FREQUENCY_SOMETIMES;
|
||||||
preSets.cacheErrors = ['notenroled'];
|
preSets.cacheErrors = ['notenroled'];
|
||||||
|
|
||||||
return site.read('core_completion_get_course_completion_status', data, preSets).then((data) => {
|
return site.read('core_completion_get_course_completion_status', data, preSets)
|
||||||
|
.then((data: AddonCourseCompletionGetCourseCompletionStatusResult): any => {
|
||||||
|
|
||||||
if (data.completionstatus) {
|
if (data.completionstatus) {
|
||||||
return data.completionstatus;
|
return data.completionstatus;
|
||||||
}
|
}
|
||||||
|
@ -243,17 +248,56 @@ export class AddonCourseCompletionProvider {
|
||||||
* Mark a course as self completed.
|
* Mark a course as self completed.
|
||||||
*
|
*
|
||||||
* @param courseId Course ID.
|
* @param courseId Course ID.
|
||||||
* @return Resolved on success.
|
* @return Promise resolved on success.
|
||||||
*/
|
*/
|
||||||
markCourseAsSelfCompleted(courseId: number): Promise<any> {
|
markCourseAsSelfCompleted(courseId: number): Promise<void> {
|
||||||
const params = {
|
const params = {
|
||||||
courseid: courseId
|
courseid: courseId
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.sitesProvider.getCurrentSite().write('core_completion_mark_course_self_completed', params).then((response) => {
|
return this.sitesProvider.getCurrentSite().write('core_completion_mark_course_self_completed', params)
|
||||||
|
.then((response: AddonCourseCompletionMarkCourseSelfCompletedResult) => {
|
||||||
|
|
||||||
if (!response.status) {
|
if (!response.status) {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completion status returned by core_completion_get_course_completion_status.
|
||||||
|
*/
|
||||||
|
export type AddonCourseCompletionCourseCompletionStatus = {
|
||||||
|
completed: boolean; // True if the course is complete, false otherwise.
|
||||||
|
aggregation: number; // Aggregation method 1 means all, 2 means any.
|
||||||
|
completions: {
|
||||||
|
type: number; // Completion criteria type.
|
||||||
|
title: string; // Completion criteria Title.
|
||||||
|
status: string; // Completion status (Yes/No) a % or number.
|
||||||
|
complete: boolean; // Completion status (true/false).
|
||||||
|
timecompleted: number; // Timestamp for criteria completetion.
|
||||||
|
details: {
|
||||||
|
type: string; // Type description.
|
||||||
|
criteria: string; // Criteria description.
|
||||||
|
requirement: string; // Requirement description.
|
||||||
|
status: string; // Status description, can be anything.
|
||||||
|
}; // Details.
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_completion_get_course_completion_status.
|
||||||
|
*/
|
||||||
|
export type AddonCourseCompletionGetCourseCompletionStatusResult = {
|
||||||
|
completionstatus: AddonCourseCompletionCourseCompletionStatus; // Course status.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_completion_mark_course_self_completed.
|
||||||
|
*/
|
||||||
|
export type AddonCourseCompletionMarkCourseSelfCompletedResult = {
|
||||||
|
status: boolean; // Status, true if success.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonFilesProvider } from '../../providers/files';
|
import { AddonFilesProvider, AddonFilesFile, AddonFilesGetUserPrivateFilesInfoResult } from '../../providers/files';
|
||||||
import { AddonFilesHelperProvider } from '../../providers/helper';
|
import { AddonFilesHelperProvider } from '../../providers/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,10 +40,10 @@ export class AddonFilesListPage implements OnDestroy {
|
||||||
root: string; // The root of the files loaded: 'my' or 'site'.
|
root: string; // The root of the files loaded: 'my' or 'site'.
|
||||||
path: string; // The path of the directory being loaded. If empty path it means the root is being loaded.
|
path: string; // The path of the directory being loaded. If empty path it means the root is being loaded.
|
||||||
userQuota: number; // The user quota (in bytes).
|
userQuota: number; // The user quota (in bytes).
|
||||||
filesInfo: any; // Info about private files (size, number of files, etc.).
|
filesInfo: AddonFilesGetUserPrivateFilesInfoResult; // Info about private files (size, number of files, etc.).
|
||||||
spaceUsed: string; // Space used in a readable format.
|
spaceUsed: string; // Space used in a readable format.
|
||||||
userQuotaReadable: string; // User quota in a readable format.
|
userQuotaReadable: string; // User quota in a readable format.
|
||||||
files: any[]; // List of files.
|
files: AddonFilesFile[]; // List of files.
|
||||||
component: string; // Component to link the file downloads to.
|
component: string; // Component to link the file downloads to.
|
||||||
filesLoaded: boolean; // Whether the files are loaded.
|
filesLoaded: boolean; // Whether the files are loaded.
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ export class AddonFilesListPage implements OnDestroy {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchFiles(): Promise<any> {
|
protected fetchFiles(): Promise<any> {
|
||||||
let promise;
|
let promise: Promise<AddonFilesFile[]>;
|
||||||
|
|
||||||
if (!this.path) {
|
if (!this.path) {
|
||||||
// The path is unknown, the user must be requesting a root.
|
// The path is unknown, the user must be requesting a root.
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle my files and site files.
|
* Service to handle my files and site files.
|
||||||
|
@ -73,7 +74,7 @@ export class AddonFilesProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the files.
|
* @return Promise resolved with the files.
|
||||||
*/
|
*/
|
||||||
getFiles(params: any, siteId?: string): Promise<any[]> {
|
getFiles(params: any, siteId?: string): Promise<AddonFilesFile[]> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const preSets = {
|
const preSets = {
|
||||||
|
@ -82,15 +83,15 @@ export class AddonFilesProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_files_get_files', params, preSets);
|
return site.read('core_files_get_files', params, preSets);
|
||||||
}).then((result) => {
|
}).then((result: AddonFilesGetFilesResult) => {
|
||||||
const entries = [];
|
const entries: AddonFilesFile[] = [];
|
||||||
|
|
||||||
if (result.files) {
|
if (result.files) {
|
||||||
result.files.forEach((entry) => {
|
result.files.forEach((entry) => {
|
||||||
if (entry.isdir) {
|
if (entry.isdir) {
|
||||||
// Create a "link" to load the folder.
|
// Create a "link" to load the folder.
|
||||||
entry.link = {
|
entry.link = {
|
||||||
contextid: entry.contextid || '',
|
contextid: entry.contextid || null,
|
||||||
component: entry.component || '',
|
component: entry.component || '',
|
||||||
filearea: entry.filearea || '',
|
filearea: entry.filearea || '',
|
||||||
itemid: entry.itemid || 0,
|
itemid: entry.itemid || 0,
|
||||||
|
@ -135,7 +136,7 @@ export class AddonFilesProvider {
|
||||||
*
|
*
|
||||||
* @return Promise resolved with the files.
|
* @return Promise resolved with the files.
|
||||||
*/
|
*/
|
||||||
getPrivateFiles(): Promise<any[]> {
|
getPrivateFiles(): Promise<AddonFilesFile[]> {
|
||||||
return this.getFiles(this.getPrivateFilesRootParams());
|
return this.getFiles(this.getPrivateFilesRootParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ export class AddonFilesProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved with the info.
|
* @return Promise resolved with the info.
|
||||||
*/
|
*/
|
||||||
getPrivateFilesInfo(userId?: number, siteId?: string): Promise<any> {
|
getPrivateFilesInfo(userId?: number, siteId?: string): Promise<AddonFilesGetUserPrivateFilesInfoResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
@ -204,7 +205,7 @@ export class AddonFilesProvider {
|
||||||
*
|
*
|
||||||
* @return Promise resolved with the files.
|
* @return Promise resolved with the files.
|
||||||
*/
|
*/
|
||||||
getSiteFiles(): Promise<any[]> {
|
getSiteFiles(): Promise<AddonFilesFile[]> {
|
||||||
return this.getFiles(this.getSiteFilesRootParams());
|
return this.getFiles(this.getSiteFilesRootParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ export class AddonFilesProvider {
|
||||||
* @param siteid ID of the site. If not defined, use current site.
|
* @param siteid ID of the site. If not defined, use current site.
|
||||||
* @return Promise resolved in success, rejected otherwise.
|
* @return Promise resolved in success, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
moveFromDraftToPrivate(draftId: number, siteId?: string): Promise<any> {
|
moveFromDraftToPrivate(draftId: number, siteId?: string): Promise<null> {
|
||||||
const params = {
|
const params = {
|
||||||
draftid: draftId
|
draftid: draftId
|
||||||
},
|
},
|
||||||
|
@ -414,3 +415,63 @@ export class AddonFilesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File data returned by core_files_get_files.
|
||||||
|
*/
|
||||||
|
export type AddonFilesFile = {
|
||||||
|
contextid: number;
|
||||||
|
component: string;
|
||||||
|
filearea: string;
|
||||||
|
itemid: number;
|
||||||
|
filepath: string;
|
||||||
|
filename: string;
|
||||||
|
isdir: boolean;
|
||||||
|
url: string;
|
||||||
|
timemodified: number;
|
||||||
|
timecreated?: number; // Time created.
|
||||||
|
filesize?: number; // File size.
|
||||||
|
author?: string; // File owner.
|
||||||
|
license?: string; // File license.
|
||||||
|
} & AddonFilesFileCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_files_get_files.
|
||||||
|
*/
|
||||||
|
export type AddonFilesGetFilesResult = {
|
||||||
|
parents: {
|
||||||
|
contextid: number;
|
||||||
|
component: string;
|
||||||
|
filearea: string;
|
||||||
|
itemid: number;
|
||||||
|
filepath: string;
|
||||||
|
filename: string;
|
||||||
|
}[];
|
||||||
|
files: AddonFilesFile[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_user_get_private_files_info.
|
||||||
|
*/
|
||||||
|
export type AddonFilesGetUserPrivateFilesInfoResult = {
|
||||||
|
filecount: number; // Number of files in the area.
|
||||||
|
foldercount: number; // Number of folders in the area.
|
||||||
|
filesize: number; // Total size of the files in the area.
|
||||||
|
filesizewithoutreferences: number; // Total size of the area excluding file references.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for AddonFilesFile.
|
||||||
|
*/
|
||||||
|
export type AddonFilesFileCalculatedData = {
|
||||||
|
link?: { // Calculated in the app. A link to open the folder.
|
||||||
|
contextid?: number; // Folder's contextid.
|
||||||
|
component?: string; // Folder's component.
|
||||||
|
filearea?: string; // Folder's filearea.
|
||||||
|
itemid?: number; // Folder's itemid.
|
||||||
|
filepath?: string; // Folder's filepath.
|
||||||
|
filename?: string; // Folder's filename.
|
||||||
|
};
|
||||||
|
imgPath?: string; // Path to file icon's image.
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, OnDestroy } from '@angular/core';
|
||||||
import { IonicPage } from 'ionic-angular';
|
import { IonicPage } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||||
import { AddonMessageOutputAirnotifierProvider } from '../../providers/airnotifier';
|
import { AddonMessageOutputAirnotifierProvider, AddonMessageOutputAirnotifierDevice } from '../../providers/airnotifier';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of devices.
|
* Page that displays the list of devices.
|
||||||
|
@ -28,7 +28,7 @@ import { AddonMessageOutputAirnotifierProvider } from '../../providers/airnotifi
|
||||||
})
|
})
|
||||||
export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
||||||
|
|
||||||
devices = [];
|
devices: AddonMessageOutputAirnotifierDeviceFormatted[] = [];
|
||||||
devicesLoaded = false;
|
devicesLoaded = false;
|
||||||
|
|
||||||
protected updateTimeout: any;
|
protected updateTimeout: any;
|
||||||
|
@ -54,7 +54,7 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
||||||
const pushId = this.pushNotificationsProvider.getPushId();
|
const pushId = this.pushNotificationsProvider.getPushId();
|
||||||
|
|
||||||
// Convert enabled to boolean and search current device.
|
// Convert enabled to boolean and search current device.
|
||||||
devices.forEach((device) => {
|
devices.forEach((device: AddonMessageOutputAirnotifierDeviceFormatted) => {
|
||||||
device.enable = !!device.enable;
|
device.enable = !!device.enable;
|
||||||
device.current = pushId && pushId == device.pushid;
|
device.current = pushId && pushId == device.pushid;
|
||||||
});
|
});
|
||||||
|
@ -110,8 +110,9 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
||||||
* @param device The device object.
|
* @param device The device object.
|
||||||
* @param enable True to enable the device, false to disable it.
|
* @param enable True to enable the device, false to disable it.
|
||||||
*/
|
*/
|
||||||
enableDevice(device: any, enable: boolean): void {
|
enableDevice(device: AddonMessageOutputAirnotifierDeviceFormatted, enable: boolean): void {
|
||||||
device.updating = true;
|
device.updating = true;
|
||||||
|
|
||||||
this.airnotifierProivder.enableDevice(device.id, enable).then(() => {
|
this.airnotifierProivder.enableDevice(device.id, enable).then(() => {
|
||||||
// Update the list of devices since it was modified.
|
// Update the list of devices since it was modified.
|
||||||
this.updateDevicesAfterDelay();
|
this.updateDevicesAfterDelay();
|
||||||
|
@ -135,3 +136,11 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User device with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonMessageOutputAirnotifierDeviceFormatted = AddonMessageOutputAirnotifierDevice & {
|
||||||
|
current?: boolean; // Calculated in the app. Whether it's the current device.
|
||||||
|
updating?: boolean; // Calculated in the app. Whether the device enable is being updated right now.
|
||||||
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreConfigConstants } from '../../../../configconstants';
|
import { CoreConfigConstants } from '../../../../configconstants';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle Airnotifier message output.
|
* Service to handle Airnotifier message output.
|
||||||
|
@ -39,14 +40,16 @@ export class AddonMessageOutputAirnotifierProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved if success.
|
* @return Promise resolved if success.
|
||||||
*/
|
*/
|
||||||
enableDevice(deviceId: number, enable: boolean, siteId?: string): Promise<any> {
|
enableDevice(deviceId: number, enable: boolean, siteId?: string): Promise<void> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
deviceid: deviceId,
|
deviceid: deviceId,
|
||||||
enable: enable ? 1 : 0
|
enable: enable ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('message_airnotifier_enable_device', data).then((result) => {
|
return site.write('message_airnotifier_enable_device', data)
|
||||||
|
.then((result: AddonMessageOutputAirnotifierEnableDeviceResult) => {
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
// Fail. Reject with warning message if any.
|
// Fail. Reject with warning message if any.
|
||||||
if (result.warnings && result.warnings.length) {
|
if (result.warnings && result.warnings.length) {
|
||||||
|
@ -74,7 +77,7 @@ export class AddonMessageOutputAirnotifierProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved with the devices.
|
* @return Promise resolved with the devices.
|
||||||
*/
|
*/
|
||||||
getUserDevices(siteId?: string): Promise<any> {
|
getUserDevices(siteId?: string): Promise<AddonMessageOutputAirnotifierDevice[]> {
|
||||||
this.logger.debug('Get user devices');
|
this.logger.debug('Get user devices');
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -86,7 +89,8 @@ export class AddonMessageOutputAirnotifierProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('message_airnotifier_get_user_devices', data, preSets).then((data) => {
|
return site.read('message_airnotifier_get_user_devices', data, preSets)
|
||||||
|
.then((data: AddonMessageOutputAirnotifierGetUserDevicesResult) => {
|
||||||
return data.devices;
|
return data.devices;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -115,3 +119,36 @@ export class AddonMessageOutputAirnotifierProvider {
|
||||||
this.sitesProvider.wsAvailableInCurrentSite('message_airnotifier_get_user_devices');
|
this.sitesProvider.wsAvailableInCurrentSite('message_airnotifier_get_user_devices');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device data returned by WS message_airnotifier_get_user_devices.
|
||||||
|
*/
|
||||||
|
export type AddonMessageOutputAirnotifierDevice = {
|
||||||
|
id: number; // Device id (in the message_airnotifier table).
|
||||||
|
appid: string; // The app id, something like com.moodle.moodlemobile.
|
||||||
|
name: string; // The device name, 'occam' or 'iPhone' etc.
|
||||||
|
model: string; // The device model 'Nexus4' or 'iPad1,1' etc.
|
||||||
|
platform: string; // The device platform 'iOS' or 'Android' etc.
|
||||||
|
version: string; // The device version '6.1.2' or '4.2.2' etc.
|
||||||
|
pushid: string; // The device PUSH token/key/identifier/registration id.
|
||||||
|
uuid: string; // The device UUID.
|
||||||
|
enable: number | boolean; // Whether the device is enabled or not.
|
||||||
|
timecreated: number; // Time created.
|
||||||
|
timemodified: number; // Time modified.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS message_airnotifier_enable_device.
|
||||||
|
*/
|
||||||
|
export type AddonMessageOutputAirnotifierEnableDeviceResult = {
|
||||||
|
success: boolean; // True if success.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS message_airnotifier_get_user_devices.
|
||||||
|
*/
|
||||||
|
export type AddonMessageOutputAirnotifierGetUserDevicesResult = {
|
||||||
|
devices: AddonMessageOutputAirnotifierDevice[]; // List of devices.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@
|
||||||
import { Content } from 'ionic-angular';
|
import { Content } from 'ionic-angular';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestro
|
||||||
loaded = false;
|
loaded = false;
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
contacts = [];
|
contacts: AddonMessagesConversationMember[] = [];
|
||||||
selectedUserId: number;
|
selectedUserId: number;
|
||||||
|
|
||||||
protected memberInfoObserver;
|
protected memberInfoObserver;
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@
|
||||||
import { Content } from 'ionic-angular';
|
import { Content } from 'ionic-angular';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ export class AddonMessagesContactRequestsComponent implements OnInit, OnDestroy
|
||||||
loaded = false;
|
loaded = false;
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
requests = [];
|
requests: AddonMessagesConversationMember[] = [];
|
||||||
selectedUserId: number;
|
selectedUserId: number;
|
||||||
|
|
||||||
protected memberInfoObserver;
|
protected memberInfoObserver;
|
||||||
|
|
|
@ -16,7 +16,9 @@ import { Component } from '@angular/core';
|
||||||
import { NavParams } from 'ionic-angular';
|
import { NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import {
|
||||||
|
AddonMessagesProvider, AddonMessagesGetContactsResult, AddonMessagesSearchContactsContact
|
||||||
|
} from '../../providers/messages';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
@ -42,7 +44,10 @@ export class AddonMessagesContactsComponent {
|
||||||
searchType = 'search';
|
searchType = 'search';
|
||||||
loadingMessage = '';
|
loadingMessage = '';
|
||||||
hasContacts = false;
|
hasContacts = false;
|
||||||
contacts = {
|
contacts: AddonMessagesGetContactsFormatted = {
|
||||||
|
online: [],
|
||||||
|
offline: [],
|
||||||
|
strangers: [],
|
||||||
search: []
|
search: []
|
||||||
};
|
};
|
||||||
searchString = '';
|
searchString = '';
|
||||||
|
@ -205,7 +210,7 @@ export class AddonMessagesContactsComponent {
|
||||||
this.searchString = query;
|
this.searchString = query;
|
||||||
this.contactTypes = ['search'];
|
this.contactTypes = ['search'];
|
||||||
|
|
||||||
this.contacts['search'] = this.sortUsers(result);
|
this.contacts.search = this.sortUsers(result);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||||
});
|
});
|
||||||
|
@ -234,3 +239,10 @@ export class AddonMessagesContactsComponent {
|
||||||
this.memberInfoObserver && this.memberInfoObserver.off();
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contacts with some calculated data.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetContactsFormatted = AddonMessagesGetContactsResult & {
|
||||||
|
search?: AddonMessagesSearchContactsContact[]; // Calculated in the app. Result of searching users.
|
||||||
|
};
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import {
|
||||||
|
AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember
|
||||||
|
} from '../../providers/messages';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,8 +30,8 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
export class AddonMessagesConversationInfoPage implements OnInit {
|
export class AddonMessagesConversationInfoPage implements OnInit {
|
||||||
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
conversation: any;
|
conversation: AddonMessagesConversationFormatted;
|
||||||
members = [];
|
members: AddonMessagesConversationMember[] = [];
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,10 @@ import { IonicPage, NavParams, NavController, Content, ModalController } from 'i
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import {
|
||||||
|
AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember, AddonMessagesConversationMessage,
|
||||||
|
AddonMessagesGetMessagesMessage
|
||||||
|
} from '../../providers/messages';
|
||||||
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
||||||
import { AddonMessagesSyncProvider } from '../../providers/sync';
|
import { AddonMessagesSyncProvider } from '../../providers/sync';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
|
@ -54,7 +57,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
protected messagesBeingSent = 0;
|
protected messagesBeingSent = 0;
|
||||||
protected pagesLoaded = 1;
|
protected pagesLoaded = 1;
|
||||||
protected lastMessage = {text: '', timecreated: 0};
|
protected lastMessage = {text: '', timecreated: 0};
|
||||||
protected keepMessageMap = {};
|
protected keepMessageMap: {[hash: string]: boolean} = {};
|
||||||
protected syncObserver: any;
|
protected syncObserver: any;
|
||||||
protected oldContentHeight = 0;
|
protected oldContentHeight = 0;
|
||||||
protected keyboardObserver: any;
|
protected keyboardObserver: any;
|
||||||
|
@ -64,7 +67,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
|
protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
|
||||||
|
|
||||||
conversationId: number; // Conversation ID. Undefined if it's a new individual conversation.
|
conversationId: number; // Conversation ID. Undefined if it's a new individual conversation.
|
||||||
conversation: any; // The conversation object (if it exists).
|
conversation: AddonMessagesConversationFormatted; // The conversation object (if it exists).
|
||||||
userId: number; // User ID you're talking to (only if group messaging not enabled or it's a new individual conversation).
|
userId: number; // User ID you're talking to (only if group messaging not enabled or it's a new individual conversation).
|
||||||
currentUserId: number;
|
currentUserId: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -74,18 +77,18 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
showKeyboard = false;
|
showKeyboard = false;
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
messages = [];
|
messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[] = [];
|
||||||
showDelete = false;
|
showDelete = false;
|
||||||
canDelete = false;
|
canDelete = false;
|
||||||
groupMessagingEnabled: boolean;
|
groupMessagingEnabled: boolean;
|
||||||
isGroup = false;
|
isGroup = false;
|
||||||
members: any = {}; // Members that wrote a message, indexed by ID.
|
members: {[id: number]: AddonMessagesConversationMember} = {}; // Members that wrote a message, indexed by ID.
|
||||||
favouriteIcon = 'fa-star';
|
favouriteIcon = 'fa-star';
|
||||||
favouriteIconSlash = false;
|
favouriteIconSlash = false;
|
||||||
deleteIcon = 'trash';
|
deleteIcon = 'trash';
|
||||||
blockIcon = 'close-circle';
|
blockIcon = 'close-circle';
|
||||||
addRemoveIcon = 'person';
|
addRemoveIcon = 'person';
|
||||||
otherMember: any; // Other member information (individual conversations only).
|
otherMember: AddonMessagesConversationMember; // Other member information (individual conversations only).
|
||||||
footerType: 'message' | 'blocked' | 'requiresContact' | 'requestSent' | 'requestReceived' | 'unable';
|
footerType: 'message' | 'blocked' | 'requiresContact' | 'requestSent' | 'requestReceived' | 'unable';
|
||||||
requestContactSent = false;
|
requestContactSent = false;
|
||||||
requestContactReceived = false;
|
requestContactReceived = false;
|
||||||
|
@ -139,7 +142,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param message Message to be added.
|
* @param message Message to be added.
|
||||||
* @param keep If set the keep flag or not.
|
* @param keep If set the keep flag or not.
|
||||||
*/
|
*/
|
||||||
protected addMessage(message: any, keep: boolean = true): void {
|
protected addMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted,
|
||||||
|
keep: boolean = true): void {
|
||||||
|
|
||||||
/* Create a hash to identify the message. The text of online messages isn't reliable because it can have random data
|
/* Create a hash to identify the message. The text of online messages isn't reliable because it can have random data
|
||||||
like VideoJS ID. Try to use id and fallback to text for offline messages. */
|
like VideoJS ID. Try to use id and fallback to text for offline messages. */
|
||||||
message.hash = Md5.hashAsciiStr(String(message.id || message.text || '')) + '#' + message.timecreated + '#' +
|
message.hash = Md5.hashAsciiStr(String(message.id || message.text || '')) + '#' + message.timecreated + '#' +
|
||||||
|
@ -158,7 +163,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @param hash Hash of the message to be removed.
|
* @param hash Hash of the message to be removed.
|
||||||
*/
|
*/
|
||||||
protected removeMessage(hash: any): void {
|
protected removeMessage(hash: string): void {
|
||||||
if (this.keepMessageMap[hash]) {
|
if (this.keepMessageMap[hash]) {
|
||||||
// Selected to keep it, clear the flag.
|
// Selected to keep it, clear the flag.
|
||||||
this.keepMessageMap[hash] = false;
|
this.keepMessageMap[hash] = false;
|
||||||
|
@ -261,10 +266,11 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
if (!this.title && this.messages.length) {
|
if (!this.title && this.messages.length) {
|
||||||
// Didn't receive the fullname via argument. Try to get it from messages.
|
// Didn't receive the fullname via argument. Try to get it from messages.
|
||||||
// It's possible that name cannot be resolved when no messages were yet exchanged.
|
// It's possible that name cannot be resolved when no messages were yet exchanged.
|
||||||
if (this.messages[0].useridto != this.currentUserId) {
|
const firstMessage = <AddonMessagesGetMessagesMessageFormatted> this.messages[0];
|
||||||
this.title = this.messages[0].usertofullname || '';
|
if (firstMessage.useridto != this.currentUserId) {
|
||||||
|
this.title = firstMessage.usertofullname || '';
|
||||||
} else {
|
} else {
|
||||||
this.title = this.messages[0].userfromfullname || '';
|
this.title = firstMessage.userfromfullname || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -302,7 +308,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchMessages(): Promise<any> {
|
protected fetchMessages(): Promise<void> {
|
||||||
this.loadMoreError = false;
|
this.loadMoreError = false;
|
||||||
|
|
||||||
if (this.messagesBeingSent > 0) {
|
if (this.messagesBeingSent > 0) {
|
||||||
|
@ -341,7 +347,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
return this.getDiscussionMessages(this.pagesLoaded);
|
return this.getDiscussionMessages(this.pagesLoaded);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).then((messages) => {
|
}).then((messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[]) => {
|
||||||
this.loadMessages(messages);
|
this.loadMessages(messages);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
@ -353,7 +359,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @param messages Messages to load.
|
* @param messages Messages to load.
|
||||||
*/
|
*/
|
||||||
protected loadMessages(messages: any[]): void {
|
protected loadMessages(messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[])
|
||||||
|
: void {
|
||||||
|
|
||||||
if (this.viewDestroyed) {
|
if (this.viewDestroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -382,7 +390,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
this.messagesProvider.sortMessages(this.messages);
|
this.messagesProvider.sortMessages(this.messages);
|
||||||
|
|
||||||
// Calculate which messages need to display the date or user data.
|
// Calculate which messages need to display the date or user data.
|
||||||
this.messages.forEach((message, index): any => {
|
this.messages.forEach((message, index) => {
|
||||||
message.showDate = this.showDate(message, this.messages[index - 1]);
|
message.showDate = this.showDate(message, this.messages[index - 1]);
|
||||||
message.showUserData = this.showUserData(message, this.messages[index - 1]);
|
message.showUserData = this.showUserData(message, this.messages[index - 1]);
|
||||||
message.showTail = this.showTail(message, this.messages[index + 1]);
|
message.showTail = this.showTail(message, this.messages[index + 1]);
|
||||||
|
@ -411,20 +419,22 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @return Promise resolved with a boolean: whether the conversation exists or not.
|
* @return Promise resolved with a boolean: whether the conversation exists or not.
|
||||||
*/
|
*/
|
||||||
protected getConversation(conversationId: number, userId: number): Promise<boolean> {
|
protected getConversation(conversationId: number, userId: number): Promise<boolean> {
|
||||||
let promise,
|
let promise: Promise<number>,
|
||||||
fallbackConversation;
|
fallbackConversation: AddonMessagesConversationFormatted;
|
||||||
|
|
||||||
// Try to get the conversationId if we don't have it.
|
// Try to get the conversationId if we don't have it.
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
promise = Promise.resolve(conversationId);
|
promise = Promise.resolve(conversationId);
|
||||||
} else {
|
} else {
|
||||||
|
let subPromise: Promise<AddonMessagesConversationFormatted>;
|
||||||
|
|
||||||
if (userId == this.currentUserId && this.messagesProvider.isSelfConversationEnabled()) {
|
if (userId == this.currentUserId && this.messagesProvider.isSelfConversationEnabled()) {
|
||||||
promise = this.messagesProvider.getSelfConversation();
|
subPromise = this.messagesProvider.getSelfConversation();
|
||||||
} else {
|
} else {
|
||||||
promise = this.messagesProvider.getConversationBetweenUsers(userId, undefined, true);
|
subPromise = this.messagesProvider.getConversationBetweenUsers(userId, undefined, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise = promise.then((conversation) => {
|
promise = subPromise.then((conversation) => {
|
||||||
fallbackConversation = conversation;
|
fallbackConversation = conversation;
|
||||||
|
|
||||||
return conversation.id;
|
return conversation.id;
|
||||||
|
@ -437,14 +447,14 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return this.messagesProvider.getConversation(conversationId, undefined, true);
|
return this.messagesProvider.getConversation(conversationId, undefined, true);
|
||||||
}).catch((error) => {
|
}).catch((error): any => {
|
||||||
// Get conversation failed, use the fallback one if we have it.
|
// Get conversation failed, use the fallback one if we have it.
|
||||||
if (fallbackConversation) {
|
if (fallbackConversation) {
|
||||||
return fallbackConversation;
|
return fallbackConversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}).then((conversation) => {
|
}).then((conversation: AddonMessagesConversationFormatted) => {
|
||||||
this.conversation = conversation;
|
this.conversation = conversation;
|
||||||
|
|
||||||
if (conversation) {
|
if (conversation) {
|
||||||
|
@ -495,7 +505,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param offset Offset for message list.
|
* @param offset Offset for message list.
|
||||||
* @return Promise resolved with the list of messages.
|
* @return Promise resolved with the list of messages.
|
||||||
*/
|
*/
|
||||||
protected getConversationMessages(pagesToLoad: number, offset: number = 0): Promise<any[]> {
|
protected getConversationMessages(pagesToLoad: number, offset: number = 0)
|
||||||
|
: Promise<AddonMessagesConversationMessageFormatted[]> {
|
||||||
|
|
||||||
const excludePending = offset > 0;
|
const excludePending = offset > 0;
|
||||||
|
|
||||||
return this.messagesProvider.getConversationMessages(this.conversationId, excludePending, offset).then((result) => {
|
return this.messagesProvider.getConversationMessages(this.conversationId, excludePending, offset).then((result) => {
|
||||||
|
@ -535,7 +547,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
protected getDiscussionMessages(pagesToLoad: number, lfReceivedUnread: number = 0, lfReceivedRead: number = 0,
|
protected getDiscussionMessages(pagesToLoad: number, lfReceivedUnread: number = 0, lfReceivedRead: number = 0,
|
||||||
lfSentUnread: number = 0, lfSentRead: number = 0): Promise<any> {
|
lfSentUnread: number = 0, lfSentRead: number = 0): Promise<AddonMessagesGetMessagesMessageFormatted[]> {
|
||||||
|
|
||||||
// Only get offline messages if we're loading the first "page".
|
// Only get offline messages if we're loading the first "page".
|
||||||
const excludePending = lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0;
|
const excludePending = lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0;
|
||||||
|
@ -547,7 +559,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
pagesToLoad--;
|
pagesToLoad--;
|
||||||
if (pagesToLoad > 0 && result.canLoadMore) {
|
if (pagesToLoad > 0 && result.canLoadMore) {
|
||||||
// More pages to load. Calculate new limit froms.
|
// More pages to load. Calculate new limit froms.
|
||||||
result.messages.forEach((message) => {
|
result.messages.forEach((message: AddonMessagesGetMessagesMessageFormatted) => {
|
||||||
if (!message.pending) {
|
if (!message.pending) {
|
||||||
if (message.useridfrom == this.userId) {
|
if (message.useridfrom == this.userId) {
|
||||||
if (message.read) {
|
if (message.read) {
|
||||||
|
@ -598,7 +610,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
for (const x in this.messages) {
|
for (const x in this.messages) {
|
||||||
const message = this.messages[x];
|
const message = this.messages[x];
|
||||||
// If an unread message is found, mark all messages as read.
|
// If an unread message is found, mark all messages as read.
|
||||||
if (message.useridfrom != this.currentUserId && message.read == 0) {
|
if (message.useridfrom != this.currentUserId &&
|
||||||
|
(<AddonMessagesGetMessagesMessageFormatted> message).read == 0) {
|
||||||
messageUnreadFound = true;
|
messageUnreadFound = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -616,7 +629,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
promise = this.messagesProvider.markAllMessagesRead(this.userId).then(() => {
|
promise = this.messagesProvider.markAllMessagesRead(this.userId).then(() => {
|
||||||
// Mark all messages as read.
|
// Mark all messages as read.
|
||||||
this.messages.forEach((message) => {
|
this.messages.forEach((message) => {
|
||||||
message.read = 1;
|
(<AddonMessagesGetMessagesMessageFormatted> message).read = 1;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -630,10 +643,10 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
// Mark each message as read one by one.
|
// Mark each message as read one by one.
|
||||||
this.messages.forEach((message) => {
|
this.messages.forEach((message) => {
|
||||||
// If the message is unread, call this.messagesProvider.markMessageRead.
|
// If the message is unread, call this.messagesProvider.markMessageRead.
|
||||||
if (message.useridfrom != this.currentUserId && message.read == 0) {
|
if (message.useridfrom != this.currentUserId && (<AddonMessagesGetMessagesMessageFormatted> message).read == 0) {
|
||||||
promises.push(this.messagesProvider.markMessageRead(message.id).then(() => {
|
promises.push(this.messagesProvider.markMessageRead(message.id).then(() => {
|
||||||
readChanged = true;
|
readChanged = true;
|
||||||
message.read = 1;
|
(<AddonMessagesGetMessagesMessageFormatted> message).read = 1;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -703,7 +716,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
if (!message.pending && message.useridfrom != this.currentUserId) {
|
if (!message.pending && message.useridfrom != this.currentUserId) {
|
||||||
found++;
|
found++;
|
||||||
if (found == this.conversation.unreadcount) {
|
if (found == this.conversation.unreadcount) {
|
||||||
this.unreadMessageFrom = parseInt(message.id, 10);
|
this.unreadMessageFrom = Number(message.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,13 +726,13 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
let previousMessageRead = false;
|
let previousMessageRead = false;
|
||||||
|
|
||||||
for (const x in this.messages) {
|
for (const x in this.messages) {
|
||||||
const message = this.messages[x];
|
const message = <AddonMessagesGetMessagesMessageFormatted> this.messages[x];
|
||||||
if (message.useridfrom != this.currentUserId) {
|
if (message.useridfrom != this.currentUserId) {
|
||||||
const unreadFrom = message.read == 0 && previousMessageRead;
|
const unreadFrom = message.read == 0 && previousMessageRead;
|
||||||
|
|
||||||
if (unreadFrom) {
|
if (unreadFrom) {
|
||||||
// Save where the label is placed.
|
// Save where the label is placed.
|
||||||
this.unreadMessageFrom = parseInt(message.id, 10);
|
this.unreadMessageFrom = Number(message.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,8 +821,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
*
|
*
|
||||||
* @param message Message to be copied.
|
* @param message Message to be copied.
|
||||||
*/
|
*/
|
||||||
copyMessage(message: any): void {
|
copyMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): void {
|
||||||
const text = this.textUtils.decodeHTMLEntities(message.smallmessage || message.text || '');
|
const text = this.textUtils.decodeHTMLEntities(
|
||||||
|
(<AddonMessagesGetMessagesMessageFormatted> message).smallmessage || message.text || '');
|
||||||
this.utils.copyToClipboard(text);
|
this.utils.copyToClipboard(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,7 +833,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param message Message object to delete.
|
* @param message Message object to delete.
|
||||||
* @param index Index where the message is to delete it from the view.
|
* @param index Index where the message is to delete it from the view.
|
||||||
*/
|
*/
|
||||||
deleteMessage(message: any, index: number): void {
|
deleteMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted, index: number)
|
||||||
|
: void {
|
||||||
|
|
||||||
const canDeleteAll = this.conversation && this.conversation.candeletemessagesforallusers,
|
const canDeleteAll = this.conversation && this.conversation.candeletemessagesforallusers,
|
||||||
langKey = message.pending || canDeleteAll || this.isSelf ? 'core.areyousure' :
|
langKey = message.pending || canDeleteAll || this.isSelf ? 'core.areyousure' :
|
||||||
'addon.messages.deletemessageconfirmation',
|
'addon.messages.deletemessageconfirmation',
|
||||||
|
@ -860,7 +876,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
loadPrevious(infiniteComplete?: any): Promise<any> {
|
loadPrevious(infiniteComplete?: any): Promise<void> {
|
||||||
let infiniteHeight = this.infinite ? this.infinite.getHeight() : 0;
|
let infiniteHeight = this.infinite ? this.infinite.getHeight() : 0;
|
||||||
const scrollHeight = this.domUtils.getScrollHeight(this.content);
|
const scrollHeight = this.domUtils.getScrollHeight(this.content);
|
||||||
|
|
||||||
|
@ -962,7 +978,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param text Message text.
|
* @param text Message text.
|
||||||
*/
|
*/
|
||||||
sendMessage(text: string): void {
|
sendMessage(text: string): void {
|
||||||
let message;
|
let message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted;
|
||||||
|
|
||||||
this.hideUnreadLabel();
|
this.hideUnreadLabel();
|
||||||
|
|
||||||
|
@ -970,6 +986,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
this.scrollBottom = true;
|
this.scrollBottom = true;
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
|
id: null,
|
||||||
pending: true,
|
pending: true,
|
||||||
sending: true,
|
sending: true,
|
||||||
useridfrom: this.currentUserId,
|
useridfrom: this.currentUserId,
|
||||||
|
@ -985,7 +1002,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
// If there is an ongoing fetch, wait for it to finish.
|
// If there is an ongoing fetch, wait for it to finish.
|
||||||
// Otherwise, if a message is sent while fetching it could disappear until the next fetch.
|
// Otherwise, if a message is sent while fetching it could disappear until the next fetch.
|
||||||
this.waitForFetch().finally(() => {
|
this.waitForFetch().finally(() => {
|
||||||
let promise;
|
let promise: Promise<{sent: boolean, message: any}>;
|
||||||
|
|
||||||
if (this.conversationId) {
|
if (this.conversationId) {
|
||||||
promise = this.messagesProvider.sendMessageToConversation(this.conversation, text);
|
promise = this.messagesProvider.sendMessageToConversation(this.conversation, text);
|
||||||
|
@ -1050,7 +1067,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param prevMessage Previous message where to compare the date with.
|
* @param prevMessage Previous message where to compare the date with.
|
||||||
* @return If date has changed and should be shown.
|
* @return If date has changed and should be shown.
|
||||||
*/
|
*/
|
||||||
showDate(message: any, prevMessage?: any): boolean {
|
showDate(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted,
|
||||||
|
prevMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean {
|
||||||
|
|
||||||
if (!prevMessage) {
|
if (!prevMessage) {
|
||||||
// First message, show it.
|
// First message, show it.
|
||||||
return true;
|
return true;
|
||||||
|
@ -1068,7 +1087,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param prevMessage Previous message.
|
* @param prevMessage Previous message.
|
||||||
* @return Whether user data should be shown.
|
* @return Whether user data should be shown.
|
||||||
*/
|
*/
|
||||||
showUserData(message: any, prevMessage?: any): boolean {
|
showUserData(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted,
|
||||||
|
prevMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean {
|
||||||
|
|
||||||
return this.isGroup && message.useridfrom != this.currentUserId && this.members[message.useridfrom] &&
|
return this.isGroup && message.useridfrom != this.currentUserId && this.members[message.useridfrom] &&
|
||||||
(!prevMessage || prevMessage.useridfrom != message.useridfrom || message.showDate);
|
(!prevMessage || prevMessage.useridfrom != message.useridfrom || message.showDate);
|
||||||
}
|
}
|
||||||
|
@ -1080,7 +1101,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
* @param nextMessage Next message.
|
* @param nextMessage Next message.
|
||||||
* @return Whether user data should be shown.
|
* @return Whether user data should be shown.
|
||||||
*/
|
*/
|
||||||
showTail(message: any, nextMessage?: any): boolean {
|
showTail(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted,
|
||||||
|
nextMessage?: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): boolean {
|
||||||
return !nextMessage || nextMessage.useridfrom != message.useridfrom || nextMessage.showDate;
|
return !nextMessage || nextMessage.useridfrom != message.useridfrom || nextMessage.showDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1422,3 +1444,26 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
||||||
this.viewDestroyed = true;
|
this.viewDestroyed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation message with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonMessagesConversationMessageFormatted = AddonMessagesConversationMessage & {
|
||||||
|
pending?: boolean; // Calculated in the app. Whether the message is pending to be sent.
|
||||||
|
sending?: boolean; // Calculated in the app. Whether the message is being sent right now.
|
||||||
|
hash?: string; // Calculated in the app. A hash to identify the message.
|
||||||
|
showDate?: boolean; // Calculated in the app. Whether to show the date before the message.
|
||||||
|
showUserData?: boolean; // Calculated in the app. Whether to show the user data in the message.
|
||||||
|
showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonMessagesGetMessagesMessageFormatted = AddonMessagesGetMessagesMessage & {
|
||||||
|
sending?: boolean; // Calculated in the app. Whether the message is being sent right now.
|
||||||
|
hash?: string; // Calculated in the app. A hash to identify the message.
|
||||||
|
showDate?: boolean; // Calculated in the app. Whether to show the date before the message.
|
||||||
|
showUserData?: boolean; // Calculated in the app. Whether to show the user data in the message.
|
||||||
|
showTail?: boolean; // Calculated in the app. Whether to show a "tail" in the message.
|
||||||
|
};
|
||||||
|
|
|
@ -17,7 +17,9 @@ import { IonicPage, Platform, NavController, NavParams, Content } from 'ionic-an
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import {
|
||||||
|
AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMessage
|
||||||
|
} from '../../providers/messages';
|
||||||
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
@ -45,19 +47,19 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
selectedConversationId: number;
|
selectedConversationId: number;
|
||||||
selectedUserId: number;
|
selectedUserId: number;
|
||||||
contactRequestsCount = 0;
|
contactRequestsCount = 0;
|
||||||
favourites: any = {
|
favourites: AddonMessagesGroupConversationOption = {
|
||||||
type: null,
|
type: null,
|
||||||
favourites: true,
|
favourites: true,
|
||||||
count: 0,
|
count: 0,
|
||||||
unread: 0
|
unread: 0,
|
||||||
};
|
};
|
||||||
group: any = {
|
group: AddonMessagesGroupConversationOption = {
|
||||||
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP,
|
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP,
|
||||||
favourites: false,
|
favourites: false,
|
||||||
count: 0,
|
count: 0,
|
||||||
unread: 0
|
unread: 0
|
||||||
};
|
};
|
||||||
individual: any = {
|
individual: AddonMessagesGroupConversationOption = {
|
||||||
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||||
favourites: false,
|
favourites: false,
|
||||||
count: 0,
|
count: 0,
|
||||||
|
@ -331,7 +333,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchDataForExpandedOption(): Promise<any> {
|
protected fetchDataForExpandedOption(): Promise<void> {
|
||||||
const expandedOption = this.getExpandedOption();
|
const expandedOption = this.getExpandedOption();
|
||||||
|
|
||||||
if (expandedOption) {
|
if (expandedOption) {
|
||||||
|
@ -349,12 +351,12 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param getCounts Whether to get counts data.
|
* @param getCounts Whether to get counts data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
fetchDataForOption(option: any, loadingMore?: boolean, getCounts?: boolean): Promise<void> {
|
fetchDataForOption(option: AddonMessagesGroupConversationOption, loadingMore?: boolean, getCounts?: boolean): Promise<void> {
|
||||||
option.loadMoreError = false;
|
option.loadMoreError = false;
|
||||||
|
|
||||||
const limitFrom = loadingMore ? option.conversations.length : 0,
|
const limitFrom = loadingMore ? option.conversations.length : 0,
|
||||||
promises = [];
|
promises = [];
|
||||||
let data,
|
let data: {conversations: AddonMessagesConversationForList[], canLoadMore: boolean},
|
||||||
offlineMessages;
|
offlineMessages;
|
||||||
|
|
||||||
// Get the conversations and, if needed, the offline messages. Always try to get the latest data.
|
// Get the conversations and, if needed, the offline messages. Always try to get the latest data.
|
||||||
|
@ -422,7 +424,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param option The option to search in. If not defined, search in all options.
|
* @param option The option to search in. If not defined, search in all options.
|
||||||
* @return Conversation.
|
* @return Conversation.
|
||||||
*/
|
*/
|
||||||
protected findConversation(conversationId: number, userId?: number, option?: any): any {
|
protected findConversation(conversationId: number, userId?: number, option?: AddonMessagesGroupConversationOption)
|
||||||
|
: AddonMessagesConversationForList {
|
||||||
|
|
||||||
if (conversationId) {
|
if (conversationId) {
|
||||||
const conversations = option ? (option.conversations || []) : ((this.favourites.conversations || [])
|
const conversations = option ? (option.conversations || []) : ((this.favourites.conversations || [])
|
||||||
.concat(this.group.conversations || []).concat(this.individual.conversations || []));
|
.concat(this.group.conversations || []).concat(this.individual.conversations || []));
|
||||||
|
@ -445,7 +449,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @return Option currently expanded.
|
* @return Option currently expanded.
|
||||||
*/
|
*/
|
||||||
protected getExpandedOption(): any {
|
protected getExpandedOption(): AddonMessagesGroupConversationOption {
|
||||||
if (this.favourites.expanded) {
|
if (this.favourites.expanded) {
|
||||||
return this.favourites;
|
return this.favourites;
|
||||||
} else if (this.group.expanded) {
|
} else if (this.group.expanded) {
|
||||||
|
@ -495,9 +499,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @param option The option to fetch data for.
|
* @param option The option to fetch data for.
|
||||||
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
* @return Resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
loadMoreConversations(option: any, infiniteComplete?: any): Promise<any> {
|
loadMoreConversations(option: AddonMessagesGroupConversationOption, infiniteComplete?: any): Promise<void> {
|
||||||
return this.fetchDataForOption(option, true).catch((error) => {
|
return this.fetchDataForOption(option, true).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true);
|
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true);
|
||||||
option.loadMoreError = true;
|
option.loadMoreError = true;
|
||||||
|
@ -513,7 +517,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param messages Offline messages.
|
* @param messages Offline messages.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected loadOfflineMessages(option: any, messages: any[]): Promise<any> {
|
protected loadOfflineMessages(option: AddonMessagesGroupConversationOption, messages: any[]): Promise<any> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
messages.forEach((message) => {
|
messages.forEach((message) => {
|
||||||
|
@ -588,7 +592,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param conversation Conversation where to put the last message.
|
* @param conversation Conversation where to put the last message.
|
||||||
* @param message Offline message to add.
|
* @param message Offline message to add.
|
||||||
*/
|
*/
|
||||||
protected addLastOfflineMessage(conversation: any, message: any): void {
|
protected addLastOfflineMessage(conversation: any, message: AddonMessagesConversationMessage): void {
|
||||||
conversation.lastmessage = message.text;
|
conversation.lastmessage = message.text;
|
||||||
conversation.lastmessagedate = message.timecreated / 1000;
|
conversation.lastmessagedate = message.timecreated / 1000;
|
||||||
conversation.lastmessagepending = true;
|
conversation.lastmessagepending = true;
|
||||||
|
@ -601,7 +605,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param conversation Conversation to check.
|
* @param conversation Conversation to check.
|
||||||
* @return Option object.
|
* @return Option object.
|
||||||
*/
|
*/
|
||||||
protected getConversationOption(conversation: any): any {
|
protected getConversationOption(conversation: AddonMessagesConversationForList): AddonMessagesGroupConversationOption {
|
||||||
if (conversation.isfavourite) {
|
if (conversation.isfavourite) {
|
||||||
return this.favourites;
|
return this.favourites;
|
||||||
} else if (conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) {
|
} else if (conversation.type == AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP) {
|
||||||
|
@ -618,7 +622,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param refreshUnreadCounts Whether to refresh unread counts.
|
* @param refreshUnreadCounts Whether to refresh unread counts.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise<any> {
|
refreshData(refresher?: any, refreshUnreadCounts: boolean = true): Promise<void> {
|
||||||
// Don't invalidate conversations and so, they always try to get latest data.
|
// Don't invalidate conversations and so, they always try to get latest data.
|
||||||
const promises = [
|
const promises = [
|
||||||
this.messagesProvider.invalidateContactRequestsCountCache(this.siteId)
|
this.messagesProvider.invalidateContactRequestsCountCache(this.siteId)
|
||||||
|
@ -638,7 +642,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
*
|
*
|
||||||
* @param option The option to expand/collapse.
|
* @param option The option to expand/collapse.
|
||||||
*/
|
*/
|
||||||
toggle(option: any): void {
|
toggle(option: AddonMessagesGroupConversationOption): void {
|
||||||
if (option.expanded) {
|
if (option.expanded) {
|
||||||
// Already expanded, close it.
|
// Already expanded, close it.
|
||||||
option.expanded = false;
|
option.expanded = false;
|
||||||
|
@ -658,7 +662,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
* @param getCounts Whether to get counts data.
|
* @param getCounts Whether to get counts data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected expandOption(option: any, getCounts?: boolean): Promise<any> {
|
protected expandOption(option: AddonMessagesGroupConversationOption, getCounts?: boolean): Promise<void> {
|
||||||
// Collapse all and expand the right one.
|
// Collapse all and expand the right one.
|
||||||
this.favourites.expanded = false;
|
this.favourites.expanded = false;
|
||||||
this.group.expanded = false;
|
this.group.expanded = false;
|
||||||
|
@ -715,3 +719,25 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
||||||
this.memberInfoObserver && this.memberInfoObserver.off();
|
this.memberInfoObserver && this.memberInfoObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation options.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGroupConversationOption = {
|
||||||
|
type: number; // Option type.
|
||||||
|
favourites: boolean; // Whether it contains favourites conversations.
|
||||||
|
count: number; // Number of conversations.
|
||||||
|
unread?: number; // Number of unread conversations.
|
||||||
|
expanded?: boolean; // Whether the option is currently expanded.
|
||||||
|
loading?: boolean; // Whether the option is being loaded.
|
||||||
|
canLoadMore?: boolean; // Whether it can load more data.
|
||||||
|
loadMoreError?: boolean; // Whether there was an error loading more conversations.
|
||||||
|
conversations?: AddonMessagesConversationForList[]; // List of conversations.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatted conversation with some calculated data for the list.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesConversationForList = AddonMessagesConversationFormatted & {
|
||||||
|
lastmessagepending?: boolean; // Calculated in the app. Whether last message is pending to be sent.
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { IonicPage } from 'ionic-angular';
|
import { IonicPage } from 'ionic-angular';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import { AddonMessagesProvider, AddonMessagesConversationMember, AddonMessagesMessageAreaContact } from '../../providers/messages';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
@ -38,21 +38,21 @@ export class AddonMessagesSearchPage implements OnDestroy {
|
||||||
contacts = {
|
contacts = {
|
||||||
type: 'contacts',
|
type: 'contacts',
|
||||||
titleString: 'addon.messages.contacts',
|
titleString: 'addon.messages.contacts',
|
||||||
results: [],
|
results: <AddonMessagesConversationMember[]> [],
|
||||||
canLoadMore: false,
|
canLoadMore: false,
|
||||||
loadingMore: false
|
loadingMore: false
|
||||||
};
|
};
|
||||||
nonContacts = {
|
nonContacts = {
|
||||||
type: 'noncontacts',
|
type: 'noncontacts',
|
||||||
titleString: 'addon.messages.noncontacts',
|
titleString: 'addon.messages.noncontacts',
|
||||||
results: [],
|
results: <AddonMessagesConversationMember[]> [],
|
||||||
canLoadMore: false,
|
canLoadMore: false,
|
||||||
loadingMore: false
|
loadingMore: false
|
||||||
};
|
};
|
||||||
messages = {
|
messages = {
|
||||||
type: 'messages',
|
type: 'messages',
|
||||||
titleString: 'addon.messages.messages',
|
titleString: 'addon.messages.messages',
|
||||||
results: [],
|
results: <AddonMessagesMessageAreaContact[]> [],
|
||||||
canLoadMore: false,
|
canLoadMore: false,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
loadMoreError: false
|
loadMoreError: false
|
||||||
|
@ -116,9 +116,9 @@ export class AddonMessagesSearchPage implements OnDestroy {
|
||||||
this.displaySearching = !loadMore;
|
this.displaySearching = !loadMore;
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let newContacts = [];
|
let newContacts: AddonMessagesConversationMember[] = [];
|
||||||
let newNonContacts = [];
|
let newNonContacts: AddonMessagesConversationMember[] = [];
|
||||||
let newMessages = [];
|
let newMessages: AddonMessagesMessageAreaContact[] = [];
|
||||||
let canLoadMoreContacts = false;
|
let canLoadMoreContacts = false;
|
||||||
let canLoadMoreNonContacts = false;
|
let canLoadMoreNonContacts = false;
|
||||||
let canLoadMoreMessages = false;
|
let canLoadMoreMessages = false;
|
||||||
|
|
|
@ -14,7 +14,10 @@
|
||||||
|
|
||||||
import { Component, OnDestroy } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
import { IonicPage } from 'ionic-angular';
|
import { IonicPage } from 'ionic-angular';
|
||||||
import { AddonMessagesProvider } from '../../providers/messages';
|
import {
|
||||||
|
AddonMessagesProvider, AddonMessagesMessagePreferences, AddonMessagesMessagePreferencesNotification,
|
||||||
|
AddonMessagesMessagePreferencesNotificationProcessor
|
||||||
|
} from '../../providers/messages';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreConfigProvider } from '@providers/config';
|
import { CoreConfigProvider } from '@providers/config';
|
||||||
|
@ -34,7 +37,7 @@ import { CoreConstants } from '@core/constants';
|
||||||
export class AddonMessagesSettingsPage implements OnDestroy {
|
export class AddonMessagesSettingsPage implements OnDestroy {
|
||||||
protected updateTimeout: any;
|
protected updateTimeout: any;
|
||||||
|
|
||||||
preferences: any;
|
preferences: AddonMessagesMessagePreferences;
|
||||||
preferencesLoaded: boolean;
|
preferencesLoaded: boolean;
|
||||||
contactablePrivacy: number | boolean;
|
contactablePrivacy: number | boolean;
|
||||||
advancedContactable = false; // Whether the site supports "advanced" contactable privacy.
|
advancedContactable = false; // Whether the site supports "advanced" contactable privacy.
|
||||||
|
@ -78,9 +81,9 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Fetches preference data.
|
* Fetches preference data.
|
||||||
*
|
*
|
||||||
* @return Resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchPreferences(): Promise<any> {
|
protected fetchPreferences(): Promise<void> {
|
||||||
return this.messagesProvider.getMessagePreferences().then((preferences) => {
|
return this.messagesProvider.getMessagePreferences().then((preferences) => {
|
||||||
if (this.groupMessagingEnabled) {
|
if (this.groupMessagingEnabled) {
|
||||||
// Simplify the preferences.
|
// Simplify the preferences.
|
||||||
|
@ -90,11 +93,12 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
||||||
return notification.preferencekey == AddonMessagesProvider.NOTIFICATION_PREFERENCES_KEY;
|
return notification.preferencekey == AddonMessagesProvider.NOTIFICATION_PREFERENCES_KEY;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const notification of component.notifications) {
|
component.notifications.forEach((notification) => {
|
||||||
for (const processor of notification.processors) {
|
notification.processors.forEach(
|
||||||
|
(processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => {
|
||||||
processor.checked = processor.loggedin.checked || processor.loggedoff.checked;
|
processor.checked = processor.loggedin.checked || processor.loggedoff.checked;
|
||||||
}
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,14 +172,16 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
||||||
* @param state State name, ['loggedin', 'loggedoff'].
|
* @param state State name, ['loggedin', 'loggedoff'].
|
||||||
* @param processor Notification processor.
|
* @param processor Notification processor.
|
||||||
*/
|
*/
|
||||||
changePreference(notification: any, state: string, processor: any): void {
|
changePreference(notification: AddonMessagesMessagePreferencesNotificationFormatted, state: string,
|
||||||
|
processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted): void {
|
||||||
|
|
||||||
if (this.groupMessagingEnabled) {
|
if (this.groupMessagingEnabled) {
|
||||||
// Update both states at the same time.
|
// Update both states at the same time.
|
||||||
const valueArray = [],
|
const valueArray = [],
|
||||||
promises = [];
|
promises = [];
|
||||||
let value = 'none';
|
let value = 'none';
|
||||||
|
|
||||||
notification.processors.forEach((processor) => {
|
notification.processors.forEach((processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => {
|
||||||
if (processor.checked) {
|
if (processor.checked) {
|
||||||
valueArray.push(processor.name);
|
valueArray.push(processor.name);
|
||||||
}
|
}
|
||||||
|
@ -268,3 +274,17 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message preferences notification with some caclulated data.
|
||||||
|
*/
|
||||||
|
type AddonMessagesMessagePreferencesNotificationFormatted = AddonMessagesMessagePreferencesNotification & {
|
||||||
|
updating?: boolean | {[state: string]: boolean}; // Calculated in the app. Whether the notification is being updated.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message preferences notification processor with some caclulated data.
|
||||||
|
*/
|
||||||
|
type AddonMessagesMessagePreferencesNotificationProcessorFormatted = AddonMessagesMessagePreferencesNotificationProcessor & {
|
||||||
|
checked?: boolean; // Calculated in the app. Whether the processor is checked either for loggedin or loggedoff.
|
||||||
|
};
|
||||||
|
|
|
@ -248,7 +248,7 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentUserId = site.getUserId(),
|
const currentUserId = site.getUserId(),
|
||||||
message = conv.messages[0]; // Treat only the last message, is the one we're interested.
|
message: any = conv.messages[0]; // Treat only the last message, is the one we're interested.
|
||||||
|
|
||||||
if (!message || message.useridfrom == currentUserId) {
|
if (!message || message.useridfrom == currentUserId) {
|
||||||
// No last message or not from current user. Return empty list.
|
// No last message or not from current user. Return empty list.
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
|
import { CoreEmulatorHelperProvider } from '@core/emulator/providers/helper';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to handle messages.
|
* Service to handle messages.
|
||||||
|
@ -89,9 +90,9 @@ export class AddonMessagesProvider {
|
||||||
*
|
*
|
||||||
* @param userId User ID of the person to block.
|
* @param userId User ID of the person to block.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
blockContact(userId: number, siteId?: string): Promise<any> {
|
blockContact(userId: number, siteId?: string): Promise<void> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
let promise;
|
let promise;
|
||||||
if (site.wsAvailable('core_message_block_user')) {
|
if (site.wsAvailable('core_message_block_user')) {
|
||||||
|
@ -313,7 +314,9 @@ export class AddonMessagesProvider {
|
||||||
* @param userId User ID viewing the conversation.
|
* @param userId User ID viewing the conversation.
|
||||||
* @return Formatted conversation.
|
* @return Formatted conversation.
|
||||||
*/
|
*/
|
||||||
protected formatConversation(conversation: any, userId: number): any {
|
protected formatConversation(conversation: AddonMessagesConversationFormatted, userId: number)
|
||||||
|
: AddonMessagesConversationFormatted {
|
||||||
|
|
||||||
const numMessages = conversation.messages.length,
|
const numMessages = conversation.messages.length,
|
||||||
lastMessage = numMessages ? conversation.messages[numMessages - 1] : null;
|
lastMessage = numMessages ? conversation.messages[numMessages - 1] : null;
|
||||||
|
|
||||||
|
@ -536,10 +539,10 @@ export class AddonMessagesProvider {
|
||||||
* Get all the contacts of the current user.
|
* Get all the contacts of the current user.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved with the WS data.
|
* @return Promise resolved with the WS data.
|
||||||
* @deprecated since Moodle 3.6
|
* @deprecated since Moodle 3.6
|
||||||
*/
|
*/
|
||||||
getAllContacts(siteId?: string): Promise<any> {
|
getAllContacts(siteId?: string): Promise<AddonMessagesGetContactsResult> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
return this.getContacts(siteId).then((contacts) => {
|
return this.getContacts(siteId).then((contacts) => {
|
||||||
|
@ -562,9 +565,9 @@ export class AddonMessagesProvider {
|
||||||
* Get all the users blocked by the current user.
|
* Get all the users blocked by the current user.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved with the WS data.
|
* @return Promise resolved with the WS data.
|
||||||
*/
|
*/
|
||||||
getBlockedContacts(siteId?: string): Promise<any> {
|
getBlockedContacts(siteId?: string): Promise<AddonMessagesGetBlockedUsersResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const userId = site.getUserId(),
|
const userId = site.getUserId(),
|
||||||
params = {
|
params = {
|
||||||
|
@ -585,19 +588,24 @@ export class AddonMessagesProvider {
|
||||||
* This excludes the blocked users.
|
* This excludes the blocked users.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved with the WS data.
|
* @return Promise resolved with the WS data.
|
||||||
* @deprecated since Moodle 3.6
|
* @deprecated since Moodle 3.6
|
||||||
*/
|
*/
|
||||||
getContacts(siteId?: string): Promise<any> {
|
getContacts(siteId?: string): Promise<AddonMessagesGetContactsResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const preSets = {
|
const preSets = {
|
||||||
cacheKey: this.getCacheKeyForContacts(),
|
cacheKey: this.getCacheKeyForContacts(),
|
||||||
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_contacts', undefined, preSets).then((contacts) => {
|
return site.read('core_message_get_contacts', undefined, preSets).then((contacts: AddonMessagesGetContactsResult) => {
|
||||||
// Filter contacts with negative ID, they are notifications.
|
// Filter contacts with negative ID, they are notifications.
|
||||||
const validContacts = {};
|
const validContacts: AddonMessagesGetContactsResult = {
|
||||||
|
online: [],
|
||||||
|
offline: [],
|
||||||
|
strangers: []
|
||||||
|
};
|
||||||
|
|
||||||
for (const typeName in contacts) {
|
for (const typeName in contacts) {
|
||||||
if (!validContacts[typeName]) {
|
if (!validContacts[typeName]) {
|
||||||
validContacts[typeName] = [];
|
validContacts[typeName] = [];
|
||||||
|
@ -621,11 +629,11 @@ export class AddonMessagesProvider {
|
||||||
* @param limitFrom Position of the first contact to fetch.
|
* @param limitFrom Position of the first contact to fetch.
|
||||||
* @param limitNum Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
* @param limitNum Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved with the list of user contacts.
|
* @return Promise resolved with the list of user contacts.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getUserContacts(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS , siteId?: string):
|
getUserContacts(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS , siteId?: string):
|
||||||
Promise<{contacts: any[], canLoadMore: boolean}> {
|
Promise<{contacts: AddonMessagesConversationMember[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -638,7 +646,9 @@ export class AddonMessagesProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_user_contacts', params, preSets).then((contacts) => {
|
return site.read('core_message_get_user_contacts', params, preSets)
|
||||||
|
.then((contacts: AddonMessagesConversationMember[]) => {
|
||||||
|
|
||||||
if (!contacts || !contacts.length) {
|
if (!contacts || !contacts.length) {
|
||||||
return { contacts: [], canLoadMore: false };
|
return { contacts: [], canLoadMore: false };
|
||||||
}
|
}
|
||||||
|
@ -663,11 +673,11 @@ export class AddonMessagesProvider {
|
||||||
* @param limitFrom Position of the first contact request to fetch.
|
* @param limitFrom Position of the first contact request to fetch.
|
||||||
* @param limitNum Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
* @param limitNum Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Resolved with the list of contact requests.
|
* @return Promise resolved with the list of contact requests.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getContactRequests(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, siteId?: string):
|
getContactRequests(limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_CONTACTS, siteId?: string):
|
||||||
Promise<{requests: any[], canLoadMore: boolean}> {
|
Promise<{requests: AddonMessagesConversationMember[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -680,7 +690,9 @@ export class AddonMessagesProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
updateFrequency: CoreSite.FREQUENCY_OFTEN
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_contact_requests', data, preSets).then((requests) => {
|
return site.read('core_message_get_contact_requests', data, preSets)
|
||||||
|
.then((requests: AddonMessagesConversationMember[]) => {
|
||||||
|
|
||||||
if (!requests || !requests.length) {
|
if (!requests || !requests.length) {
|
||||||
return { requests: [], canLoadMore: false };
|
return { requests: [], canLoadMore: false };
|
||||||
}
|
}
|
||||||
|
@ -716,7 +728,7 @@ export class AddonMessagesProvider {
|
||||||
typeExpected: 'number'
|
typeExpected: 'number'
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_received_contact_requests_count', data, preSets).then((count) => {
|
return site.read('core_message_get_received_contact_requests_count', data, preSets).then((count: number) => {
|
||||||
// Notify the new count so all badges are updated.
|
// Notify the new count so all badges are updated.
|
||||||
this.eventsProvider.trigger(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, { count }, site.id);
|
this.eventsProvider.trigger(AddonMessagesProvider.CONTACT_REQUESTS_COUNT_EVENT, { count }, site.id);
|
||||||
|
|
||||||
|
@ -745,7 +757,7 @@ export class AddonMessagesProvider {
|
||||||
*/
|
*/
|
||||||
getConversation(conversationId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
getConversation(conversationId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
||||||
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
||||||
newestFirst: boolean = true, siteId?: string, userId?: number): Promise<any> {
|
newestFirst: boolean = true, siteId?: string, userId?: number): Promise<AddonMessagesConversationFormatted> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -765,7 +777,7 @@ export class AddonMessagesProvider {
|
||||||
newestmessagesfirst: newestFirst ? 1 : 0
|
newestmessagesfirst: newestFirst ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_conversation', params, preSets).then((conversation) => {
|
return site.read('core_message_get_conversation', params, preSets).then((conversation: AddonMessagesConversation) => {
|
||||||
return this.formatConversation(conversation, userId);
|
return this.formatConversation(conversation, userId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -792,7 +804,8 @@ export class AddonMessagesProvider {
|
||||||
*/
|
*/
|
||||||
getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
||||||
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
messageOffset: number = 0, messageLimit: number = 1, memberOffset: number = 0, memberLimit: number = 2,
|
||||||
newestFirst: boolean = true, siteId?: string, userId?: number, preferCache?: boolean): Promise<any> {
|
newestFirst: boolean = true, siteId?: string, userId?: number, preferCache?: boolean)
|
||||||
|
: Promise<AddonMessagesConversationFormatted> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -813,7 +826,8 @@ export class AddonMessagesProvider {
|
||||||
newestmessagesfirst: newestFirst ? 1 : 0
|
newestmessagesfirst: newestFirst ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_conversation_between_users', params, preSets).then((conversation) => {
|
return site.read('core_message_get_conversation_between_users', params, preSets)
|
||||||
|
.then((conversation: AddonMessagesConversation) => {
|
||||||
return this.formatConversation(conversation, userId);
|
return this.formatConversation(conversation, userId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -826,12 +840,11 @@ export class AddonMessagesProvider {
|
||||||
* @param limitFrom Offset for members list.
|
* @param limitFrom Offset for members list.
|
||||||
* @param limitTo Limit of members.
|
* @param limitTo Limit of members.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @param userId User ID. If not defined, current user in the site.
|
* @param userId User ID. If not defined, current user in
|
||||||
* @return Promise resolved with the response.
|
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getConversationMembers(conversationId: number, limitFrom: number = 0, limitTo?: number, includeContactRequests?: boolean,
|
getConversationMembers(conversationId: number, limitFrom: number = 0, limitTo?: number, includeContactRequests?: boolean,
|
||||||
siteId?: string, userId?: number): Promise<any> {
|
siteId?: string, userId?: number): Promise<{members: AddonMessagesConversationMember[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -853,18 +866,21 @@ export class AddonMessagesProvider {
|
||||||
includeprivacyinfo: 1,
|
includeprivacyinfo: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_conversation_members', params, preSets).then((members) => {
|
return site.read('core_message_get_conversation_members', params, preSets)
|
||||||
const result: any = {};
|
.then((members: AddonMessagesConversationMember[]) => {
|
||||||
|
|
||||||
if (limitTo < 1) {
|
if (limitTo < 1) {
|
||||||
result.canLoadMore = false;
|
return {
|
||||||
result.members = members;
|
canLoadMore: false,
|
||||||
|
members: members
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
result.canLoadMore = members.length > limitTo;
|
return {
|
||||||
result.members = members.slice(0, limitTo);
|
canLoadMore: members.length > limitTo,
|
||||||
|
members: members.slice(0, limitTo)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -884,7 +900,8 @@ export class AddonMessagesProvider {
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getConversationMessages(conversationId: number, excludePending: boolean, limitFrom: number = 0, limitTo?: number,
|
getConversationMessages(conversationId: number, excludePending: boolean, limitFrom: number = 0, limitTo?: number,
|
||||||
newestFirst: boolean = true, timeFrom: number = 0, siteId?: string, userId?: number): Promise<any> {
|
newestFirst: boolean = true, timeFrom: number = 0, siteId?: string, userId?: number)
|
||||||
|
: Promise<AddonMessagesGetConversationMessagesResult> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -913,7 +930,9 @@ export class AddonMessagesProvider {
|
||||||
preSets['emergencyCache'] = false;
|
preSets['emergencyCache'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('core_message_get_conversation_messages', params, preSets).then((result) => {
|
return site.read('core_message_get_conversation_messages', params, preSets)
|
||||||
|
.then((result: AddonMessagesGetConversationMessagesResult) => {
|
||||||
|
|
||||||
if (limitTo < 1) {
|
if (limitTo < 1) {
|
||||||
result.canLoadMore = false;
|
result.canLoadMore = false;
|
||||||
result.messages = result.messages;
|
result.messages = result.messages;
|
||||||
|
@ -975,7 +994,8 @@ export class AddonMessagesProvider {
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getConversations(type?: number, favourites?: boolean, limitFrom: number = 0, siteId?: string, userId?: number,
|
getConversations(type?: number, favourites?: boolean, limitFrom: number = 0, siteId?: string, userId?: number,
|
||||||
forceCache?: boolean, ignoreCache?: boolean): Promise<{conversations: any[], canLoadMore: boolean}> {
|
forceCache?: boolean, ignoreCache?: boolean)
|
||||||
|
: Promise<{conversations: AddonMessagesConversationFormatted[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -1017,7 +1037,7 @@ export class AddonMessagesProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}).then((response) => {
|
}).then((response: AddonMessagesGetConversationsResult) => {
|
||||||
// Format the conversations, adding some calculated fields.
|
// Format the conversations, adding some calculated fields.
|
||||||
const conversations = response.conversations.slice(0, this.LIMIT_MESSAGES).map((conversation) => {
|
const conversations = response.conversations.slice(0, this.LIMIT_MESSAGES).map((conversation) => {
|
||||||
return this.formatConversation(conversation, userId);
|
return this.formatConversation(conversation, userId);
|
||||||
|
@ -1053,7 +1073,9 @@ export class AddonMessagesProvider {
|
||||||
cacheKey: this.getCacheKeyForConversationCounts()
|
cacheKey: this.getCacheKeyForConversationCounts()
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_conversation_counts', {}, preSets).then((result) => {
|
return site.read('core_message_get_conversation_counts', {}, preSets)
|
||||||
|
.then((result: AddonMessagesGetConversationCountsResult) => {
|
||||||
|
|
||||||
const counts = {
|
const counts = {
|
||||||
favourites: result.favourites,
|
favourites: result.favourites,
|
||||||
individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
|
individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
|
||||||
|
@ -1080,10 +1102,14 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved with messages and a boolean telling if can load more messages.
|
* @return Promise resolved with messages and a boolean telling if can load more messages.
|
||||||
*/
|
*/
|
||||||
getDiscussion(userId: number, excludePending: boolean, lfReceivedUnread: number = 0, lfReceivedRead: number = 0,
|
getDiscussion(userId: number, excludePending: boolean, lfReceivedUnread: number = 0, lfReceivedRead: number = 0,
|
||||||
lfSentUnread: number = 0, lfSentRead: number = 0, toDisplay: boolean = true, siteId?: string): Promise<any> {
|
lfSentUnread: number = 0, lfSentRead: number = 0, toDisplay: boolean = true, siteId?: string)
|
||||||
|
: Promise<{messages: AddonMessagesGetMessagesMessage[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const result = {},
|
const result = {
|
||||||
|
messages: <AddonMessagesGetMessagesMessage[]> [],
|
||||||
|
canLoadMore: false
|
||||||
|
},
|
||||||
preSets = {
|
preSets = {
|
||||||
cacheKey: this.getCacheKeyForDiscussion(userId)
|
cacheKey: this.getCacheKeyForDiscussion(userId)
|
||||||
},
|
},
|
||||||
|
@ -1107,7 +1133,7 @@ export class AddonMessagesProvider {
|
||||||
// Get message received by current user.
|
// Get message received by current user.
|
||||||
return this.getRecentMessages(params, preSets, lfReceivedUnread, lfReceivedRead, toDisplay, site.getId())
|
return this.getRecentMessages(params, preSets, lfReceivedUnread, lfReceivedRead, toDisplay, site.getId())
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
result['messages'] = response;
|
result.messages = response;
|
||||||
params.useridto = userId;
|
params.useridto = userId;
|
||||||
params.useridfrom = site.getUserId();
|
params.useridfrom = site.getUserId();
|
||||||
hasReceived = response.length > 0;
|
hasReceived = response.length > 0;
|
||||||
|
@ -1115,16 +1141,16 @@ export class AddonMessagesProvider {
|
||||||
// Get message sent by current user.
|
// Get message sent by current user.
|
||||||
return this.getRecentMessages(params, preSets, lfSentUnread, lfSentRead, toDisplay, siteId);
|
return this.getRecentMessages(params, preSets, lfSentUnread, lfSentRead, toDisplay, siteId);
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
result['messages'] = result['messages'].concat(response);
|
result.messages = result.messages.concat(response);
|
||||||
hasSent = response.length > 0;
|
hasSent = response.length > 0;
|
||||||
|
|
||||||
if (result['messages'].length > this.LIMIT_MESSAGES) {
|
if (result.messages.length > this.LIMIT_MESSAGES) {
|
||||||
// Sort messages and get the more recent ones.
|
// Sort messages and get the more recent ones.
|
||||||
result['canLoadMore'] = true;
|
result.canLoadMore = true;
|
||||||
result['messages'] = this.sortMessages(result['messages']);
|
result.messages = this.sortMessages(result['messages']);
|
||||||
result['messages'] = result['messages'].slice(-this.LIMIT_MESSAGES);
|
result.messages = result.messages.slice(-this.LIMIT_MESSAGES);
|
||||||
} else {
|
} else {
|
||||||
result['canLoadMore'] = result['messages'].length == this.LIMIT_MESSAGES && (!hasReceived || !hasSent);
|
result.canLoadMore = result.messages.length == this.LIMIT_MESSAGES && (!hasReceived || !hasSent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excludePending) {
|
if (excludePending) {
|
||||||
|
@ -1140,7 +1166,7 @@ export class AddonMessagesProvider {
|
||||||
message.text = message.smallmessage;
|
message.text = message.smallmessage;
|
||||||
});
|
});
|
||||||
|
|
||||||
result['messages'] = result['messages'].concat(offlineMessages);
|
result.messages = result.messages.concat(offlineMessages);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
@ -1153,11 +1179,11 @@ export class AddonMessagesProvider {
|
||||||
* If the site is 3.6 or higher, please use getConversations.
|
* If the site is 3.6 or higher, please use getConversations.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Resolved with an object where the keys are the user ID of the other user.
|
* @return Promise resolved with an object where the keys are the user ID of the other user.
|
||||||
*/
|
*/
|
||||||
getDiscussions(siteId?: string): Promise<any> {
|
getDiscussions(siteId?: string): Promise<{[userId: number]: AddonMessagesDiscussion}> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const discussions = {},
|
const discussions: {[userId: number]: AddonMessagesDiscussion} = {},
|
||||||
currentUserId = site.getUserId(),
|
currentUserId = site.getUserId(),
|
||||||
params = {
|
params = {
|
||||||
useridto: currentUserId,
|
useridto: currentUserId,
|
||||||
|
@ -1171,7 +1197,7 @@ export class AddonMessagesProvider {
|
||||||
/**
|
/**
|
||||||
* Convenience function to treat a recent message, adding it to discussions list if needed.
|
* Convenience function to treat a recent message, adding it to discussions list if needed.
|
||||||
*/
|
*/
|
||||||
const treatRecentMessage = (message: any, userId: number, userFullname: string): void => {
|
const treatRecentMessage = (message: AddonMessagesGetMessagesMessage, userId: number, userFullname: string): void => {
|
||||||
if (typeof discussions[userId] === 'undefined') {
|
if (typeof discussions[userId] === 'undefined') {
|
||||||
discussions[userId] = {
|
discussions[userId] = {
|
||||||
fullname: userFullname,
|
fullname: userFullname,
|
||||||
|
@ -1272,7 +1298,7 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved with the member info.
|
* @return Promise resolved with the member info.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
getMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise<any> {
|
getMemberInfo(otherUserId: number, siteId?: string, userId?: number): Promise<AddonMessagesConversationMember> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
@ -1287,7 +1313,9 @@ export class AddonMessagesProvider {
|
||||||
includeprivacyinfo: 1,
|
includeprivacyinfo: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_member_info', params, preSets).then((members) => {
|
return site.read('core_message_get_member_info', params, preSets)
|
||||||
|
.then((members: AddonMessagesConversationMember[]): any => {
|
||||||
|
|
||||||
if (!members || members.length < 1) {
|
if (!members || members.length < 1) {
|
||||||
// Should never happen.
|
// Should never happen.
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
|
@ -1313,7 +1341,7 @@ export class AddonMessagesProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @return Promise resolved with the message preferences.
|
* @return Promise resolved with the message preferences.
|
||||||
*/
|
*/
|
||||||
getMessagePreferences(siteId?: string): Promise<any> {
|
getMessagePreferences(siteId?: string): Promise<AddonMessagesMessagePreferences> {
|
||||||
this.logger.debug('Get message preferences');
|
this.logger.debug('Get message preferences');
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -1322,7 +1350,9 @@ export class AddonMessagesProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_user_message_preferences', {}, preSets).then((data) => {
|
return site.read('core_message_get_user_message_preferences', {}, preSets)
|
||||||
|
.then((data: AddonMessagesGetUserMessagePreferencesResult): any => {
|
||||||
|
|
||||||
if (data.preferences) {
|
if (data.preferences) {
|
||||||
data.preferences.blocknoncontacts = data.blocknoncontacts;
|
data.preferences.blocknoncontacts = data.blocknoncontacts;
|
||||||
|
|
||||||
|
@ -1341,15 +1371,18 @@ export class AddonMessagesProvider {
|
||||||
* @param preSets Set of presets for the WS.
|
* @param preSets Set of presets for the WS.
|
||||||
* @param toDisplay True if messages will be displayed to the user, either in view or in a notification.
|
* @param toDisplay True if messages will be displayed to the user, either in view or in a notification.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @return Promise resolved with the data.
|
||||||
*/
|
*/
|
||||||
protected getMessages(params: any, preSets: any, toDisplay: boolean = true, siteId?: string): Promise<any> {
|
protected getMessages(params: any, preSets: any, toDisplay: boolean = true, siteId?: string)
|
||||||
|
: Promise<AddonMessagesGetMessagesResult> {
|
||||||
|
|
||||||
params['type'] = 'conversations';
|
params['type'] = 'conversations';
|
||||||
params['newestfirst'] = 1;
|
params['newestfirst'] = 1;
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const userId = site.getUserId();
|
const userId = site.getUserId();
|
||||||
|
|
||||||
return site.read('core_message_get_messages', params, preSets).then((response) => {
|
return site.read('core_message_get_messages', params, preSets).then((response: AddonMessagesGetMessagesResult) => {
|
||||||
response.messages.forEach((message) => {
|
response.messages.forEach((message) => {
|
||||||
message.read = params.read == 0 ? 0 : 1;
|
message.read = params.read == 0 ? 0 : 1;
|
||||||
// Convert times to milliseconds.
|
// Convert times to milliseconds.
|
||||||
|
@ -1377,9 +1410,10 @@ export class AddonMessagesProvider {
|
||||||
* @param limitFromRead Number of unread messages already fetched, so fetch will be done from this number.
|
* @param limitFromRead Number of unread messages already fetched, so fetch will be done from this number.
|
||||||
* @param toDisplay True if messages will be displayed to the user, either in view or in a notification.
|
* @param toDisplay True if messages will be displayed to the user, either in view or in a notification.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @return Promise resolved with the data.
|
||||||
*/
|
*/
|
||||||
protected getRecentMessages(params: any, preSets: any, limitFromUnread: number = 0, limitFromRead: number = 0,
|
protected getRecentMessages(params: any, preSets: any, limitFromUnread: number = 0, limitFromRead: number = 0,
|
||||||
toDisplay: boolean = true, siteId?: string): Promise<any> {
|
toDisplay: boolean = true, siteId?: string): Promise<AddonMessagesGetMessagesMessage[]> {
|
||||||
limitFromUnread = limitFromUnread || 0;
|
limitFromUnread = limitFromUnread || 0;
|
||||||
limitFromRead = limitFromRead || 0;
|
limitFromRead = limitFromRead || 0;
|
||||||
|
|
||||||
|
@ -1427,7 +1461,7 @@ export class AddonMessagesProvider {
|
||||||
* @since 3.7
|
* @since 3.7
|
||||||
*/
|
*/
|
||||||
getSelfConversation(messageOffset: number = 0, messageLimit: number = 1, newestFirst: boolean = true, siteId?: string,
|
getSelfConversation(messageOffset: number = 0, messageLimit: number = 1, newestFirst: boolean = true, siteId?: string,
|
||||||
userId?: number): Promise<any> {
|
userId?: number): Promise<AddonMessagesConversationFormatted> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -1442,7 +1476,8 @@ export class AddonMessagesProvider {
|
||||||
newestmessagesfirst: newestFirst ? 1 : 0
|
newestmessagesfirst: newestFirst ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_get_self_conversation', params, preSets).then((conversation) => {
|
return site.read('core_message_get_self_conversation', params, preSets)
|
||||||
|
.then((conversation: AddonMessagesConversation) => {
|
||||||
return this.formatConversation(conversation, userId);
|
return this.formatConversation(conversation, userId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1466,7 +1501,8 @@ export class AddonMessagesProvider {
|
||||||
cacheKey: this.getCacheKeyForUnreadConversationCounts()
|
cacheKey: this.getCacheKeyForUnreadConversationCounts()
|
||||||
};
|
};
|
||||||
|
|
||||||
promise = site.read('core_message_get_unread_conversation_counts', {}, preSets).then((result) => {
|
promise = site.read('core_message_get_unread_conversation_counts', {}, preSets)
|
||||||
|
.then((result: AddonMessagesGetUnreadConversationCountsResult) => {
|
||||||
return {
|
return {
|
||||||
favourites: result.favourites,
|
favourites: result.favourites,
|
||||||
individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
|
individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
|
||||||
|
@ -1485,7 +1521,7 @@ export class AddonMessagesProvider {
|
||||||
typeExpected: 'number'
|
typeExpected: 'number'
|
||||||
};
|
};
|
||||||
|
|
||||||
promise = site.read('core_message_get_unread_conversations_count', params, preSets).then((count) => {
|
promise = site.read('core_message_get_unread_conversations_count', params, preSets).then((count: number) => {
|
||||||
return { favourites: 0, individual: count, group: 0, self: 0 };
|
return { favourites: 0, individual: count, group: 0, self: 0 };
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -1536,7 +1572,7 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved with the message unread count.
|
* @return Promise resolved with the message unread count.
|
||||||
*/
|
*/
|
||||||
getUnreadReceivedMessages(toDisplay: boolean = true, forceCache: boolean = false, ignoreCache: boolean = false,
|
getUnreadReceivedMessages(toDisplay: boolean = true, forceCache: boolean = false, ignoreCache: boolean = false,
|
||||||
siteId?: string): Promise<any> {
|
siteId?: string): Promise<AddonMessagesGetMessagesResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
read: 0,
|
read: 0,
|
||||||
|
@ -2049,7 +2085,7 @@ export class AddonMessagesProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with boolean marking success or not.
|
* @return Promise resolved with boolean marking success or not.
|
||||||
*/
|
*/
|
||||||
markMessageRead(messageId: number, siteId?: string): Promise<any> {
|
markMessageRead(messageId: number, siteId?: string): Promise<AddonMessagesMarkMessageReadResult> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
messageid: messageId,
|
messageid: messageId,
|
||||||
|
@ -2067,7 +2103,7 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved if success.
|
* @return Promise resolved if success.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
markAllConversationMessagesRead(conversationId?: number): Promise<any> {
|
markAllConversationMessagesRead(conversationId?: number): Promise<null> {
|
||||||
const params = {
|
const params = {
|
||||||
userid: this.sitesProvider.getCurrentSiteUserId(),
|
userid: this.sitesProvider.getCurrentSiteUserId(),
|
||||||
conversationid: conversationId
|
conversationid: conversationId
|
||||||
|
@ -2085,7 +2121,7 @@ export class AddonMessagesProvider {
|
||||||
* @param userIdFrom User Id for the sender.
|
* @param userIdFrom User Id for the sender.
|
||||||
* @return Promise resolved with boolean marking success or not.
|
* @return Promise resolved with boolean marking success or not.
|
||||||
*/
|
*/
|
||||||
markAllMessagesRead(userIdFrom?: number): Promise<any> {
|
markAllMessagesRead(userIdFrom?: number): Promise<boolean> {
|
||||||
const params = {
|
const params = {
|
||||||
useridto: this.sitesProvider.getCurrentSiteUserId(),
|
useridto: this.sitesProvider.getCurrentSiteUserId(),
|
||||||
useridfrom: userIdFrom
|
useridfrom: userIdFrom
|
||||||
|
@ -2217,8 +2253,9 @@ export class AddonMessagesProvider {
|
||||||
* @param query The query string.
|
* @param query The query string.
|
||||||
* @param limit The number of results to return, 0 for none.
|
* @param limit The number of results to return, 0 for none.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the contacts.
|
||||||
*/
|
*/
|
||||||
searchContacts(query: string, limit: number = 100, siteId?: string): Promise<any> {
|
searchContacts(query: string, limit: number = 100, siteId?: string): Promise<AddonMessagesSearchContactsContact[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
searchtext: query,
|
searchtext: query,
|
||||||
|
@ -2228,7 +2265,9 @@ export class AddonMessagesProvider {
|
||||||
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_search_contacts', data, preSets).then((contacts) => {
|
return site.read('core_message_search_contacts', data, preSets)
|
||||||
|
.then((contacts: AddonMessagesSearchContactsContact[]) => {
|
||||||
|
|
||||||
if (limit && contacts.length > limit) {
|
if (limit && contacts.length > limit) {
|
||||||
contacts = contacts.splice(0, limit);
|
contacts = contacts.splice(0, limit);
|
||||||
}
|
}
|
||||||
|
@ -2250,7 +2289,7 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved with the results.
|
* @return Promise resolved with the results.
|
||||||
*/
|
*/
|
||||||
searchMessages(query: string, userId?: number, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH,
|
searchMessages(query: string, userId?: number, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH,
|
||||||
siteId?: string): Promise<{messages: any[], canLoadMore: boolean}> {
|
siteId?: string): Promise<{messages: AddonMessagesMessageAreaContact[], canLoadMore: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -2263,13 +2302,15 @@ export class AddonMessagesProvider {
|
||||||
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_data_for_messagearea_search_messages', params, preSets).then((result) => {
|
return site.read('core_message_data_for_messagearea_search_messages', params, preSets)
|
||||||
|
.then((result: AddonMessagesDataForMessageAreaSearchMessagesResult) => {
|
||||||
|
|
||||||
if (!result.contacts || !result.contacts.length) {
|
if (!result.contacts || !result.contacts.length) {
|
||||||
return { messages: [], canLoadMore: false };
|
return { messages: [], canLoadMore: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
result.contacts.forEach((result) => {
|
result.contacts.forEach((contact) => {
|
||||||
result.id = result.userid;
|
contact.id = contact.userid;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.userProvider.storeUsers(result.contacts, site.id);
|
this.userProvider.storeUsers(result.contacts, site.id);
|
||||||
|
@ -2297,7 +2338,8 @@ export class AddonMessagesProvider {
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
searchUsers(query: string, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, siteId?: string):
|
searchUsers(query: string, limitFrom: number = 0, limitNum: number = AddonMessagesProvider.LIMIT_SEARCH, siteId?: string):
|
||||||
Promise<{contacts: any[], nonContacts: any[], canLoadMoreContacts: boolean, canLoadMoreNonContacts: boolean}> {
|
Promise<{contacts: AddonMessagesConversationMember[], nonContacts: AddonMessagesConversationMember[],
|
||||||
|
canLoadMoreContacts: boolean, canLoadMoreNonContacts: boolean}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -2310,7 +2352,7 @@ export class AddonMessagesProvider {
|
||||||
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
getFromCache: false // Always try to get updated data. If it fails, it will get it from cache.
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('core_message_message_search_users', data, preSets).then((result) => {
|
return site.read('core_message_message_search_users', data, preSets).then((result: AddonMessagesSearchUsersResult) => {
|
||||||
const contacts = result.contacts || [];
|
const contacts = result.contacts || [];
|
||||||
const nonContacts = result.noncontacts || [];
|
const nonContacts = result.noncontacts || [];
|
||||||
|
|
||||||
|
@ -2341,7 +2383,9 @@ export class AddonMessagesProvider {
|
||||||
* - sent (Boolean) True if message was sent to server, false if stored in device.
|
* - sent (Boolean) True if message was sent to server, false if stored in device.
|
||||||
* - message (Object) If sent=false, contains the stored message.
|
* - message (Object) If sent=false, contains the stored message.
|
||||||
*/
|
*/
|
||||||
sendMessage(toUserId: number, message: string, siteId?: string): Promise<any> {
|
sendMessage(toUserId: number, message: string, siteId?: string)
|
||||||
|
: Promise<{sent: boolean, message: AddonMessagesSendInstantMessagesMessage}> {
|
||||||
|
|
||||||
// Convenience function to store a message to be synchronized later.
|
// Convenience function to store a message to be synchronized later.
|
||||||
const storeOffline = (): Promise<any> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
return this.messagesOffline.saveMessage(toUserId, message, siteId).then((entry) => {
|
return this.messagesOffline.saveMessage(toUserId, message, siteId).then((entry) => {
|
||||||
|
@ -2395,7 +2439,7 @@ export class AddonMessagesProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved if success, rejected if failure.
|
* @return Promise resolved if success, rejected if failure.
|
||||||
*/
|
*/
|
||||||
sendMessageOnline(toUserId: number, message: string, siteId?: string): Promise<any> {
|
sendMessageOnline(toUserId: number, message: string, siteId?: string): Promise<AddonMessagesSendInstantMessagesMessage> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
|
@ -2430,7 +2474,7 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved if success, rejected if failure. Promise resolved doesn't mean that messages
|
* @return Promise resolved if success, rejected if failure. Promise resolved doesn't mean that messages
|
||||||
* have been sent, the resolve param can contain errors for messages not sent.
|
* have been sent, the resolve param can contain errors for messages not sent.
|
||||||
*/
|
*/
|
||||||
sendMessagesOnline(messages: any, siteId?: string): Promise<any> {
|
sendMessagesOnline(messages: any[], siteId?: string): Promise<AddonMessagesSendInstantMessagesMessage[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const data = {
|
const data = {
|
||||||
messages: messages
|
messages: messages
|
||||||
|
@ -2451,7 +2495,9 @@ export class AddonMessagesProvider {
|
||||||
* - message (any) If sent=false, contains the stored message.
|
* - message (any) If sent=false, contains the stored message.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
sendMessageToConversation(conversation: any, message: string, siteId?: string): Promise<any> {
|
sendMessageToConversation(conversation: any, message: string, siteId?: string)
|
||||||
|
: Promise<{sent: boolean, message: AddonMessagesSendMessagesToConversationMessage}> {
|
||||||
|
|
||||||
// Convenience function to store a message to be synchronized later.
|
// Convenience function to store a message to be synchronized later.
|
||||||
const storeOffline = (): Promise<any> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
return this.messagesOffline.saveConversationMessage(conversation, message, siteId).then((entry) => {
|
return this.messagesOffline.saveConversationMessage(conversation, message, siteId).then((entry) => {
|
||||||
|
@ -2506,7 +2552,8 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved if success, rejected if failure.
|
* @return Promise resolved if success, rejected if failure.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
sendMessageToConversationOnline(conversationId: number, message: string, siteId?: string): Promise<any> {
|
sendMessageToConversationOnline(conversationId: number, message: string, siteId?: string)
|
||||||
|
: Promise<AddonMessagesSendMessagesToConversationMessage> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
|
@ -2534,7 +2581,9 @@ export class AddonMessagesProvider {
|
||||||
* @return Promise resolved if success, rejected if failure.
|
* @return Promise resolved if success, rejected if failure.
|
||||||
* @since 3.6
|
* @since 3.6
|
||||||
*/
|
*/
|
||||||
sendMessagesToConversationOnline(conversationId: number, messages: any, siteId?: string): Promise<any> {
|
sendMessagesToConversationOnline(conversationId: number, messages: any[], siteId?: string)
|
||||||
|
: Promise<AddonMessagesSendMessagesToConversationMessage[]> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
conversationid: conversationId,
|
conversationid: conversationId,
|
||||||
|
@ -2603,10 +2652,10 @@ export class AddonMessagesProvider {
|
||||||
* @param conversations Array of conversations.
|
* @param conversations Array of conversations.
|
||||||
* @return Conversations sorted with most recent last.
|
* @return Conversations sorted with most recent last.
|
||||||
*/
|
*/
|
||||||
sortConversations(conversations: any[]): any[] {
|
sortConversations(conversations: AddonMessagesConversationFormatted[]): AddonMessagesConversationFormatted[] {
|
||||||
return conversations.sort((a, b) => {
|
return conversations.sort((a, b) => {
|
||||||
const timeA = parseInt(a.lastmessagedate, 10),
|
const timeA = Number(a.lastmessagedate),
|
||||||
timeB = parseInt(b.lastmessagedate, 10);
|
timeB = Number(b.lastmessagedate);
|
||||||
|
|
||||||
if (timeA == timeB && a.id) {
|
if (timeA == timeB && a.id) {
|
||||||
// Same time, sort by ID.
|
// Same time, sort by ID.
|
||||||
|
@ -2651,7 +2700,9 @@ export class AddonMessagesProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected storeLastReceivedMessageIfNeeded(convIdOrUserIdFrom: number, message: any, siteId?: string): Promise<any> {
|
protected storeLastReceivedMessageIfNeeded(convIdOrUserIdFrom: number,
|
||||||
|
message: AddonMessagesGetMessagesMessage | AddonMessagesConversationMessage, siteId?: string): Promise<any> {
|
||||||
|
|
||||||
const component = AddonMessagesProvider.PUSH_SIMULATION_COMPONENT;
|
const component = AddonMessagesProvider.PUSH_SIMULATION_COMPONENT;
|
||||||
|
|
||||||
// Get the last received message.
|
// Get the last received message.
|
||||||
|
@ -2675,7 +2726,7 @@ export class AddonMessagesProvider {
|
||||||
*
|
*
|
||||||
* @param contactTypes List of contacts grouped in types.
|
* @param contactTypes List of contacts grouped in types.
|
||||||
*/
|
*/
|
||||||
protected storeUsersFromAllContacts(contactTypes: any): void {
|
protected storeUsersFromAllContacts(contactTypes: AddonMessagesGetContactsResult): void {
|
||||||
for (const x in contactTypes) {
|
for (const x in contactTypes) {
|
||||||
this.userProvider.storeUsers(contactTypes[x]);
|
this.userProvider.storeUsers(contactTypes[x]);
|
||||||
}
|
}
|
||||||
|
@ -2735,3 +2786,377 @@ export class AddonMessagesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesConversation = {
|
||||||
|
id: number; // The conversation id.
|
||||||
|
name: string; // The conversation name, if set.
|
||||||
|
subname: string; // A subtitle for the conversation name, if set.
|
||||||
|
imageurl: string; // A link to the conversation picture, if set.
|
||||||
|
type: number; // The type of the conversation (1=individual,2=group,3=self).
|
||||||
|
membercount: number; // Total number of conversation members.
|
||||||
|
ismuted: boolean; // @since 3.7. If the user muted this conversation.
|
||||||
|
isfavourite: boolean; // If the user marked this conversation as a favourite.
|
||||||
|
isread: boolean; // If the user has read all messages in the conversation.
|
||||||
|
unreadcount: number; // The number of unread messages in this conversation.
|
||||||
|
members: AddonMessagesConversationMember[];
|
||||||
|
messages: AddonMessagesConversationMessage[];
|
||||||
|
candeletemessagesforallusers: boolean; // @since 3.7. If the user can delete messages in the conversation for all users.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation with some calculated data.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesConversationFormatted = AddonMessagesConversation & {
|
||||||
|
lastmessage?: string; // Calculated in the app. Last message.
|
||||||
|
lastmessagedate?: number; // Calculated in the app. Date the last message was sent.
|
||||||
|
sentfromcurrentuser?: boolean; // Calculated in the app. Whether last message was sent by the current user.
|
||||||
|
name?: string; // Calculated in the app. If private conversation, name of the other user.
|
||||||
|
userid?: number; // Calculated in the app. URL. If private conversation, ID of the other user.
|
||||||
|
showonlinestatus?: boolean; // Calculated in the app. If private conversation, whether to show online status of the other user.
|
||||||
|
isonline?: boolean; // Calculated in the app. If private conversation, whether the other user is online.
|
||||||
|
isblocked?: boolean; // Calculated in the app. If private conversation, whether the other user is blocked.
|
||||||
|
otherUser?: AddonMessagesConversationMember; // Calculated in the app. Other user in the conversation.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation member.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesConversationMember = {
|
||||||
|
id: number; // The user id.
|
||||||
|
fullname: string; // The user's name.
|
||||||
|
profileurl: string; // The link to the user's profile page.
|
||||||
|
profileimageurl: string; // User picture URL.
|
||||||
|
profileimageurlsmall: string; // Small user picture URL.
|
||||||
|
isonline: boolean; // The user's online status.
|
||||||
|
showonlinestatus: boolean; // Show the user's online status?.
|
||||||
|
isblocked: boolean; // If the user has been blocked.
|
||||||
|
iscontact: boolean; // Is the user a contact?.
|
||||||
|
isdeleted: boolean; // Is the user deleted?.
|
||||||
|
canmessageevenifblocked: boolean; // @since 3.8. If the user can still message even if they get blocked.
|
||||||
|
canmessage: boolean; // If the user can be messaged.
|
||||||
|
requirescontact: boolean; // If the user requires to be contacts.
|
||||||
|
contactrequests?: { // The contact requests.
|
||||||
|
id: number; // The id of the contact request.
|
||||||
|
userid: number; // The id of the user who created the contact request.
|
||||||
|
requesteduserid: number; // The id of the user confirming the request.
|
||||||
|
timecreated: number; // The timecreated timestamp for the contact request.
|
||||||
|
}[];
|
||||||
|
conversations?: { // Conversations between users.
|
||||||
|
id: number; // Conversations id.
|
||||||
|
type: number; // Conversation type: private or public.
|
||||||
|
name: string; // Multilang compatible conversation name2.
|
||||||
|
timecreated: number; // The timecreated timestamp for the conversation.
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conversation message.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesConversationMessage = {
|
||||||
|
id: number; // The id of the message.
|
||||||
|
useridfrom: number; // The id of the user who sent the message.
|
||||||
|
text: string; // The text of the message.
|
||||||
|
timecreated: number; // The timecreated timestamp for the message.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message preferences.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessagePreferences = {
|
||||||
|
userid: number; // User id.
|
||||||
|
disableall: number; // Whether all the preferences are disabled.
|
||||||
|
processors: { // Config form values.
|
||||||
|
displayname: string; // Display name.
|
||||||
|
name: string; // Processor name.
|
||||||
|
hassettings: boolean; // Whether has settings.
|
||||||
|
contextid: number; // Context id.
|
||||||
|
userconfigured: number; // Whether is configured by the user.
|
||||||
|
}[];
|
||||||
|
components: { // Available components.
|
||||||
|
displayname: string; // Display name.
|
||||||
|
notifications: AddonMessagesMessagePreferencesNotification[]; // List of notificaitons for the component.
|
||||||
|
}[];
|
||||||
|
} & AddonMessagesMessagePreferencesCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification processor in message preferences.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessagePreferencesNotification = {
|
||||||
|
displayname: string; // Display name.
|
||||||
|
preferencekey: string; // Preference key.
|
||||||
|
processors: AddonMessagesMessagePreferencesNotificationProcessor[]; // Processors values for this notification.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification processor in message preferences.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessagePreferencesNotificationProcessor = {
|
||||||
|
displayname: string; // Display name.
|
||||||
|
name: string; // Processor name.
|
||||||
|
locked: boolean; // Is locked by admin?.
|
||||||
|
lockedmessage?: string; // @since 3.6. Text to display if locked.
|
||||||
|
userconfigured: number; // Is configured?.
|
||||||
|
loggedin: {
|
||||||
|
name: string; // Name.
|
||||||
|
displayname: string; // Display name.
|
||||||
|
checked: boolean; // Is checked?.
|
||||||
|
};
|
||||||
|
loggedoff: {
|
||||||
|
name: string; // Name.
|
||||||
|
displayname: string; // Display name.
|
||||||
|
checked: boolean; // Is checked?.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message discussion (before 3.6).
|
||||||
|
*/
|
||||||
|
export type AddonMessagesDiscussion = {
|
||||||
|
fullname: string; // Full name of the other user in the discussion.
|
||||||
|
profileimageurl: string; // Profile image of the other user in the discussion.
|
||||||
|
message?: { // Last message.
|
||||||
|
id: number; // Message ID.
|
||||||
|
user: number; // User ID that sent the message.
|
||||||
|
message: string; // Text of the message.
|
||||||
|
timecreated: number; // Time the message was sent.
|
||||||
|
pending?: boolean; // Whether the message is pending to be sent.
|
||||||
|
};
|
||||||
|
unread?: boolean; // Whether the discussion has unread messages.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contact for message area.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessageAreaContact = {
|
||||||
|
userid: number; // The user's id.
|
||||||
|
fullname: string; // The user's name.
|
||||||
|
profileimageurl: string; // User picture URL.
|
||||||
|
profileimageurlsmall: string; // Small user picture URL.
|
||||||
|
ismessaging: boolean; // If we are messaging the user.
|
||||||
|
sentfromcurrentuser: boolean; // Was the last message sent from the current user?.
|
||||||
|
lastmessage: string; // The user's last message.
|
||||||
|
lastmessagedate: number; // @since 3.6. Timestamp for last message.
|
||||||
|
messageid: number; // The unique search message id.
|
||||||
|
showonlinestatus: boolean; // Show the user's online status?.
|
||||||
|
isonline: boolean; // The user's online status.
|
||||||
|
isread: boolean; // If the user has read the message.
|
||||||
|
isblocked: boolean; // If the user has been blocked.
|
||||||
|
unreadcount: number; // The number of unread messages in this conversation.
|
||||||
|
conversationid: number; // @since 3.6. The id of the conversation.
|
||||||
|
} & AddonMessagesMessageAreaContactCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_blocked_users.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetBlockedUsersResult = {
|
||||||
|
users: AddonMessagesBlockedUser[]; // List of blocked users.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data returned by core_message_get_blocked_users.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesBlockedUser = {
|
||||||
|
id: number; // User ID.
|
||||||
|
fullname: string; // User full name.
|
||||||
|
profileimageurl?: string; // User picture URL.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_contacts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetContactsResult = {
|
||||||
|
online: AddonMessagesGetContactsContact[]; // List of online contacts.
|
||||||
|
offline: AddonMessagesGetContactsContact[]; // List of offline contacts.
|
||||||
|
strangers: AddonMessagesGetContactsContact[]; // List of users that are not in the user's contact list but have sent a message.
|
||||||
|
} & AddonMessagesGetContactsCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data returned by core_message_get_contacts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetContactsContact = {
|
||||||
|
id: number; // User ID.
|
||||||
|
fullname: string; // User full name.
|
||||||
|
profileimageurl?: string; // User picture URL.
|
||||||
|
profileimageurlsmall?: string; // Small user picture URL.
|
||||||
|
unread: number; // Unread message count.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data returned by core_message_search_contacts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesSearchContactsContact = {
|
||||||
|
id: number; // User ID.
|
||||||
|
fullname: string; // User full name.
|
||||||
|
profileimageurl?: string; // User picture URL.
|
||||||
|
profileimageurlsmall?: string; // Small user picture URL.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_conversation_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetConversationMessagesResult = {
|
||||||
|
id: number; // The conversation id.
|
||||||
|
members: AddonMessagesConversationMember[];
|
||||||
|
messages: AddonMessagesConversationMessage[];
|
||||||
|
} & AddonMessagesGetConversationMessagesCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_conversations.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetConversationsResult = {
|
||||||
|
conversations: AddonMessagesConversation[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_conversation_counts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetConversationCountsResult = {
|
||||||
|
favourites: number; // Total number of favourite conversations.
|
||||||
|
types: {
|
||||||
|
1: number; // Total number of individual conversations.
|
||||||
|
2: number; // Total number of group conversations.
|
||||||
|
3: number; // @since 3.7. Total number of self conversations.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_unread_conversation_counts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetUnreadConversationCountsResult = {
|
||||||
|
favourites: number; // Total number of unread favourite conversations.
|
||||||
|
types: {
|
||||||
|
1: number; // Total number of unread individual conversations.
|
||||||
|
2: number; // Total number of unread group conversations.
|
||||||
|
3: number; // @since 3.7. Total number of unread self conversations.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_user_message_preferences.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetUserMessagePreferencesResult = {
|
||||||
|
preferences: AddonMessagesMessagePreferences;
|
||||||
|
blocknoncontacts: number; // Privacy messaging setting to define who can message you.
|
||||||
|
entertosend: boolean; // @since 3.6. User preference for using enter to send messages.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_get_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetMessagesResult = {
|
||||||
|
messages: AddonMessagesGetMessagesMessage[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message data returned by core_message_get_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetMessagesMessage = {
|
||||||
|
id: number; // Message id.
|
||||||
|
useridfrom: number; // User from id.
|
||||||
|
useridto: number; // User to id.
|
||||||
|
subject: string; // The message subject.
|
||||||
|
text: string; // The message text formated.
|
||||||
|
fullmessage: string; // The message.
|
||||||
|
fullmessageformat: number; // Fullmessage format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
fullmessagehtml: string; // The message in html.
|
||||||
|
smallmessage: string; // The shorten message.
|
||||||
|
notification: number; // Is a notification?.
|
||||||
|
contexturl: string; // Context URL.
|
||||||
|
contexturlname: string; // Context URL link name.
|
||||||
|
timecreated: number; // Time created.
|
||||||
|
timeread: number; // Time read.
|
||||||
|
usertofullname: string; // User to full name.
|
||||||
|
userfromfullname: string; // User from full name.
|
||||||
|
component?: string; // @since 3.7. The component that generated the notification.
|
||||||
|
eventtype?: string; // @since 3.7. The type of notification.
|
||||||
|
customdata?: string; // @since 3.7. Custom data to be passed to the message processor.
|
||||||
|
} & AddonMessagesGetMessagesMessageCalculatedData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_data_for_messagearea_search_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesDataForMessageAreaSearchMessagesResult = {
|
||||||
|
contacts: AddonMessagesMessageAreaContact[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_message_search_users.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesSearchUsersResult = {
|
||||||
|
contacts: AddonMessagesConversationMember[];
|
||||||
|
noncontacts: AddonMessagesConversationMember[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_mark_message_read.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMarkMessageReadResult = {
|
||||||
|
messageid: number; // The id of the message in the messages table.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_send_instant_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesSendInstantMessagesMessage = {
|
||||||
|
msgid: number; // Test this to know if it succeeds: id of the created message if it succeeded, -1 when failed.
|
||||||
|
clientmsgid?: string; // Your own id for the message.
|
||||||
|
errormessage?: string; // Error message - if it failed.
|
||||||
|
text?: string; // @since 3.6. The text of the message.
|
||||||
|
timecreated?: number; // @since 3.6. The timecreated timestamp for the message.
|
||||||
|
conversationid?: number; // @since 3.6. The conversation id for this message.
|
||||||
|
useridfrom?: number; // @since 3.6. The user id who sent the message.
|
||||||
|
candeletemessagesforallusers: boolean; // @since 3.7. If the user can delete messages in the conversation for all users.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS core_message_send_messages_to_conversation.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesSendMessagesToConversationMessage = {
|
||||||
|
id: number; // The id of the message.
|
||||||
|
useridfrom: number; // The id of the user who sent the message.
|
||||||
|
text: string; // The text of the message.
|
||||||
|
timecreated: number; // The timecreated timestamp for the message.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for core_message_get_contacts.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetContactsCalculatedData = {
|
||||||
|
blocked?: AddonMessagesBlockedUser[]; // Calculated in the app. List of blocked users.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for core_message_get_conversation_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetConversationMessagesCalculatedData = {
|
||||||
|
canLoadMore?: boolean; // Calculated in the app. Whether more messages can be loaded.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for message preferences.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessagePreferencesCalculatedData = {
|
||||||
|
blocknoncontacts?: number; // Calculated in the app. Based on the result of core_message_get_user_message_preferences.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for messages returned by core_message_get_messages.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesGetMessagesMessageCalculatedData = {
|
||||||
|
pending?: boolean; // Calculated in the app. Whether the message is pending to be sent.
|
||||||
|
read?: number; // Calculated in the app. Whether the message has been read.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for contact for message area.
|
||||||
|
*/
|
||||||
|
export type AddonMessagesMessageAreaContactCalculatedData = {
|
||||||
|
id?: number; // Calculated in the app. User ID.
|
||||||
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { AddonMessagesOfflineProvider } from './messages-offline';
|
import { AddonMessagesOfflineProvider } from './messages-offline';
|
||||||
import { AddonMessagesProvider } from './messages';
|
import { AddonMessagesProvider, AddonMessagesConversationFormatted } from './messages';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
@ -258,7 +258,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
||||||
// Get conversation name and add errors to warnings array.
|
// Get conversation name and add errors to warnings array.
|
||||||
return this.messagesProvider.getConversation(conversationId, false, false).catch(() => {
|
return this.messagesProvider.getConversation(conversationId, false, false).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
return {};
|
return <AddonMessagesConversationFormatted> {};
|
||||||
}).then((conversation) => {
|
}).then((conversation) => {
|
||||||
errors.forEach((error) => {
|
errors.forEach((error) => {
|
||||||
warnings.push(this.translate.instant('addon.messages.warningconversationmessagenotsent', {
|
warnings.push(this.translate.instant('addon.messages.warningconversationmessagenotsent', {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { Injector } from '@angular/core';
|
import { Injector } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { AddonModAssignFeedbackHandler } from '../providers/feedback-delegate';
|
import { AddonModAssignFeedbackHandler } from '../providers/feedback-delegate';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base handler for feedback plugins.
|
* Base handler for feedback plugins.
|
||||||
|
@ -48,7 +49,7 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +75,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The plugin name.
|
* @return The plugin name.
|
||||||
*/
|
*/
|
||||||
getPluginName(plugin: any): string {
|
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||||
// Check if there's a translated string for the plugin.
|
// Check if there's a translated string for the plugin.
|
||||||
const translationId = 'addon.mod_assign_feedback_' + plugin.type + '.pluginname',
|
const translationId = 'addon.mod_assign_feedback_' + plugin.type + '.pluginname',
|
||||||
translation = this.translate.instant(translationId);
|
translation = this.translate.instant(translationId);
|
||||||
|
@ -109,7 +111,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param inputData Data entered by the user for the feedback.
|
* @param inputData Data entered by the user for the feedback.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged(assign: any, submission: any, plugin: any, inputData: any): boolean | Promise<boolean> {
|
hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +147,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch(assign: any, submission: any, plugin: any, siteId?: string): Promise<any> {
|
prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +162,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareFeedbackData(assignId: number, userId: number, plugin: any, pluginData: any, siteId?: string): void | Promise<any> {
|
prepareFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
siteId?: string): void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +177,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
saveDraft(assignId: number, userId: number, plugin: any, data: any, siteId?: string): void | Promise<any> {
|
saveDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string)
|
||||||
|
: void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { Injector } from '@angular/core';
|
import { Injector } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { AddonModAssignSubmissionHandler } from '../providers/submission-delegate';
|
import { AddonModAssignSubmissionHandler } from '../providers/submission-delegate';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base handler for submission plugins.
|
* Base handler for submission plugins.
|
||||||
|
@ -38,7 +39,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canEditOffline(assign: any, submission: any, plugin: any): boolean | Promise<boolean> {
|
canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): boolean | Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +52,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
*/
|
*/
|
||||||
clearTmpData(assign: any, submission: any, plugin: any, inputData: any): void {
|
clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): void {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +68,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
copySubmissionData(assign: any, plugin: any, pluginData: any, userId?: number, siteId?: string): void | Promise<any> {
|
copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +83,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
deleteOfflineData(assign: any, submission: any, plugin: any, offlineData: any, siteId?: string): void | Promise<any> {
|
deleteOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +97,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any, edit?: boolean): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +111,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +122,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The plugin name.
|
* @return The plugin name.
|
||||||
*/
|
*/
|
||||||
getPluginName(plugin: any): string {
|
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||||
// Check if there's a translated string for the plugin.
|
// Check if there's a translated string for the plugin.
|
||||||
const translationId = 'addon.mod_assign_submission_' + plugin.type + '.pluginname',
|
const translationId = 'addon.mod_assign_submission_' + plugin.type + '.pluginname',
|
||||||
translation = this.translate.instant(translationId);
|
translation = this.translate.instant(translationId);
|
||||||
|
@ -139,7 +145,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForCopy(assign: any, plugin: any): number | Promise<number> {
|
getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise<number> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,10 +153,12 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* Get the size of data (in bytes) this plugin will send to add or edit a submission.
|
* Get the size of data (in bytes) this plugin will send to add or edit a submission.
|
||||||
*
|
*
|
||||||
* @param assign The assignment.
|
* @param assign The assignment.
|
||||||
|
* @param submission The submission.
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForEdit(assign: any, plugin: any): number | Promise<number> {
|
getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): number | Promise<number> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +171,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged(assign: any, submission: any, plugin: any, inputData: any): boolean | Promise<boolean> {
|
hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): boolean | Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +203,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch(assign: any, submission: any, plugin: any, siteId?: string): Promise<any> {
|
prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +221,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSubmissionData?(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean,
|
||||||
userId?: number, siteId?: string): void | Promise<any> {
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
@ -228,8 +239,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSyncData?(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
: void | Promise<any> {
|
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,15 @@
|
||||||
|
|
||||||
import { Input } from '@angular/core';
|
import { Input } from '@angular/core';
|
||||||
import { ModalController } from 'ionic-angular';
|
import { ModalController } from 'ionic-angular';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for component to render a feedback plugin.
|
* Base class for component to render a feedback plugin.
|
||||||
*/
|
*/
|
||||||
export class AddonModAssignFeedbackPluginComponentBase {
|
export class AddonModAssignFeedbackPluginComponentBase {
|
||||||
@Input() assign: any; // The assignment.
|
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||||
@Input() submission: any; // The submission.
|
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||||
@Input() plugin: any; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() userId: number; // The user ID of the submission.
|
@Input() userId: number; // The user ID of the submission.
|
||||||
@Input() configs: any; // The configs for the plugin.
|
@Input() configs: any; // The configs for the plugin.
|
||||||
@Input() canEdit: boolean; // Whether the user can edit.
|
@Input() canEdit: boolean; // Whether the user can edit.
|
||||||
|
|
|
@ -13,15 +13,16 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Input } from '@angular/core';
|
import { Input } from '@angular/core';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for component to render a submission plugin.
|
* Base class for component to render a submission plugin.
|
||||||
*/
|
*/
|
||||||
export class AddonModAssignSubmissionPluginComponent {
|
export class AddonModAssignSubmissionPluginComponent {
|
||||||
@Input() assign: any; // The assignment.
|
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||||
@Input() submission: any; // The submission.
|
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||||
@Input() plugin: any; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() configs: any; // The configs for the plugin.
|
@Input() configs: {[name: string]: string}; // The configs for the plugin.
|
||||||
@Input() edit: boolean; // Whether the user is editing.
|
@Input() edit: boolean; // Whether the user is editing.
|
||||||
@Input() allowOffline: boolean; // Whether to allow offline.
|
@Input() allowOffline: boolean; // Whether to allow offline.
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core';
|
import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||||
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
|
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
|
||||||
|
@ -28,9 +30,9 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
|
||||||
export class AddonModAssignFeedbackPluginComponent implements OnInit {
|
export class AddonModAssignFeedbackPluginComponent implements OnInit {
|
||||||
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
||||||
|
|
||||||
@Input() assign: any; // The assignment.
|
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||||
@Input() submission: any; // The submission.
|
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||||
@Input() plugin: any; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() userId: number; // The user ID of the submission.
|
@Input() userId: number; // The user ID of the submission.
|
||||||
@Input() canEdit: boolean | string; // Whether the user can edit.
|
@Input() canEdit: boolean | string; // Whether the user can edit.
|
||||||
@Input() edit: boolean | string; // Whether the user is editing.
|
@Input() edit: boolean | string; // Whether the user is editing.
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { Content, NavController } from 'ionic-angular';
|
||||||
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmissionGradingSummary } from '../../providers/assign';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
||||||
|
@ -36,13 +36,13 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
component = AddonModAssignProvider.COMPONENT;
|
component = AddonModAssignProvider.COMPONENT;
|
||||||
moduleName = 'assign';
|
moduleName = 'assign';
|
||||||
|
|
||||||
assign: any; // The assign object.
|
assign: AddonModAssignAssign; // The assign object.
|
||||||
canViewAllSubmissions: boolean; // Whether the user can view all submissions.
|
canViewAllSubmissions: boolean; // Whether the user can view all submissions.
|
||||||
canViewOwnSubmission: boolean; // Whether the user can view their own submission.
|
canViewOwnSubmission: boolean; // Whether the user can view their own submission.
|
||||||
timeRemaining: string; // Message about time remaining to submit.
|
timeRemaining: string; // Message about time remaining to submit.
|
||||||
lateSubmissions: string; // Message about late submissions.
|
lateSubmissions: string; // Message about late submissions.
|
||||||
showNumbers = true; // Whether to show number of submissions with each status.
|
showNumbers = true; // Whether to show number of submissions with each status.
|
||||||
summary: any; // The summary.
|
summary: AddonModAssignSubmissionGradingSummary; // The grading summary.
|
||||||
needsGradingAvalaible: boolean; // Whether we can see the submissions that need grading.
|
needsGradingAvalaible: boolean; // Whether we can see the submissions that need grading.
|
||||||
|
|
||||||
groupInfo: CoreGroupInfo = {
|
groupInfo: CoreGroupInfo = {
|
||||||
|
@ -153,7 +153,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
this.assign = assignData;
|
this.assign = assignData;
|
||||||
|
|
||||||
this.dataRetrieved.emit(this.assign);
|
this.dataRetrieved.emit(this.assign);
|
||||||
this.description = this.assign.intro || this.description;
|
this.description = this.assign.intro;
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
// Try to synchronize the assign.
|
// Try to synchronize the assign.
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core';
|
import { Component, Input, OnInit, Injector, ViewChild } from '@angular/core';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate';
|
||||||
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
|
import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component';
|
||||||
|
@ -28,9 +30,9 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
|
||||||
export class AddonModAssignSubmissionPluginComponent implements OnInit {
|
export class AddonModAssignSubmissionPluginComponent implements OnInit {
|
||||||
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
||||||
|
|
||||||
@Input() assign: any; // The assignment.
|
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||||
@Input() submission: any; // The submission.
|
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||||
@Input() plugin: any; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() edit: boolean | string; // Whether the user is editing.
|
@Input() edit: boolean | string; // Whether the user is editing.
|
||||||
@Input() allowOffline: boolean | string; // Whether to allow offline.
|
@Input() allowOffline: boolean | string; // Whether to allow offline.
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,10 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper';
|
import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper';
|
||||||
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmissionFeedback, AddonModAssignSubmission,
|
||||||
|
AddonModAssignSubmissionAttempt, AddonModAssignSubmissionPreviousAttempt, AddonModAssignPlugin
|
||||||
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||||
|
@ -55,11 +58,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
loaded: boolean; // Whether data has been loaded.
|
loaded: boolean; // Whether data has been loaded.
|
||||||
selectedTab: number; // Tab selected on start.
|
selectedTab: number; // Tab selected on start.
|
||||||
assign: any; // The assignment the submission belongs to.
|
assign: AddonModAssignAssign; // The assignment the submission belongs to.
|
||||||
userSubmission: any; // The submission object.
|
userSubmission: AddonModAssignSubmission; // The submission object.
|
||||||
isSubmittedForGrading: boolean; // Whether the submission has been submitted for grading.
|
isSubmittedForGrading: boolean; // Whether the submission has been submitted for grading.
|
||||||
submitModel: any = {}; // Model where to store the data to submit (for grading).
|
submitModel: any = {}; // Model where to store the data to submit (for grading).
|
||||||
feedback: any; // The feedback.
|
feedback: AddonModAssignSubmissionFeedbackFormatted; // The feedback.
|
||||||
hasOffline: boolean; // Whether there is offline data.
|
hasOffline: boolean; // Whether there is offline data.
|
||||||
submittedOffline: boolean; // Whether it was submitted in offline.
|
submittedOffline: boolean; // Whether it was submitted in offline.
|
||||||
fromDate: string; // Readable date when the assign started accepting submissions.
|
fromDate: string; // Readable date when the assign started accepting submissions.
|
||||||
|
@ -67,7 +70,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
maxAttemptsText: string; // The text for maximum attempts.
|
maxAttemptsText: string; // The text for maximum attempts.
|
||||||
blindMarking: boolean; // Whether blind marking is enabled.
|
blindMarking: boolean; // Whether blind marking is enabled.
|
||||||
user: any; // The user.
|
user: any; // The user.
|
||||||
lastAttempt: any; // The last attempt.
|
lastAttempt: AddonModAssignSubmissionAttemptFormatted; // The last attempt.
|
||||||
membersToSubmit: any[]; // Team members that need to submit the assignment.
|
membersToSubmit: any[]; // Team members that need to submit the assignment.
|
||||||
canSubmit: boolean; // Whether the user can submit for grading.
|
canSubmit: boolean; // Whether the user can submit for grading.
|
||||||
canEdit: boolean; // Whether the user can edit the submission.
|
canEdit: boolean; // Whether the user can edit the submission.
|
||||||
|
@ -77,7 +80,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
gradingStatusTranslationId: string; // Key of the text to display for the grading status.
|
gradingStatusTranslationId: string; // Key of the text to display for the grading status.
|
||||||
gradingColor: string; // Color to apply to the grading status.
|
gradingColor: string; // Color to apply to the grading status.
|
||||||
workflowStatusTranslationId: string; // Key of the text to display for the workflow status.
|
workflowStatusTranslationId: string; // Key of the text to display for the workflow status.
|
||||||
submissionPlugins: string[]; // List of submission plugins names.
|
submissionPlugins: AddonModAssignPlugin[]; // List of submission plugins.
|
||||||
timeRemaining: string; // Message about time remaining.
|
timeRemaining: string; // Message about time remaining.
|
||||||
timeRemainingClass: string; // Class to apply to time remaining message.
|
timeRemainingClass: string; // Class to apply to time remaining message.
|
||||||
statusTranslated: string; // Status.
|
statusTranslated: string; // Status.
|
||||||
|
@ -99,7 +102,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
protected siteId: string; // Current site ID.
|
protected siteId: string; // Current site ID.
|
||||||
protected currentUserId: number; // Current user ID.
|
protected currentUserId: number; // Current user ID.
|
||||||
protected previousAttempt: any; // The previous attempt.
|
protected previousAttempt: AddonModAssignSubmissionPreviousAttempt; // The previous attempt.
|
||||||
protected submissionStatusAvailable: boolean; // Whether we were able to retrieve the submission status.
|
protected submissionStatusAvailable: boolean; // Whether we were able to retrieve the submission status.
|
||||||
protected originalGrades: any = {}; // Object with the original grade data, to check for changes.
|
protected originalGrades: any = {}; // Object with the original grade data, to check for changes.
|
||||||
protected isDestroyed: boolean; // Whether the component has been destroyed.
|
protected isDestroyed: boolean; // Whether the component has been destroyed.
|
||||||
|
@ -209,7 +212,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
return this.goToEdit();
|
return this.goToEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, this.previousAttempt);
|
const previousSubmission = this.previousAttempt.submission;
|
||||||
let modal = this.domUtils.showModalLoading();
|
let modal = this.domUtils.showModalLoading();
|
||||||
|
|
||||||
this.assignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission).catch(() => {
|
this.assignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission).catch(() => {
|
||||||
|
@ -303,7 +306,8 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.feedback && this.feedback.plugins) {
|
if (this.feedback && this.feedback.plugins) {
|
||||||
return this.assignHelper.hasFeedbackDataChanged(this.assign, this.submitId, this.feedback).catch(() => {
|
return this.assignHelper.hasFeedbackDataChanged(this.assign, this.userSubmission, this.feedback, this.submitId)
|
||||||
|
.catch(() => {
|
||||||
// Error ocurred, consider there are no changes.
|
// Error ocurred, consider there are no changes.
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -438,7 +442,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
// Check if there's any unsupported plugin for editing.
|
// Check if there's any unsupported plugin for editing.
|
||||||
if (!this.userSubmission || !this.userSubmission.plugins) {
|
if (!this.userSubmission || !this.userSubmission.plugins) {
|
||||||
// Submission not created yet, we have to use assign configs to detect the plugins used.
|
// Submission not created yet, we have to use assign configs to detect the plugins used.
|
||||||
this.userSubmission = {};
|
this.userSubmission = this.assignHelper.createEmptySubmission();
|
||||||
this.userSubmission.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignsubmission');
|
this.userSubmission.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignsubmission');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +465,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
* @param feedback The feedback data from the submission status.
|
* @param feedback The feedback data from the submission status.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected loadFeedback(feedback: any): Promise<any> {
|
protected loadFeedback(feedback: AddonModAssignSubmissionFeedback): Promise<any> {
|
||||||
this.grade = {
|
this.grade = {
|
||||||
method: false,
|
method: false,
|
||||||
grade: false,
|
grade: false,
|
||||||
|
@ -571,7 +575,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
if (!this.feedback || !this.feedback.plugins) {
|
if (!this.feedback || !this.feedback.plugins) {
|
||||||
// Feedback plugins not present, we have to use assign configs to detect the plugins used.
|
// Feedback plugins not present, we have to use assign configs to detect the plugins used.
|
||||||
this.feedback = {};
|
this.feedback = this.assignHelper.createEmptyFeedback();
|
||||||
this.feedback.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignfeedback');
|
this.feedback.plugins = this.assignHelper.getPluginsEnabled(this.assign, 'assignfeedback');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,7 +889,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
// Show error if submission statement should be shown but it couldn't be retrieved.
|
// Show error if submission statement should be shown but it couldn't be retrieved.
|
||||||
this.showErrorStatementEdit = submissionStatementMissing && !this.assign.submissiondrafts &&
|
this.showErrorStatementEdit = submissionStatementMissing && !this.assign.submissiondrafts &&
|
||||||
this.submitId == this.currentUserId;
|
this.submitId == this.currentUserId;
|
||||||
this.showErrorStatementSubmit = submissionStatementMissing && this.assign.submissiondrafts;
|
this.showErrorStatementSubmit = submissionStatementMissing && !!this.assign.submissiondrafts;
|
||||||
|
|
||||||
this.userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt);
|
this.userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt);
|
||||||
|
|
||||||
|
@ -954,3 +958,17 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submission attempt with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonModAssignSubmissionAttemptFormatted = AddonModAssignSubmissionAttempt & {
|
||||||
|
submissiongroupname?: string; // Calculated in the app. Group name the attempt belongs to.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback of an assign submission with some calculated data.
|
||||||
|
*/
|
||||||
|
type AddonModAssignSubmissionFeedbackFormatted = AddonModAssignSubmissionFeedback & {
|
||||||
|
advancedgrade?: boolean; // Calculated in the app. Whether it uses advanced grading.
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
||||||
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
||||||
import { AddonModAssignFeedbackCommentsComponent } from '../component/comments';
|
import { AddonModAssignFeedbackCommentsComponent } from '../component/comments';
|
||||||
|
@ -50,14 +52,14 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Component to use to display the plugin data, either in read or in edit mode.
|
* Return the Component to use to display the plugin data.
|
||||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
*
|
*
|
||||||
* @param injector Injector.
|
* @param injector Injector.
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise<any> {
|
||||||
return AddonModAssignFeedbackCommentsComponent;
|
return AddonModAssignFeedbackCommentsComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +103,8 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +138,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
* @param userId User ID of the submission.
|
* @param userId User ID of the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged(assign: any, submission: any, plugin: any, inputData: any, userId: number): boolean | Promise<boolean> {
|
hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise<boolean> {
|
||||||
|
|
||||||
// Get it from plugin or offline.
|
// Get it from plugin or offline.
|
||||||
return this.assignOfflineProvider.getSubmissionGrade(assign.id, userId).catch(() => {
|
return this.assignOfflineProvider.getSubmissionGrade(assign.id, userId).catch(() => {
|
||||||
// No offline data found.
|
// No offline data found.
|
||||||
|
@ -191,7 +196,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareFeedbackData(assignId: number, userId: number, plugin: any, pluginData: any, siteId?: string): void | Promise<any> {
|
prepareFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
const draft = this.getDraft(assignId, userId, siteId);
|
const draft = this.getDraft(assignId, userId, siteId);
|
||||||
|
|
||||||
if (draft) {
|
if (draft) {
|
||||||
|
@ -212,7 +219,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
saveDraft(assignId: number, userId: number, plugin: any, data: any, siteId?: string): void | Promise<any> {
|
saveDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string)
|
||||||
|
: void | Promise<any> {
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
this.drafts[this.getDraftId(assignId, userId, siteId)] = data;
|
this.drafts[this.getDraftId(assignId, userId, siteId)] = data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
||||||
import { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf';
|
import { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf';
|
||||||
|
|
||||||
|
@ -29,14 +31,14 @@ export class AddonModAssignFeedbackEditPdfHandler implements AddonModAssignFeedb
|
||||||
constructor(private assignProvider: AddonModAssignProvider) { }
|
constructor(private assignProvider: AddonModAssignProvider) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Component to use to display the plugin data, either in read or in edit mode.
|
* Return the Component to use to display the plugin data.
|
||||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
*
|
*
|
||||||
* @param injector Injector.
|
* @param injector Injector.
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise<any> {
|
||||||
return AddonModAssignFeedbackEditPdfComponent;
|
return AddonModAssignFeedbackEditPdfComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +52,8 @@ export class AddonModAssignFeedbackEditPdfHandler implements AddonModAssignFeedb
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
||||||
import { AddonModAssignFeedbackFileComponent } from '../component/file';
|
import { AddonModAssignFeedbackFileComponent } from '../component/file';
|
||||||
|
|
||||||
|
@ -29,14 +31,14 @@ export class AddonModAssignFeedbackFileHandler implements AddonModAssignFeedback
|
||||||
constructor(private assignProvider: AddonModAssignProvider) { }
|
constructor(private assignProvider: AddonModAssignProvider) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Component to use to display the plugin data, either in read or in edit mode.
|
* Return the Component to use to display the plugin data.
|
||||||
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
*
|
*
|
||||||
* @param injector Injector.
|
* @param injector Injector.
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin): any | Promise<any> {
|
||||||
return AddonModAssignFeedbackFileComponent;
|
return AddonModAssignFeedbackFileComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +52,8 @@ export class AddonModAssignFeedbackFileHandler implements AddonModAssignFeedback
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||||
|
import {
|
||||||
|
AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modal that allows editing a feedback plugin.
|
* Modal that allows editing a feedback plugin.
|
||||||
|
@ -28,9 +31,9 @@ import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegat
|
||||||
})
|
})
|
||||||
export class AddonModAssignEditFeedbackModalPage {
|
export class AddonModAssignEditFeedbackModalPage {
|
||||||
|
|
||||||
@Input() assign: any; // The assignment.
|
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||||
@Input() submission: any; // The submission.
|
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||||
@Input() plugin: any; // The plugin object.
|
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||||
@Input() userId: number; // The user ID of the submission.
|
@Input() userId: number; // The user ID of the submission.
|
||||||
|
|
||||||
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
||||||
|
@ -99,8 +102,8 @@ export class AddonModAssignEditFeedbackModalPage {
|
||||||
* @return Promise resolved with boolean: whether the data has changed.
|
* @return Promise resolved with boolean: whether the data has changed.
|
||||||
*/
|
*/
|
||||||
protected hasDataChanged(): Promise<boolean> {
|
protected hasDataChanged(): Promise<boolean> {
|
||||||
return this.feedbackDelegate.hasPluginDataChanged(this.assign, this.userId, this.plugin, this.getInputData(), this.userId)
|
return this.feedbackDelegate.hasPluginDataChanged(this.assign, this.submission, this.plugin, this.getInputData(),
|
||||||
.catch(() => {
|
this.userId).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreSyncProvider } from '@providers/sync';
|
import { CoreSyncProvider } from '@providers/sync';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper';
|
import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission } from '../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
|
@ -35,9 +35,9 @@ import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
})
|
})
|
||||||
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
title: string; // Title to display.
|
title: string; // Title to display.
|
||||||
assign: any; // Assignment.
|
assign: AddonModAssignAssign; // Assignment.
|
||||||
courseId: number; // Course ID the assignment belongs to.
|
courseId: number; // Course ID the assignment belongs to.
|
||||||
userSubmission: any; // The user submission.
|
userSubmission: AddonModAssignSubmission; // The user submission.
|
||||||
allowOffline: boolean; // Whether offline is allowed.
|
allowOffline: boolean; // Whether offline is allowed.
|
||||||
submissionStatement: string; // The submission statement.
|
submissionStatement: string; // The submission statement.
|
||||||
submissionStatementAccepted: boolean; // Whether submission statement is accepted.
|
submissionStatementAccepted: boolean; // Whether submission statement is accepted.
|
||||||
|
@ -129,7 +129,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt);
|
const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt);
|
||||||
|
|
||||||
// Check if the user can edit it in offline.
|
// Check if the user can edit it in offline.
|
||||||
return this.assignHelper.canEditSubmissionOffline(this.assign, userSubmission).then((canEditOffline) => {
|
return this.assignHelper.canEditSubmissionOffline(this.assign, userSubmission).then((canEditOffline): any => {
|
||||||
if (canEditOffline) {
|
if (canEditOffline) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||||
} else {
|
} else {
|
||||||
// Try to send it to server.
|
// Try to send it to server.
|
||||||
promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
||||||
this.userSubmission.timemodified, this.assign.submissiondrafts, this.userId);
|
this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
return promise.then(() => {
|
||||||
|
|
|
@ -19,9 +19,11 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignGrade, AddonModAssignSubmission
|
||||||
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from '../../providers/helper';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +38,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
|
||||||
title: string; // Title to display.
|
title: string; // Title to display.
|
||||||
assign: any; // Assignment.
|
assign: AddonModAssignAssign; // Assignment.
|
||||||
submissions: any[]; // List of submissions
|
submissions: any[]; // List of submissions
|
||||||
loaded: boolean; // Whether data has been loaded.
|
loaded: boolean; // Whether data has been loaded.
|
||||||
haveAllParticipants: boolean; // Whether all participants have been loaded.
|
haveAllParticipants: boolean; // Whether all participants have been loaded.
|
||||||
|
@ -53,7 +55,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
protected courseId: number; // Course ID the assignment belongs to.
|
protected courseId: number; // Course ID the assignment belongs to.
|
||||||
protected selectedStatus: string; // The status to see.
|
protected selectedStatus: string; // The status to see.
|
||||||
protected gradedObserver; // Observer to refresh data when a grade changes.
|
protected gradedObserver; // Observer to refresh data when a grade changes.
|
||||||
protected submissionsData: any;
|
protected submissionsData: {canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]};
|
||||||
|
|
||||||
constructor(navParams: NavParams, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
constructor(navParams: NavParams, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
||||||
protected domUtils: CoreDomUtilsProvider, protected translate: TranslateService,
|
protected domUtils: CoreDomUtilsProvider, protected translate: TranslateService,
|
||||||
|
@ -161,14 +163,14 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
!this.assign.markingworkflow ? this.assignProvider.getAssignmentGrades(this.assign.id) : Promise.resolve(null),
|
!this.assign.markingworkflow ? this.assignProvider.getAssignmentGrades(this.assign.id) : Promise.resolve(null),
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise.all(promises).then(([submissions, grades]) => {
|
return Promise.all(promises).then(([submissions, grades]: [AddonModAssignSubmissionFormatted[], AddonModAssignGrade[]]) => {
|
||||||
// Filter the submissions to get only the ones with the right status and add some extra data.
|
// Filter the submissions to get only the ones with the right status and add some extra data.
|
||||||
const getNeedGrading = this.selectedStatus == AddonModAssignProvider.NEED_GRADING,
|
const getNeedGrading = this.selectedStatus == AddonModAssignProvider.NEED_GRADING,
|
||||||
searchStatus = getNeedGrading ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : this.selectedStatus,
|
searchStatus = getNeedGrading ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : this.selectedStatus,
|
||||||
promises = [],
|
promises = [],
|
||||||
showSubmissions = [];
|
showSubmissions = [];
|
||||||
|
|
||||||
submissions.forEach((submission) => {
|
submissions.forEach((submission: AddonModAssignSubmissionForList) => {
|
||||||
if (!searchStatus || searchStatus == submission.status) {
|
if (!searchStatus || searchStatus == submission.status) {
|
||||||
promises.push(this.assignOfflineProvider.getSubmissionGrade(this.assign.id, submission.userid).catch(() => {
|
promises.push(this.assignOfflineProvider.getSubmissionGrade(this.assign.id, submission.userid).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
@ -213,7 +215,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
submission.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' +
|
submission.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' +
|
||||||
submission.status);
|
submission.status);
|
||||||
} else {
|
} else {
|
||||||
submission.statusTranslated = false;
|
submission.statusTranslated = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notSynced) {
|
if (notSynced) {
|
||||||
|
@ -224,7 +226,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
submission.gradingStatusTranslationId =
|
submission.gradingStatusTranslationId =
|
||||||
this.assignProvider.getSubmissionGradingStatusTranslationId(submission.gradingstatus);
|
this.assignProvider.getSubmissionGradingStatusTranslationId(submission.gradingstatus);
|
||||||
} else {
|
} else {
|
||||||
submission.gradingStatusTranslationId = false;
|
submission.gradingStatusTranslationId = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
showSubmissions.push(submission);
|
showSubmissions.push(submission);
|
||||||
|
@ -299,3 +301,13 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
this.gradedObserver && this.gradedObserver.off();
|
this.gradedObserver && this.gradedObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculated data for an assign submission.
|
||||||
|
*/
|
||||||
|
type AddonModAssignSubmissionForList = AddonModAssignSubmissionFormatted & {
|
||||||
|
statusColor?: string; // Calculated in the app. Color of the submission status.
|
||||||
|
gradingColor?: string; // Calculated in the app. Color of the submission grading status.
|
||||||
|
statusTranslated?: string; // Calculated in the app. Translated text of the submission status.
|
||||||
|
gradingStatusTranslationId?: string; // Calculated in the app. Key of the text of the submission grading status.
|
||||||
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { AddonModAssignProvider } from '../../providers/assign';
|
import { AddonModAssignProvider, AddonModAssignAssign } from '../../providers/assign';
|
||||||
import { AddonModAssignSubmissionComponent } from '../../components/submission/submission';
|
import { AddonModAssignSubmissionComponent } from '../../components/submission/submission';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit {
|
||||||
loaded: boolean; // Whether data has been loaded.
|
loaded: boolean; // Whether data has been loaded.
|
||||||
canSaveGrades: boolean; // Whether the user can save grades.
|
canSaveGrades: boolean; // Whether the user can save grades.
|
||||||
|
|
||||||
protected assign: any; // The assignment the submission belongs to.
|
protected assign: AddonModAssignAssign; // The assignment the submission belongs to.
|
||||||
protected blindMarking: boolean; // Whether it uses blind marking.
|
protected blindMarking: boolean; // Whether it uses blind marking.
|
||||||
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
protected forceLeave = false; // To allow leaving the page without checking for changes.
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||||
import { AddonModAssignProvider } from './assign';
|
import { AddonModAssignProvider, AddonModAssignAssign } from './assign';
|
||||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||||
|
|
||||||
|
@ -169,14 +169,14 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
syncAssign(assignId: number, siteId?: string): Promise<AddonModAssignSyncResult> {
|
syncAssign(assignId: number, siteId?: string): Promise<AddonModAssignSyncResult> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
const promises = [],
|
const promises: Promise<any>[] = [],
|
||||||
result: AddonModAssignSyncResult = {
|
result: AddonModAssignSyncResult = {
|
||||||
warnings: [],
|
warnings: [],
|
||||||
updated: false
|
updated: false
|
||||||
};
|
};
|
||||||
let assign,
|
let assign: AddonModAssignAssign,
|
||||||
courseId,
|
courseId: number,
|
||||||
syncPromise;
|
syncPromise: Promise<any>;
|
||||||
|
|
||||||
if (this.isSyncing(assignId, siteId)) {
|
if (this.isSyncing(assignId, siteId)) {
|
||||||
// There's already a sync ongoing for this assign, return the promise.
|
// There's already a sync ongoing for this assign, return the promise.
|
||||||
|
@ -269,7 +269,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved if success, rejected otherwise.
|
* @return Promise resolved if success, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
protected syncSubmission(assign: any, offlineData: any, warnings: string[], siteId?: string): Promise<any> {
|
protected syncSubmission(assign: AddonModAssignAssign, offlineData: any, warnings: string[], siteId?: string): Promise<any> {
|
||||||
const userId = offlineData.userid,
|
const userId = offlineData.userid,
|
||||||
pluginData = {};
|
pluginData = {};
|
||||||
let discardError,
|
let discardError,
|
||||||
|
@ -358,8 +358,8 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved if success, rejected otherwise.
|
* @return Promise resolved if success, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
protected syncSubmissionGrade(assign: any, offlineData: any, warnings: string[], courseId: number, siteId?: string)
|
protected syncSubmissionGrade(assign: AddonModAssignAssign, offlineData: any, warnings: string[], courseId: number,
|
||||||
: Promise<any> {
|
siteId?: string): Promise<any> {
|
||||||
|
|
||||||
const userId = offlineData.userid;
|
const userId = offlineData.userid;
|
||||||
let discardError;
|
let discardError;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreInterceptor } from '@classes/interceptor';
|
import { CoreInterceptor } from '@classes/interceptor';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some functions for assign.
|
* Service that provides some functions for assign.
|
||||||
|
@ -123,7 +124,7 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the assignment.
|
* @return Promise resolved with the assignment.
|
||||||
*/
|
*/
|
||||||
getAssignment(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getAssignment(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignAssign> {
|
||||||
return this.getAssignmentByField(courseId, 'cmid', cmId, ignoreCache, siteId);
|
return this.getAssignmentByField(courseId, 'cmid', cmId, ignoreCache, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise resolved when the assignment is retrieved.
|
* @return Promise resolved when the assignment is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getAssignmentByField(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string)
|
protected getAssignmentByField(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string)
|
||||||
: Promise<any> {
|
: Promise<AddonModAssignAssign> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -161,7 +162,7 @@ export class AddonModAssignProvider {
|
||||||
delete params.includenotenrolledcourses;
|
delete params.includenotenrolledcourses;
|
||||||
|
|
||||||
return site.read('mod_assign_get_assignments', params, preSets);
|
return site.read('mod_assign_get_assignments', params, preSets);
|
||||||
}).then((response) => {
|
}).then((response: AddonModAssignGetAssignmentsResult): any => {
|
||||||
// Search the assignment to return.
|
// Search the assignment to return.
|
||||||
if (response.courses && response.courses.length) {
|
if (response.courses && response.courses.length) {
|
||||||
const assignments = response.courses[0].assignments;
|
const assignments = response.courses[0].assignments;
|
||||||
|
@ -187,7 +188,7 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the assignment.
|
* @return Promise resolved with the assignment.
|
||||||
*/
|
*/
|
||||||
getAssignmentById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getAssignmentById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignAssign> {
|
||||||
return this.getAssignmentByField(courseId, 'id', id, ignoreCache, siteId);
|
return this.getAssignmentByField(courseId, 'id', id, ignoreCache, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +226,9 @@ export class AddonModAssignProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_assign_get_user_mappings', params, preSets).then((response) => {
|
return site.read('mod_assign_get_user_mappings', params, preSets)
|
||||||
|
.then((response: AddonModAssignGetUserMappingsResult): any => {
|
||||||
|
|
||||||
// Search the user.
|
// Search the user.
|
||||||
if (response.assignments && response.assignments.length) {
|
if (response.assignments && response.assignments.length) {
|
||||||
if (!userId || userId < 0) {
|
if (!userId || userId < 0) {
|
||||||
|
@ -271,7 +274,7 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Resolved with requested info when done.
|
* @return Resolved with requested info when done.
|
||||||
*/
|
*/
|
||||||
getAssignmentGrades(assignId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getAssignmentGrades(assignId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignGrade[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
assignmentids: [assignId]
|
assignmentids: [assignId]
|
||||||
|
@ -285,7 +288,7 @@ export class AddonModAssignProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_assign_get_grades', params, preSets).then((response) => {
|
return site.read('mod_assign_get_grades', params, preSets).then((response: AddonModAssignGetGradesResult): any => {
|
||||||
// Search the assignment.
|
// Search the assignment.
|
||||||
if (response.assignments && response.assignments.length) {
|
if (response.assignments && response.assignments.length) {
|
||||||
const assignment = response.assignments[0];
|
const assignment = response.assignments[0];
|
||||||
|
@ -294,7 +297,7 @@ export class AddonModAssignProvider {
|
||||||
return assignment.grades;
|
return assignment.grades;
|
||||||
}
|
}
|
||||||
} else if (response.warnings && response.warnings.length) {
|
} else if (response.warnings && response.warnings.length) {
|
||||||
if (response.warnings[0].warningcode == 3) {
|
if (response.warnings[0].warningcode == '3') {
|
||||||
// No grades found.
|
// No grades found.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -362,7 +365,9 @@ export class AddonModAssignProvider {
|
||||||
* @param attempt Attempt.
|
* @param attempt Attempt.
|
||||||
* @return Submission object or null.
|
* @return Submission object or null.
|
||||||
*/
|
*/
|
||||||
getSubmissionObjectFromAttempt(assign: any, attempt: any): any {
|
getSubmissionObjectFromAttempt(assign: AddonModAssignAssign, attempt: AddonModAssignSubmissionAttempt)
|
||||||
|
: AddonModAssignSubmission {
|
||||||
|
|
||||||
if (!attempt) {
|
if (!attempt) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -432,7 +437,7 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
getSubmissions(assignId: number, ignoreCache?: boolean, siteId?: string)
|
getSubmissions(assignId: number, ignoreCache?: boolean, siteId?: string)
|
||||||
: Promise<{canviewsubmissions: boolean, submissions?: any[]}> {
|
: Promise<{canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]}> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -448,9 +453,11 @@ export class AddonModAssignProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_assign_get_submissions', params, preSets).then((response): any => {
|
return site.read('mod_assign_get_submissions', params, preSets)
|
||||||
|
.then((response: AddonModAssignGetSubmissionsResult): any => {
|
||||||
|
|
||||||
// Check if we can view submissions, with enough permissions.
|
// Check if we can view submissions, with enough permissions.
|
||||||
if (response.warnings.length > 0 && response.warnings[0].warningcode == 1) {
|
if (response.warnings.length > 0 && response.warnings[0].warningcode == '1') {
|
||||||
return {canviewsubmissions: false};
|
return {canviewsubmissions: false};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +496,7 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise always resolved with the user submission status.
|
* @return Promise always resolved with the user submission status.
|
||||||
*/
|
*/
|
||||||
getSubmissionStatus(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true,
|
getSubmissionStatus(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true,
|
||||||
ignoreCache?: boolean, siteId?: string): Promise<any> {
|
ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignGetSubmissionStatusResult> {
|
||||||
|
|
||||||
userId = userId || 0;
|
userId = userId || 0;
|
||||||
|
|
||||||
|
@ -540,7 +547,7 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise always resolved with the user submission status.
|
* @return Promise always resolved with the user submission status.
|
||||||
*/
|
*/
|
||||||
getSubmissionStatusWithRetry(assign: any, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true,
|
getSubmissionStatusWithRetry(assign: any, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true,
|
||||||
ignoreCache?: boolean, siteId?: string): Promise<any> {
|
ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignGetSubmissionStatusResult> {
|
||||||
|
|
||||||
return this.getSubmissionStatus(assign.id, userId, groupId, isBlind, filter, ignoreCache, siteId).then((response) => {
|
return this.getSubmissionStatus(assign.id, userId, groupId, isBlind, filter, ignoreCache, siteId).then((response) => {
|
||||||
const userSubmission = this.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
const userSubmission = this.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
||||||
|
@ -630,7 +637,9 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the list of participants and summary of submissions.
|
* @return Promise resolved with the list of participants and summary of submissions.
|
||||||
*/
|
*/
|
||||||
listParticipants(assignId: number, groupId?: number, ignoreCache?: boolean, siteId?: string): Promise<any[]> {
|
listParticipants(assignId: number, groupId?: number, ignoreCache?: boolean, siteId?: string)
|
||||||
|
: Promise<AddonModAssignParticipant[]> {
|
||||||
|
|
||||||
groupId = groupId || 0;
|
groupId = groupId || 0;
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
@ -1051,14 +1060,14 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when saved, rejected otherwise.
|
* @return Promise resolved when saved, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
saveSubmissionOnline(assignId: number, pluginData: any, siteId?: string): Promise<any> {
|
saveSubmissionOnline(assignId: number, pluginData: any, siteId?: string): Promise<void> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
assignmentid: assignId,
|
assignmentid: assignId,
|
||||||
plugindata: pluginData
|
plugindata: pluginData
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('mod_assign_save_submission', params).then((warnings) => {
|
return site.write('mod_assign_save_submission', params).then((warnings: CoreWSExternalWarning[]) => {
|
||||||
if (warnings && warnings.length) {
|
if (warnings && warnings.length) {
|
||||||
// The WebService returned warnings, reject.
|
// The WebService returned warnings, reject.
|
||||||
return Promise.reject(warnings[0]);
|
return Promise.reject(warnings[0]);
|
||||||
|
@ -1120,14 +1129,14 @@ export class AddonModAssignProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when submitted, rejected otherwise.
|
* @return Promise resolved when submitted, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
submitForGradingOnline(assignId: number, acceptStatement: boolean, siteId?: string): Promise<any> {
|
submitForGradingOnline(assignId: number, acceptStatement: boolean, siteId?: string): Promise<void> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
assignmentid: assignId,
|
assignmentid: assignId,
|
||||||
acceptsubmissionstatement: acceptStatement ? 1 : 0
|
acceptsubmissionstatement: acceptStatement ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('mod_assign_submit_for_grading', params).then((warnings) => {
|
return site.write('mod_assign_submit_for_grading', params).then((warnings: CoreWSExternalWarning[]) => {
|
||||||
if (warnings && warnings.length) {
|
if (warnings && warnings.length) {
|
||||||
// The WebService returned warnings, reject.
|
// The WebService returned warnings, reject.
|
||||||
return Promise.reject(warnings[0]);
|
return Promise.reject(warnings[0]);
|
||||||
|
@ -1169,7 +1178,10 @@ export class AddonModAssignProvider {
|
||||||
return this.isGradingOfflineEnabled(siteId).then((enabled) => {
|
return this.isGradingOfflineEnabled(siteId).then((enabled) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return this.submitGradingFormOnline(assignId, userId, grade, attemptNumber, addAttempt, workflowState,
|
return this.submitGradingFormOnline(assignId, userId, grade, attemptNumber, addAttempt, workflowState,
|
||||||
applyToAll, outcomes, pluginData, siteId);
|
applyToAll, outcomes, pluginData, siteId).then(() => {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.appProvider.isOnline()) {
|
if (!this.appProvider.isOnline()) {
|
||||||
|
@ -1212,7 +1224,7 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise resolved when submitted, rejected otherwise.
|
* @return Promise resolved when submitted, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
submitGradingFormOnline(assignId: number, userId: number, grade: number, attemptNumber: number, addAttempt: boolean,
|
submitGradingFormOnline(assignId: number, userId: number, grade: number, attemptNumber: number, addAttempt: boolean,
|
||||||
workflowState: string, applyToAll: boolean, outcomes: any, pluginData: any, siteId?: string): Promise<any> {
|
workflowState: string, applyToAll: boolean, outcomes: any, pluginData: any, siteId?: string): Promise<void | null> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
@ -1243,7 +1255,7 @@ export class AddonModAssignProvider {
|
||||||
jsonformdata: JSON.stringify(serialized)
|
jsonformdata: JSON.stringify(serialized)
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('mod_assign_submit_grading_form', params).then((warnings) => {
|
return site.write('mod_assign_submit_grading_form', params).then((warnings: CoreWSExternalWarning[]) => {
|
||||||
if (warnings && warnings.length) {
|
if (warnings && warnings.length) {
|
||||||
// The WebService returned warnings, reject.
|
// The WebService returned warnings, reject.
|
||||||
return Promise.reject(warnings[0]);
|
return Promise.reject(warnings[0]);
|
||||||
|
@ -1271,3 +1283,285 @@ export class AddonModAssignProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign data returned by mod_assign_get_assignments.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignAssign = {
|
||||||
|
id: number; // Assignment id.
|
||||||
|
cmid: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Assignment name.
|
||||||
|
nosubmissions: number; // No submissions.
|
||||||
|
submissiondrafts: number; // Submissions drafts.
|
||||||
|
sendnotifications: number; // Send notifications.
|
||||||
|
sendlatenotifications: number; // Send notifications.
|
||||||
|
sendstudentnotifications: number; // Send student notifications (default).
|
||||||
|
duedate: number; // Assignment due date.
|
||||||
|
allowsubmissionsfromdate: number; // Allow submissions from date.
|
||||||
|
grade: number; // Grade type.
|
||||||
|
timemodified: number; // Last time assignment was modified.
|
||||||
|
completionsubmit: number; // If enabled, set activity as complete following submission.
|
||||||
|
cutoffdate: number; // Date after which submission is not accepted without an extension.
|
||||||
|
gradingduedate?: number; // @since 3.3. The expected date for marking the submissions.
|
||||||
|
teamsubmission: number; // If enabled, students submit as a team.
|
||||||
|
requireallteammemberssubmit: number; // If enabled, all team members must submit.
|
||||||
|
teamsubmissiongroupingid: number; // The grouping id for the team submission groups.
|
||||||
|
blindmarking: number; // If enabled, hide identities until reveal identities actioned.
|
||||||
|
hidegrader?: number; // @since 3.7. If enabled, hide grader to student.
|
||||||
|
revealidentities: number; // Show identities for a blind marking assignment.
|
||||||
|
attemptreopenmethod: string; // Method used to control opening new attempts.
|
||||||
|
maxattempts: number; // Maximum number of attempts allowed.
|
||||||
|
markingworkflow: number; // Enable marking workflow.
|
||||||
|
markingallocation: number; // Enable marking allocation.
|
||||||
|
requiresubmissionstatement: number; // Student must accept submission statement.
|
||||||
|
preventsubmissionnotingroup?: number; // @since 3.2. Prevent submission not in group.
|
||||||
|
submissionstatement?: string; // @since 3.2. Submission statement formatted.
|
||||||
|
submissionstatementformat?: number; // @since 3.2. Submissionstatement format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
configs: AddonModAssignConfig[]; // Configuration settings.
|
||||||
|
intro?: string; // Assignment intro, not allways returned because it deppends on the activity configuration.
|
||||||
|
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
introattachments?: CoreWSExternalFile[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config setting in an assign.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignConfig = {
|
||||||
|
id?: number; // Assign_plugin_config id.
|
||||||
|
assignment?: number; // Assignment id.
|
||||||
|
plugin: string; // Plugin.
|
||||||
|
subtype: string; // Subtype.
|
||||||
|
name: string; // Name.
|
||||||
|
value: string; // Value.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grade of an assign, returned by mod_assign_get_grades.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGrade = {
|
||||||
|
id: number; // Grade id.
|
||||||
|
assignment?: number; // Assignment id.
|
||||||
|
userid: number; // Student id.
|
||||||
|
attemptnumber: number; // Attempt number.
|
||||||
|
timecreated: number; // Grade creation time.
|
||||||
|
timemodified: number; // Grade last modified time.
|
||||||
|
grader: number; // Grader, -1 if grader is hidden.
|
||||||
|
grade: string; // Grade.
|
||||||
|
gradefordisplay?: string; // Grade rendered into a format suitable for display.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign submission returned by mod_assign_get_submissions.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmission = {
|
||||||
|
id: number; // Submission id.
|
||||||
|
userid: number; // Student id.
|
||||||
|
attemptnumber: number; // Attempt number.
|
||||||
|
timecreated: number; // Submission creation time.
|
||||||
|
timemodified: number; // Submission last modified time.
|
||||||
|
status: string; // Submission status.
|
||||||
|
groupid: number; // Group id.
|
||||||
|
assignment?: number; // Assignment id.
|
||||||
|
latest?: number; // Latest attempt.
|
||||||
|
plugins?: AddonModAssignPlugin[]; // Plugins.
|
||||||
|
gradingstatus?: string; // @since 3.2. Grading status.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign plugin.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignPlugin = {
|
||||||
|
type: string; // Submission plugin type.
|
||||||
|
name: string; // Submission plugin name.
|
||||||
|
fileareas?: { // Fileareas.
|
||||||
|
area: string; // File area.
|
||||||
|
files?: CoreWSExternalFile[];
|
||||||
|
}[];
|
||||||
|
editorfields?: { // Editorfields.
|
||||||
|
name: string; // Field name.
|
||||||
|
description: string; // Field description.
|
||||||
|
text: string; // Field value.
|
||||||
|
format: number; // Text format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grading summary of an assign submission.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmissionGradingSummary = {
|
||||||
|
participantcount: number; // Number of users who can submit.
|
||||||
|
submissiondraftscount: number; // Number of submissions in draft status.
|
||||||
|
submissionsenabled: boolean; // Whether submissions are enabled or not.
|
||||||
|
submissionssubmittedcount: number; // Number of submissions in submitted status.
|
||||||
|
submissionsneedgradingcount: number; // Number of submissions that need grading.
|
||||||
|
warnofungroupedusers: string; // Whether we need to warn people about groups.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt of an assign submission.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmissionAttempt = {
|
||||||
|
submission?: AddonModAssignSubmission; // Submission info.
|
||||||
|
teamsubmission?: AddonModAssignSubmission; // Submission info.
|
||||||
|
submissiongroup?: number; // The submission group id (for group submissions only).
|
||||||
|
submissiongroupmemberswhoneedtosubmit?: number[]; // List of users who still need to submit (for group submissions only).
|
||||||
|
submissionsenabled: boolean; // Whether submissions are enabled or not.
|
||||||
|
locked: boolean; // Whether new submissions are locked.
|
||||||
|
graded: boolean; // Whether the submission is graded.
|
||||||
|
canedit: boolean; // Whether the user can edit the current submission.
|
||||||
|
caneditowner?: boolean; // @since 3.2. Whether the owner of the submission can edit it.
|
||||||
|
cansubmit: boolean; // Whether the user can submit.
|
||||||
|
extensionduedate: number; // Extension due date.
|
||||||
|
blindmarking: boolean; // Whether blind marking is enabled.
|
||||||
|
gradingstatus: string; // Grading status.
|
||||||
|
usergroups: number[]; // User groups in the course.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous attempt of an assign submission.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmissionPreviousAttempt = {
|
||||||
|
attemptnumber: number; // Attempt number.
|
||||||
|
submission?: AddonModAssignSubmission; // Submission info.
|
||||||
|
grade?: AddonModAssignGrade; // Grade information.
|
||||||
|
feedbackplugins?: AddonModAssignPlugin[]; // Feedback info.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feedback of an assign submission.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmissionFeedback = {
|
||||||
|
grade: AddonModAssignGrade; // Grade information.
|
||||||
|
gradefordisplay: string; // Grade rendered into a format suitable for display.
|
||||||
|
gradeddate: number; // The date the user was graded.
|
||||||
|
plugins?: AddonModAssignPlugin[]; // Plugins info.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Participant returned by mod_assign_list_participants.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignParticipant = {
|
||||||
|
id: number; // ID of the user.
|
||||||
|
username?: string; // The username.
|
||||||
|
firstname?: string; // The first name(s) of the user.
|
||||||
|
lastname?: string; // The family name of the user.
|
||||||
|
fullname: string; // The fullname of the user.
|
||||||
|
email?: string; // Email address.
|
||||||
|
address?: string; // Postal address.
|
||||||
|
phone1?: string; // Phone 1.
|
||||||
|
phone2?: string; // Phone 2.
|
||||||
|
icq?: string; // Icq number.
|
||||||
|
skype?: string; // Skype id.
|
||||||
|
yahoo?: string; // Yahoo id.
|
||||||
|
aim?: string; // Aim id.
|
||||||
|
msn?: string; // Msn number.
|
||||||
|
department?: string; // Department.
|
||||||
|
institution?: string; // Institution.
|
||||||
|
idnumber?: string; // The idnumber of the user.
|
||||||
|
interests?: string; // User interests (separated by commas).
|
||||||
|
firstaccess?: number; // First access to the site (0 if never).
|
||||||
|
lastaccess?: number; // Last access to the site (0 if never).
|
||||||
|
suspended?: boolean; // @since 3.2. Suspend user account, either false to enable user login or true to disable it.
|
||||||
|
description?: string; // User profile description.
|
||||||
|
descriptionformat?: number; // Int format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
city?: string; // Home city of the user.
|
||||||
|
url?: string; // URL of the user.
|
||||||
|
country?: string; // Home country code of the user, such as AU or CZ.
|
||||||
|
profileimageurlsmall?: string; // User image profile URL - small version.
|
||||||
|
profileimageurl?: string; // User image profile URL - big version.
|
||||||
|
customfields?: { // User custom fields (also known as user profile fields).
|
||||||
|
type: string; // The type of the custom field - text field, checkbox...
|
||||||
|
value: string; // The value of the custom field.
|
||||||
|
name: string; // The name of the custom field.
|
||||||
|
shortname: string; // The shortname of the custom field - to be able to build the field class in the code.
|
||||||
|
}[];
|
||||||
|
preferences?: { // Users preferences.
|
||||||
|
name: string; // The name of the preferences.
|
||||||
|
value: string; // The value of the preference.
|
||||||
|
}[];
|
||||||
|
recordid?: number; // @since 3.7. Record id.
|
||||||
|
groups?: { // User groups.
|
||||||
|
id: number; // Group id.
|
||||||
|
name: string; // Group name.
|
||||||
|
description: string; // Group description.
|
||||||
|
}[];
|
||||||
|
roles?: { // User roles.
|
||||||
|
roleid: number; // Role id.
|
||||||
|
name: string; // Role name.
|
||||||
|
shortname: string; // Role shortname.
|
||||||
|
sortorder: number; // Role sortorder.
|
||||||
|
}[];
|
||||||
|
enrolledcourses?: { // Courses where the user is enrolled - limited by which courses the user is able to see.
|
||||||
|
id: number; // Id of the course.
|
||||||
|
fullname: string; // Fullname of the course.
|
||||||
|
shortname: string; // Shortname of the course.
|
||||||
|
}[];
|
||||||
|
submitted: boolean; // Have they submitted their assignment.
|
||||||
|
requiregrading: boolean; // Is their submission waiting for grading.
|
||||||
|
grantedextension?: boolean; // @since 3.3. Have they been granted an extension.
|
||||||
|
groupid?: number; // For group assignments this is the group id.
|
||||||
|
groupname?: string; // For group assignments this is the group name.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_assign_get_assignments.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGetAssignmentsResult = {
|
||||||
|
courses: { // List of courses.
|
||||||
|
id: number; // Course id.
|
||||||
|
fullname: string; // Course full name.
|
||||||
|
shortname: string; // Course short name.
|
||||||
|
timemodified: number; // Last time modified.
|
||||||
|
assignments: AddonModAssignAssign[]; // Assignment info.
|
||||||
|
}[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_assign_get_user_mappings.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGetUserMappingsResult = {
|
||||||
|
assignments: { // List of assign user mapping data.
|
||||||
|
assignmentid: number; // Assignment id.
|
||||||
|
mappings: {
|
||||||
|
id: number; // User mapping id.
|
||||||
|
userid: number; // Student id.
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_assign_get_grades.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGetGradesResult = {
|
||||||
|
assignments: { // List of assignment grade information.
|
||||||
|
assignmentid: number; // Assignment id.
|
||||||
|
grades: AddonModAssignGrade[];
|
||||||
|
}[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_assign_get_submissions.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGetSubmissionsResult = {
|
||||||
|
assignments: { // Assignment submissions.
|
||||||
|
assignmentid: number; // Assignment id.
|
||||||
|
submissions: AddonModAssignSubmission[];
|
||||||
|
}[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_assign_get_submission_status.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignGetSubmissionStatusResult = {
|
||||||
|
gradingsummary?: AddonModAssignSubmissionGradingSummary; // Grading information.
|
||||||
|
lastattempt?: AddonModAssignSubmissionAttempt; // Last attempt information.
|
||||||
|
feedback?: AddonModAssignSubmissionFeedback; // Feedback for the last attempt.
|
||||||
|
previousattempts?: AddonModAssignSubmissionPreviousAttempt[]; // List all the previous attempts did by the user.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
import { AddonModAssignDefaultFeedbackHandler } from './default-feedback-handler';
|
import { AddonModAssignDefaultFeedbackHandler } from './default-feedback-handler';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that all feedback handlers must implement.
|
* Interface that all feedback handlers must implement.
|
||||||
|
@ -47,7 +48,7 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent?(injector: Injector, plugin: any): any | Promise<any>;
|
getComponent?(injector: Injector, plugin: AddonModAssignPlugin): any | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the draft saved data of the feedback plugin.
|
* Return the draft saved data of the feedback plugin.
|
||||||
|
@ -69,7 +70,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles?(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]>;
|
getPluginFiles?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a readable name to use for the plugin.
|
* Get a readable name to use for the plugin.
|
||||||
|
@ -77,7 +79,7 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The plugin name.
|
* @return The plugin name.
|
||||||
*/
|
*/
|
||||||
getPluginName?(plugin: any): string;
|
getPluginName?(plugin: AddonModAssignPlugin): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the feedback data has changed for this plugin.
|
* Check if the feedback data has changed for this plugin.
|
||||||
|
@ -89,7 +91,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param userId User ID of the submission.
|
* @param userId User ID of the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged?(assign: any, submission: any, plugin: any, inputData: any, userId: number): boolean | Promise<boolean>;
|
hasDataChanged?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, userId: number): boolean | Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the plugin has draft data stored.
|
* Check whether the plugin has draft data stored.
|
||||||
|
@ -111,7 +114,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch?(assign: any, submission: any, plugin: any, siteId?: string): Promise<any>;
|
prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare and add to pluginData the data to send to the server based on the draft data saved.
|
* Prepare and add to pluginData the data to send to the server based on the draft data saved.
|
||||||
|
@ -123,7 +127,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareFeedbackData?(assignId: number, userId: number, plugin: any, pluginData: any, siteId?: string): void | Promise<any>;
|
prepareFeedbackData?(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
siteId?: string): void | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save draft data of the feedback plugin.
|
* Save draft data of the feedback plugin.
|
||||||
|
@ -135,7 +140,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
saveDraft?(assignId: number, userId: number, plugin: any, data: any, siteId?: string): void | Promise<any>;
|
saveDraft?(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string)
|
||||||
|
: void | Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +166,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
discardPluginFeedbackData(assignId: number, userId: number, plugin: any, siteId?: string): Promise<any> {
|
discardPluginFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string)
|
||||||
|
: Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +178,7 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Promise resolved with the component to use, undefined if not found.
|
* @return Promise resolved with the component to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponentForPlugin(injector: Injector, plugin: any): Promise<any> {
|
getComponentForPlugin(injector: Injector, plugin: AddonModAssignPlugin): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +191,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the draft data.
|
* @return Promise resolved with the draft data.
|
||||||
*/
|
*/
|
||||||
getPluginDraftData(assignId: number, userId: number, plugin: any, siteId?: string): Promise<any> {
|
getPluginDraftData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string)
|
||||||
|
: Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, userId, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, userId, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +206,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the files.
|
* @return Promise resolved with the files.
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any[]> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +217,7 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param plugin Plugin to get the name for.
|
* @param plugin Plugin to get the name for.
|
||||||
* @return Human readable name.
|
* @return Human readable name.
|
||||||
*/
|
*/
|
||||||
getPluginName(plugin: any): string {
|
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||||
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +231,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param userId User ID of the submission.
|
* @param userId User ID of the submission.
|
||||||
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
||||||
*/
|
*/
|
||||||
hasPluginDataChanged(assign: any, submission: any, plugin: any, inputData: any, userId: number): Promise<boolean> {
|
hasPluginDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, userId: number): Promise<boolean> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged',
|
||||||
[assign, submission, plugin, inputData, userId]));
|
[assign, submission, plugin, inputData, userId]));
|
||||||
}
|
}
|
||||||
|
@ -236,7 +246,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with true if it has draft data.
|
* @return Promise resolved with true if it has draft data.
|
||||||
*/
|
*/
|
||||||
hasPluginDraftData(assignId: number, userId: number, plugin: any, siteId?: string): Promise<boolean> {
|
hasPluginDraftData(assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string)
|
||||||
|
: Promise<boolean> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDraftData', [assignId, userId, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDraftData', [assignId, userId, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +270,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch(assign: any, submission: any, plugin: any, siteId?: string): Promise<any> {
|
prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +285,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when data has been gathered.
|
* @return Promise resolved when data has been gathered.
|
||||||
*/
|
*/
|
||||||
preparePluginFeedbackData(assignId: number, userId: number, plugin: any, pluginData: any, siteId?: string): Promise<any> {
|
preparePluginFeedbackData(assignId: number, userId: number, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareFeedbackData',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareFeedbackData',
|
||||||
[assignId, userId, plugin, pluginData, siteId]));
|
[assignId, userId, plugin, pluginData, siteId]));
|
||||||
|
@ -289,7 +302,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when data has been saved.
|
* @return Promise resolved when data has been saved.
|
||||||
*/
|
*/
|
||||||
saveFeedbackDraft(assignId: number, userId: number, plugin: any, inputData: any, siteId?: string): Promise<any> {
|
saveFeedbackDraft(assignId: number, userId: number, plugin: AddonModAssignPlugin, inputData: any,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'saveDraft',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'saveDraft',
|
||||||
[assignId, userId, plugin, inputData, siteId]));
|
[assignId, userId, plugin, inputData, siteId]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||||
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
||||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||||
import { AddonModAssignProvider } from './assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignParticipant,
|
||||||
|
AddonModAssignSubmissionFeedback
|
||||||
|
} from './assign';
|
||||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +49,7 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param submission Submission.
|
* @param submission Submission.
|
||||||
* @return Whether it can be edited offline.
|
* @return Whether it can be edited offline.
|
||||||
*/
|
*/
|
||||||
canEditSubmissionOffline(assign: any, submission: any): Promise<boolean> {
|
canEditSubmissionOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission): Promise<boolean> {
|
||||||
if (!submission) {
|
if (!submission) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +84,7 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param submission Submission to clear the data for.
|
* @param submission Submission to clear the data for.
|
||||||
* @param inputData Data entered in the submission form.
|
* @param inputData Data entered in the submission form.
|
||||||
*/
|
*/
|
||||||
clearSubmissionPluginTmpData(assign: any, submission: any, inputData: any): void {
|
clearSubmissionPluginTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any): void {
|
||||||
submission.plugins.forEach((plugin) => {
|
submission.plugins.forEach((plugin) => {
|
||||||
this.submissionDelegate.clearTmpData(assign, submission, plugin, inputData);
|
this.submissionDelegate.clearTmpData(assign, submission, plugin, inputData);
|
||||||
});
|
});
|
||||||
|
@ -95,7 +98,7 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param previousSubmission Submission to copy.
|
* @param previousSubmission Submission to copy.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
copyPreviousAttempt(assign: any, previousSubmission: any): Promise<any> {
|
copyPreviousAttempt(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<any> {
|
||||||
const pluginData = {},
|
const pluginData = {},
|
||||||
promises = [];
|
promises = [];
|
||||||
|
|
||||||
|
@ -112,6 +115,36 @@ export class AddonModAssignHelperProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty feedback object.
|
||||||
|
*
|
||||||
|
* @return Feedback.
|
||||||
|
*/
|
||||||
|
createEmptyFeedback(): AddonModAssignSubmissionFeedback {
|
||||||
|
return {
|
||||||
|
grade: undefined,
|
||||||
|
gradefordisplay: undefined,
|
||||||
|
gradeddate: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty submission object.
|
||||||
|
*
|
||||||
|
* @return Submission.
|
||||||
|
*/
|
||||||
|
createEmptySubmission(): AddonModAssignSubmissionFormatted {
|
||||||
|
return {
|
||||||
|
id: undefined,
|
||||||
|
userid: undefined,
|
||||||
|
attemptnumber: undefined,
|
||||||
|
timecreated: undefined,
|
||||||
|
timemodified: undefined,
|
||||||
|
status: undefined,
|
||||||
|
groupid: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete stored submission files for a plugin. See storeSubmissionFiles.
|
* Delete stored submission files for a plugin. See storeSubmissionFiles.
|
||||||
*
|
*
|
||||||
|
@ -136,7 +169,9 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
discardFeedbackPluginData(assignId: number, userId: number, feedback: any, siteId?: string): Promise<any> {
|
discardFeedbackPluginData(assignId: number, userId: number, feedback: AddonModAssignSubmissionFeedback,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
feedback.plugins.forEach((plugin) => {
|
feedback.plugins.forEach((plugin) => {
|
||||||
|
@ -155,7 +190,9 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the list of participants and summary of submissions.
|
* @return Promise resolved with the list of participants and summary of submissions.
|
||||||
*/
|
*/
|
||||||
getParticipants(assign: any, groupId?: number, ignoreCache?: boolean, siteId?: string): Promise<any[]> {
|
getParticipants(assign: AddonModAssignAssign, groupId?: number, ignoreCache?: boolean, siteId?: string)
|
||||||
|
: Promise<AddonModAssignParticipant[]> {
|
||||||
|
|
||||||
groupId = groupId || 0;
|
groupId = groupId || 0;
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
@ -167,7 +204,7 @@ export class AddonModAssignHelperProvider {
|
||||||
// If no participants returned and all groups specified, get participants by groups.
|
// If no participants returned and all groups specified, get participants by groups.
|
||||||
return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((info) => {
|
return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((info) => {
|
||||||
const promises = [],
|
const promises = [],
|
||||||
participants = {};
|
participants: {[id: number]: AddonModAssignParticipant} = {};
|
||||||
|
|
||||||
info.groups.forEach((userGroup) => {
|
info.groups.forEach((userGroup) => {
|
||||||
promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, ignoreCache, siteId)
|
promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, ignoreCache, siteId)
|
||||||
|
@ -194,8 +231,8 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param type Name of the subplugin.
|
* @param type Name of the subplugin.
|
||||||
* @return Object containing all configurations of the subplugin selected.
|
* @return Object containing all configurations of the subplugin selected.
|
||||||
*/
|
*/
|
||||||
getPluginConfig(assign: any, subtype: string, type: string): any {
|
getPluginConfig(assign: AddonModAssignAssign, subtype: string, type: string): {[name: string]: string} {
|
||||||
const configs = {};
|
const configs: {[name: string]: string} = {};
|
||||||
|
|
||||||
assign.configs.forEach((config) => {
|
assign.configs.forEach((config) => {
|
||||||
if (config.subtype == subtype && config.plugin == type) {
|
if (config.subtype == subtype && config.plugin == type) {
|
||||||
|
@ -213,7 +250,7 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param subtype Subtype name (assignsubmission or assignfeedback)
|
* @param subtype Subtype name (assignsubmission or assignfeedback)
|
||||||
* @return List of enabled plugins for the assign.
|
* @return List of enabled plugins for the assign.
|
||||||
*/
|
*/
|
||||||
getPluginsEnabled(assign: any, subtype: string): any[] {
|
getPluginsEnabled(assign: AddonModAssignAssign, subtype: string): any[] {
|
||||||
const enabled = [];
|
const enabled = [];
|
||||||
|
|
||||||
assign.configs.forEach((config) => {
|
assign.configs.forEach((config) => {
|
||||||
|
@ -250,7 +287,7 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param previousSubmission Submission to copy.
|
* @param previousSubmission Submission to copy.
|
||||||
* @return Promise resolved with the size.
|
* @return Promise resolved with the size.
|
||||||
*/
|
*/
|
||||||
getSubmissionSizeForCopy(assign: any, previousSubmission: any): Promise<number> {
|
getSubmissionSizeForCopy(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<number> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
|
||||||
|
@ -273,7 +310,8 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param inputData Data entered in the submission form.
|
* @param inputData Data entered in the submission form.
|
||||||
* @return Promise resolved with the size.
|
* @return Promise resolved with the size.
|
||||||
*/
|
*/
|
||||||
getSubmissionSizeForEdit(assign: any, submission: any, inputData: any): Promise<number> {
|
getSubmissionSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any): Promise<number> {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
|
||||||
|
@ -298,14 +336,14 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param siteId Site id (empty for current site).
|
* @param siteId Site id (empty for current site).
|
||||||
* @return Promise always resolved. Resolve param is the formatted submissions.
|
* @return Promise always resolved. Resolve param is the formatted submissions.
|
||||||
*/
|
*/
|
||||||
getSubmissionsUserData(assign: any, submissions: any[], groupId?: number, ignoreCache?: boolean, siteId?: string):
|
getSubmissionsUserData(assign: AddonModAssignAssign, submissions: AddonModAssignSubmissionFormatted[], groupId?: number,
|
||||||
Promise<any[]> {
|
ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignSubmissionFormatted[]> {
|
||||||
return this.getParticipants(assign, groupId).then((participants) => {
|
|
||||||
|
return this.getParticipants(assign, groupId).then((parts) => {
|
||||||
const blind = assign.blindmarking && !assign.revealidentities;
|
const blind = assign.blindmarking && !assign.revealidentities;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const result = [];
|
const result: AddonModAssignSubmissionFormatted[] = [];
|
||||||
|
const participants: {[id: number]: AddonModAssignParticipant} = this.utils.arrayToObject(parts, 'id');
|
||||||
participants = this.utils.arrayToObject(participants, 'id');
|
|
||||||
|
|
||||||
submissions.forEach((submission) => {
|
submissions.forEach((submission) => {
|
||||||
submission.submitid = submission.userid > 0 ? submission.userid : submission.blindid;
|
submission.submitid = submission.userid > 0 ? submission.userid : submission.blindid;
|
||||||
|
@ -356,10 +394,10 @@ export class AddonModAssignHelperProvider {
|
||||||
|
|
||||||
return Promise.all(promises).then(() => {
|
return Promise.all(promises).then(() => {
|
||||||
// Create a submission for each participant left in the list (the participants already treated were removed).
|
// Create a submission for each participant left in the list (the participants already treated were removed).
|
||||||
this.utils.objectToArray(participants).forEach((participant) => {
|
this.utils.objectToArray(participants).forEach((participant: AddonModAssignParticipant) => {
|
||||||
const submission: any = {
|
const submission = this.createEmptySubmission();
|
||||||
submitid: participant.id
|
|
||||||
};
|
submission.submitid = participant.id;
|
||||||
|
|
||||||
if (!blind) {
|
if (!blind) {
|
||||||
submission.userid = participant.id;
|
submission.userid = participant.id;
|
||||||
|
@ -390,17 +428,20 @@ export class AddonModAssignHelperProvider {
|
||||||
* Check if the feedback data has changed for a certain submission and assign.
|
* Check if the feedback data has changed for a certain submission and assign.
|
||||||
*
|
*
|
||||||
* @param assign Assignment.
|
* @param assign Assignment.
|
||||||
* @param userId User Id.
|
* @param submission The submission.
|
||||||
* @param feedback Feedback data.
|
* @param feedback Feedback data.
|
||||||
|
* @param userId The user ID.
|
||||||
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
||||||
*/
|
*/
|
||||||
hasFeedbackDataChanged(assign: any, userId: number, feedback: any): Promise<boolean> {
|
hasFeedbackDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
feedback: AddonModAssignSubmissionFeedback, userId: number): Promise<boolean> {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let hasChanged = false;
|
let hasChanged = false;
|
||||||
|
|
||||||
feedback.plugins.forEach((plugin) => {
|
feedback.plugins.forEach((plugin) => {
|
||||||
promises.push(this.prepareFeedbackPluginData(assign.id, userId, feedback).then((inputData) => {
|
promises.push(this.prepareFeedbackPluginData(assign.id, userId, feedback).then((inputData) => {
|
||||||
return this.feedbackDelegate.hasPluginDataChanged(assign, userId, plugin, inputData, userId).then((changed) => {
|
return this.feedbackDelegate.hasPluginDataChanged(assign, submission, plugin, inputData, userId).then((changed) => {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
|
@ -423,7 +464,9 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param inputData Data entered in the submission form.
|
* @param inputData Data entered in the submission form.
|
||||||
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
||||||
*/
|
*/
|
||||||
hasSubmissionDataChanged(assign: any, submission: any, inputData: any): Promise<boolean> {
|
hasSubmissionDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any)
|
||||||
|
: Promise<boolean> {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
let hasChanged = false;
|
let hasChanged = false;
|
||||||
|
|
||||||
|
@ -451,7 +494,9 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with plugin data to send to server.
|
* @return Promise resolved with plugin data to send to server.
|
||||||
*/
|
*/
|
||||||
prepareFeedbackPluginData(assignId: number, userId: number, feedback: any, siteId?: string): Promise<any> {
|
prepareFeedbackPluginData(assignId: number, userId: number, feedback: AddonModAssignSubmissionFeedback, siteId?: string)
|
||||||
|
: Promise<any> {
|
||||||
|
|
||||||
const pluginData = {},
|
const pluginData = {},
|
||||||
promises = [];
|
promises = [];
|
||||||
|
|
||||||
|
@ -473,7 +518,9 @@ export class AddonModAssignHelperProvider {
|
||||||
* @param offline True to prepare the data for an offline submission, false otherwise.
|
* @param offline True to prepare the data for an offline submission, false otherwise.
|
||||||
* @return Promise resolved with plugin data to send to server.
|
* @return Promise resolved with plugin data to send to server.
|
||||||
*/
|
*/
|
||||||
prepareSubmissionPluginData(assign: any, submission: any, inputData: any, offline?: boolean): Promise<any> {
|
prepareSubmissionPluginData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, inputData: any,
|
||||||
|
offline?: boolean): Promise<any> {
|
||||||
|
|
||||||
const pluginData = {},
|
const pluginData = {},
|
||||||
promises = [];
|
promises = [];
|
||||||
|
|
||||||
|
@ -553,3 +600,16 @@ export class AddonModAssignHelperProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign submission with some calculated data.
|
||||||
|
*/
|
||||||
|
export type AddonModAssignSubmissionFormatted = AddonModAssignSubmission & {
|
||||||
|
blindid?: number; // Calculated in the app. Blindid of the user that did the submission.
|
||||||
|
submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission.
|
||||||
|
userfullname?: string; // Calculated in the app. Full name of the user that did the submission.
|
||||||
|
userprofileimageurl?: string; // Calculated in the app. Avatar of the user that did the submission.
|
||||||
|
manyGroups?: boolean; // Calculated in the app. Whether the user belongs to more than 1 group.
|
||||||
|
noGroups?: boolean; // Calculated in the app. Whether the user doesn't belong to any group.
|
||||||
|
groupname?: string; // Calculated in the app. Name of the group the submission belongs to.
|
||||||
|
};
|
||||||
|
|
|
@ -26,8 +26,8 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||||
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonModAssignProvider } from './assign';
|
import { AddonModAssignProvider, AddonModAssignGetSubmissionStatusResult, AddonModAssignSubmission } from './assign';
|
||||||
import { AddonModAssignHelperProvider } from './helper';
|
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from './helper';
|
||||||
import { AddonModAssignSyncProvider } from './assign-sync';
|
import { AddonModAssignSyncProvider } from './assign-sync';
|
||||||
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
||||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||||
|
@ -106,7 +106,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
if (data.canviewsubmissions) {
|
if (data.canviewsubmissions) {
|
||||||
// Teacher, get all submissions.
|
// Teacher, get all submissions.
|
||||||
return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, false, siteId)
|
return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, false, siteId)
|
||||||
.then((submissions) => {
|
.then((submissions: AddonModAssignSubmissionFormatted[]) => {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
|
@ -161,9 +161,10 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
return this.assignProvider.getSubmissionStatusWithRetry(assign, submitId, undefined, blindMarking, true, false, siteId)
|
return this.assignProvider.getSubmissionStatusWithRetry(assign, submitId, undefined, blindMarking, true, false, siteId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
let userSubmission: AddonModAssignSubmission;
|
||||||
|
|
||||||
if (response.lastattempt) {
|
if (response.lastattempt) {
|
||||||
const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
||||||
if (userSubmission && userSubmission.plugins) {
|
if (userSubmission && userSubmission.plugins) {
|
||||||
// Add submission plugin files.
|
// Add submission plugin files.
|
||||||
userSubmission.plugins.forEach((plugin) => {
|
userSubmission.plugins.forEach((plugin) => {
|
||||||
|
@ -175,7 +176,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
if (response.feedback && response.feedback.plugins) {
|
if (response.feedback && response.feedback.plugins) {
|
||||||
// Add feedback plugin files.
|
// Add feedback plugin files.
|
||||||
response.feedback.plugins.forEach((plugin) => {
|
response.feedback.plugins.forEach((plugin) => {
|
||||||
promises.push(this.feedbackDelegate.getPluginFiles(assign, response, plugin, siteId));
|
promises.push(this.feedbackDelegate.getPluginFiles(assign, userSubmission, plugin, siteId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +304,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
|
|
||||||
groupInfo.groups.forEach((group) => {
|
groupInfo.groups.forEach((group) => {
|
||||||
groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, true, siteId)
|
groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, true, siteId)
|
||||||
.then((submissions) => {
|
.then((submissions: AddonModAssignSubmissionFormatted[]) => {
|
||||||
|
|
||||||
const subPromises = [];
|
const subPromises = [];
|
||||||
|
|
||||||
|
@ -327,7 +328,8 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetch the submission of the current user even if it does not exist, this will be create it.
|
// Prefetch the submission of the current user even if it does not exist, this will be create it.
|
||||||
if (!data.submissions || !data.submissions.find((subm) => subm.submitid == userId)) {
|
if (!data.submissions ||
|
||||||
|
!data.submissions.find((subm: AddonModAssignSubmissionFormatted) => subm.submitid == userId)) {
|
||||||
subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, userId, group.id,
|
subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, userId, group.id,
|
||||||
false, true, true, siteId).then((subm) => {
|
false, true, true, siteId).then((subm) => {
|
||||||
return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId);
|
return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId);
|
||||||
|
@ -385,15 +387,16 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when prefetched, rejected otherwise.
|
* @return Promise resolved when prefetched, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
protected prefetchSubmission(assign: any, courseId: number, moduleId: number, submission: any, userId?: number,
|
protected prefetchSubmission(assign: any, courseId: number, moduleId: number,
|
||||||
siteId?: string): Promise<any> {
|
submission: AddonModAssignGetSubmissionStatusResult, userId?: number, siteId?: string): Promise<any> {
|
||||||
|
|
||||||
const promises = [],
|
const promises = [],
|
||||||
blindMarking = assign.blindmarking && !assign.revealidentities;
|
blindMarking = assign.blindmarking && !assign.revealidentities;
|
||||||
let userIds = [];
|
let userIds = [],
|
||||||
|
userSubmission: AddonModAssignSubmission;
|
||||||
|
|
||||||
if (submission.lastattempt) {
|
if (submission.lastattempt) {
|
||||||
const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, submission.lastattempt);
|
userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, submission.lastattempt);
|
||||||
|
|
||||||
// Get IDs of the members who need to submit.
|
// Get IDs of the members who need to submit.
|
||||||
if (!blindMarking && submission.lastattempt.submissiongroupmemberswhoneedtosubmit) {
|
if (!blindMarking && submission.lastattempt.submissiongroupmemberswhoneedtosubmit) {
|
||||||
|
@ -440,10 +443,10 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
||||||
if (submission.feedback.plugins) {
|
if (submission.feedback.plugins) {
|
||||||
submission.feedback.plugins.forEach((plugin) => {
|
submission.feedback.plugins.forEach((plugin) => {
|
||||||
// Prefetch the plugin WS data.
|
// Prefetch the plugin WS data.
|
||||||
promises.push(this.feedbackDelegate.prefetch(assign, submission, plugin, siteId));
|
promises.push(this.feedbackDelegate.prefetch(assign, userSubmission, plugin, siteId));
|
||||||
|
|
||||||
// Prefetch the plugin files.
|
// Prefetch the plugin files.
|
||||||
promises.push(this.feedbackDelegate.getPluginFiles(assign, submission, plugin, siteId).then((files) => {
|
promises.push(this.feedbackDelegate.getPluginFiles(assign, userSubmission, plugin, siteId).then((files) => {
|
||||||
return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id);
|
return this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
import { AddonModAssignDefaultSubmissionHandler } from './default-submission-handler';
|
import { AddonModAssignDefaultSubmissionHandler } from './default-submission-handler';
|
||||||
|
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that all submission handlers must implement.
|
* Interface that all submission handlers must implement.
|
||||||
|
@ -39,7 +40,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canEditOffline?(assign: any, submission: any, plugin: any): boolean | Promise<boolean>;
|
canEditOffline?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): boolean | Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should clear temporary data for a cancelled submission.
|
* Should clear temporary data for a cancelled submission.
|
||||||
|
@ -49,7 +51,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
*/
|
*/
|
||||||
clearTmpData?(assign: any, submission: any, plugin: any, inputData: any): void;
|
clearTmpData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will be called when the user wants to create a new submission based on the previous one.
|
* This function will be called when the user wants to create a new submission based on the previous one.
|
||||||
|
@ -62,7 +65,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
copySubmissionData?(assign: any, plugin: any, pluginData: any, userId?: number, siteId?: string): void | Promise<any>;
|
copySubmissionData?(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
userId?: number, siteId?: string): void | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete any stored data for the plugin and submission.
|
* Delete any stored data for the plugin and submission.
|
||||||
|
@ -74,7 +78,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
deleteOfflineData?(assign: any, submission: any, plugin: any, offlineData: any, siteId?: string): void | Promise<any>;
|
deleteOfflineData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Component to use to display the plugin data, either in read or in edit mode.
|
* Return the Component to use to display the plugin data, either in read or in edit mode.
|
||||||
|
@ -85,7 +90,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent?(injector: Injector, plugin: any, edit?: boolean): any | Promise<any>;
|
getComponent?(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get files used by this plugin.
|
* Get files used by this plugin.
|
||||||
|
@ -97,7 +102,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles?(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]>;
|
getPluginFiles?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a readable name to use for the plugin.
|
* Get a readable name to use for the plugin.
|
||||||
|
@ -105,7 +111,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The plugin name.
|
* @return The plugin name.
|
||||||
*/
|
*/
|
||||||
getPluginName?(plugin: any): string;
|
getPluginName?(plugin: AddonModAssignPlugin): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size of data (in bytes) this plugin will send to copy a previous submission.
|
* Get the size of data (in bytes) this plugin will send to copy a previous submission.
|
||||||
|
@ -114,7 +120,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForCopy?(assign: any, plugin: any): number | Promise<number>;
|
getSizeForCopy?(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise<number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size of data (in bytes) this plugin will send to add or edit a submission.
|
* Get the size of data (in bytes) this plugin will send to add or edit a submission.
|
||||||
|
@ -125,7 +131,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForEdit?(assign: any, submission: any, plugin: any, inputData: any): number | Promise<number>;
|
getSizeForEdit?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): number | Promise<number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the submission data has changed for this plugin.
|
* Check if the submission data has changed for this plugin.
|
||||||
|
@ -136,7 +143,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged?(assign: any, submission: any, plugin: any, inputData: any): boolean | Promise<boolean>;
|
hasDataChanged?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): boolean | Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the handler is enabled for edit on a site level.
|
* Whether or not the handler is enabled for edit on a site level.
|
||||||
|
@ -155,7 +163,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch?(assign: any, submission: any, plugin: any, siteId?: string): Promise<any>;
|
prefetch?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare and add to pluginData the data to send to the server based on the input data.
|
* Prepare and add to pluginData the data to send to the server based on the input data.
|
||||||
|
@ -170,7 +179,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSubmissionData?(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
prepareSubmissionData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean,
|
||||||
userId?: number, siteId?: string): void | Promise<any>;
|
userId?: number, siteId?: string): void | Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,8 +195,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSyncData?(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
prepareSyncData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
: void | Promise<any>;
|
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -210,7 +220,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Promise resolved with boolean: whether it can be edited in offline.
|
* @return Promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canPluginEditOffline(assign: any, submission: any, plugin: any): Promise<boolean> {
|
canPluginEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): Promise<boolean> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'canEditOffline', [assign, submission, plugin]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'canEditOffline', [assign, submission, plugin]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +233,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
*/
|
*/
|
||||||
clearTmpData(assign: any, submission: any, plugin: any, inputData: any): void {
|
clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): void {
|
||||||
return this.executeFunctionOnEnabled(plugin.type, 'clearTmpData', [assign, submission, plugin, inputData]);
|
return this.executeFunctionOnEnabled(plugin.type, 'clearTmpData', [assign, submission, plugin, inputData]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +248,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the data has been copied.
|
* @return Promise resolved when the data has been copied.
|
||||||
*/
|
*/
|
||||||
copyPluginSubmissionData(assign: any, plugin: any, pluginData: any, userId?: number, siteId?: string): void | Promise<any> {
|
copyPluginSubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'copySubmissionData',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'copySubmissionData',
|
||||||
[assign, plugin, pluginData, userId, siteId]));
|
[assign, plugin, pluginData, userId, siteId]));
|
||||||
}
|
}
|
||||||
|
@ -251,7 +264,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
deletePluginOfflineData(assign: any, submission: any, plugin: any, offlineData: any, siteId?: string): Promise<any> {
|
deletePluginOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'deleteOfflineData',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'deleteOfflineData',
|
||||||
[assign, submission, plugin, offlineData, siteId]));
|
[assign, submission, plugin, offlineData, siteId]));
|
||||||
}
|
}
|
||||||
|
@ -264,7 +278,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return Promise resolved with the component to use, undefined if not found.
|
* @return Promise resolved with the component to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponentForPlugin(injector: Injector, plugin: any, edit?: boolean): Promise<any> {
|
getComponentForPlugin(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin, edit]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getComponent', [injector, plugin, edit]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +292,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with the files.
|
* @return Promise resolved with the files.
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any[]> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getPluginFiles', [assign, submission, plugin, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +303,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param plugin Plugin to get the name for.
|
* @param plugin Plugin to get the name for.
|
||||||
* @return Human readable name.
|
* @return Human readable name.
|
||||||
*/
|
*/
|
||||||
getPluginName(plugin: any): string {
|
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||||
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +314,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Promise resolved with size.
|
* @return Promise resolved with size.
|
||||||
*/
|
*/
|
||||||
getPluginSizeForCopy(assign: any, plugin: any): Promise<number> {
|
getPluginSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): Promise<number> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForCopy', [assign, plugin]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForCopy', [assign, plugin]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +327,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Promise resolved with size.
|
* @return Promise resolved with size.
|
||||||
*/
|
*/
|
||||||
getPluginSizeForEdit(assign: any, submission: any, plugin: any, inputData: any): Promise<number> {
|
getPluginSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): Promise<number> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForEdit',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'getSizeForEdit',
|
||||||
[assign, submission, plugin, inputData]));
|
[assign, submission, plugin, inputData]));
|
||||||
}
|
}
|
||||||
|
@ -326,7 +342,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
* @return Promise resolved with true if data has changed, resolved with false otherwise.
|
||||||
*/
|
*/
|
||||||
hasPluginDataChanged(assign: any, submission: any, plugin: any, inputData: any): Promise<boolean> {
|
hasPluginDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): Promise<boolean> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'hasDataChanged',
|
||||||
[assign, submission, plugin, inputData]));
|
[assign, submission, plugin, inputData]));
|
||||||
}
|
}
|
||||||
|
@ -360,7 +377,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch(assign: any, submission: any, plugin: any, siteId?: string): Promise<any> {
|
prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]));
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prefetch', [assign, submission, plugin, siteId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,8 +395,9 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when data has been gathered.
|
* @return Promise resolved when data has been gathered.
|
||||||
*/
|
*/
|
||||||
preparePluginSubmissionData(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
preparePluginSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
userId?: number, siteId?: string): Promise<any> {
|
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, userId?: number,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSubmissionData',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSubmissionData',
|
||||||
[assign, submission, plugin, inputData, pluginData, offline, userId, siteId]));
|
[assign, submission, plugin, inputData, pluginData, offline, userId, siteId]));
|
||||||
|
@ -395,8 +414,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when data has been gathered.
|
* @return Promise resolved when data has been gathered.
|
||||||
*/
|
*/
|
||||||
preparePluginSyncData(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
preparePluginSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
: Promise<any> {
|
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): Promise<any> {
|
||||||
|
|
||||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSyncData',
|
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSyncData',
|
||||||
[assign, submission, plugin, offlineData, pluginData, siteId]));
|
[assign, submission, plugin, offlineData, pluginData, siteId]));
|
||||||
|
|
|
@ -17,6 +17,9 @@ import { Injectable, Injector } from '@angular/core';
|
||||||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||||
import { AddonModAssignSubmissionCommentsComponent } from '../component/comments';
|
import { AddonModAssignSubmissionCommentsComponent } from '../component/comments';
|
||||||
|
import {
|
||||||
|
AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for comments submission plugin.
|
* Handler for comments submission plugin.
|
||||||
|
@ -38,7 +41,8 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canEditOffline(assign: any, submission: any, plugin: any): boolean | Promise<boolean> {
|
canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): boolean | Promise<boolean> {
|
||||||
// This plugin is read only, but return true to prevent blocking the edition.
|
// This plugin is read only, but return true to prevent blocking the edition.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +56,7 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any, edit?: boolean): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise<any> {
|
||||||
return edit ? undefined : AddonModAssignSubmissionCommentsComponent;
|
return edit ? undefined : AddonModAssignSubmissionCommentsComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +88,9 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prefetch(assign: any, submission: any, plugin: any, siteId?: string): Promise<any> {
|
prefetch(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): Promise<any> {
|
||||||
|
|
||||||
return this.commentsProvider.getComments('module', assign.cmid, 'assignsubmission_comments', submission.id,
|
return this.commentsProvider.getComments('module', assign.cmid, 'assignsubmission_comments', submission.id,
|
||||||
'submission_comments', 0, siteId).catch(() => {
|
'submission_comments', 0, siteId).catch(() => {
|
||||||
// Fail silently (Moodle < 3.1.1, 3.2)
|
// Fail silently (Moodle < 3.1.1, 3.2)
|
||||||
|
|
|
@ -21,7 +21,9 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreWSProvider } from '@providers/ws';
|
import { CoreWSProvider } from '@providers/ws';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
||||||
import { AddonModAssignHelperProvider } from '../../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../../providers/helper';
|
||||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||||
|
@ -53,7 +55,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canEditOffline(assign: any, submission: any, plugin: any): boolean | Promise<boolean> {
|
canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): boolean | Promise<boolean> {
|
||||||
// This plugin doesn't use Moodle filters, it can be edited in offline.
|
// This plugin doesn't use Moodle filters, it can be edited in offline.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +69,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
*/
|
*/
|
||||||
clearTmpData(assign: any, submission: any, plugin: any, inputData: any): void {
|
clearTmpData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): void {
|
||||||
const files = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
|
const files = this.fileSessionProvider.getFiles(AddonModAssignProvider.COMPONENT, assign.id);
|
||||||
|
|
||||||
// Clear the files in session for this assign.
|
// Clear the files in session for this assign.
|
||||||
|
@ -87,7 +91,9 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
copySubmissionData(assign: any, plugin: any, pluginData: any, userId?: number, siteId?: string): void | Promise<any> {
|
copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
// We need to re-upload all the existing files.
|
// We need to re-upload all the existing files.
|
||||||
const files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
const files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
|
|
||||||
|
@ -105,7 +111,7 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any, edit?: boolean): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise<any> {
|
||||||
return AddonModAssignSubmissionFileComponent;
|
return AddonModAssignSubmissionFileComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +125,9 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
deleteOfflineData(assign: any, submission: any, plugin: any, offlineData: any, siteId?: string): void | Promise<any> {
|
deleteOfflineData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, offlineData: any, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
return this.assignHelper.deleteStoredSubmissionFiles(assign.id, AddonModAssignSubmissionFileHandler.FOLDER_NAME,
|
return this.assignHelper.deleteStoredSubmissionFiles(assign.id, AddonModAssignSubmissionFileHandler.FOLDER_NAME,
|
||||||
submission.userid, siteId).catch(() => {
|
submission.userid, siteId).catch(() => {
|
||||||
// Ignore errors, maybe the folder doesn't exist.
|
// Ignore errors, maybe the folder doesn't exist.
|
||||||
|
@ -136,7 +144,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +156,7 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForCopy(assign: any, plugin: any): number | Promise<number> {
|
getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise<number> {
|
||||||
const files = this.assignProvider.getSubmissionPluginAttachments(plugin),
|
const files = this.assignProvider.getSubmissionPluginAttachments(plugin),
|
||||||
promises = [];
|
promises = [];
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
@ -177,7 +186,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForEdit(assign: any, submission: any, plugin: any, inputData: any): number | Promise<number> {
|
getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): number | Promise<number> {
|
||||||
const siteId = this.sitesProvider.getCurrentSiteId();
|
const siteId = this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
// Check if there's any change.
|
// Check if there's any change.
|
||||||
|
@ -232,7 +242,9 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged(assign: any, submission: any, plugin: any, inputData: any): boolean | Promise<boolean> {
|
hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): boolean | Promise<boolean> {
|
||||||
|
|
||||||
// Check if there's any offline data.
|
// Check if there's any offline data.
|
||||||
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
||||||
// No offline data found.
|
// No offline data found.
|
||||||
|
@ -299,7 +311,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSubmissionData(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean,
|
||||||
userId?: number, siteId?: string): void | Promise<any> {
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
if (this.hasDataChanged(assign, submission, plugin, inputData)) {
|
if (this.hasDataChanged(assign, submission, plugin, inputData)) {
|
||||||
|
@ -330,8 +343,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSyncData(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
: void | Promise<any> {
|
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
const filesData = offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager;
|
const filesData = offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager;
|
||||||
if (filesData) {
|
if (filesData) {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<div *ngIf="edit && loaded">
|
<div *ngIf="edit && loaded">
|
||||||
<ion-item-divider text-wrap>{{ plugin.name }}</ion-item-divider>
|
<ion-item-divider text-wrap>{{ plugin.name }}</ion-item-divider>
|
||||||
<ion-item text-wrap *ngIf="configs.wordlimitenabled && words >= 0">
|
<ion-item text-wrap *ngIf="wordLimitEnabled && words >= 0">
|
||||||
<h2>{{ 'addon.mod_assign.wordlimit' | translate }}</h2>
|
<h2>{{ 'addon.mod_assign.wordlimit' | translate }}</h2>
|
||||||
<p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}</p>
|
<p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}</p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -34,6 +34,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
||||||
component = AddonModAssignProvider.COMPONENT;
|
component = AddonModAssignProvider.COMPONENT;
|
||||||
text: string;
|
text: string;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
|
wordLimitEnabled: boolean;
|
||||||
|
|
||||||
protected wordCountTimeout: any;
|
protected wordCountTimeout: any;
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
|
@ -61,9 +62,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
||||||
// No offline data found, return online text.
|
// No offline data found, return online text.
|
||||||
return this.assignProvider.getSubmissionPluginText(this.plugin);
|
return this.assignProvider.getSubmissionPluginText(this.plugin);
|
||||||
}).then((text) => {
|
}).then((text) => {
|
||||||
// We receive them as strings, convert to int.
|
this.wordLimitEnabled = !!parseInt(this.configs.wordlimitenabled, 10);
|
||||||
this.configs.wordlimit = parseInt(this.configs.wordlimit, 10);
|
|
||||||
this.configs.wordlimitenabled = parseInt(this.configs.wordlimitenabled, 10);
|
|
||||||
|
|
||||||
// Set the text.
|
// Set the text.
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
@ -85,7 +84,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate initial words.
|
// Calculate initial words.
|
||||||
if (this.configs.wordlimitenabled) {
|
if (this.wordLimitEnabled) {
|
||||||
this.words = this.textUtils.countWords(text);
|
this.words = this.textUtils.countWords(text);
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
@ -100,7 +99,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
||||||
*/
|
*/
|
||||||
onChange(text: string): void {
|
onChange(text: string): void {
|
||||||
// Count words if needed.
|
// Count words if needed.
|
||||||
if (this.configs.wordlimitenabled) {
|
if (this.wordLimitEnabled) {
|
||||||
// Cancel previous wait.
|
// Cancel previous wait.
|
||||||
clearTimeout(this.wordCountTimeout);
|
clearTimeout(this.wordCountTimeout);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@ import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreWSProvider } from '@providers/ws';
|
import { CoreWSProvider } from '@providers/ws';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonModAssignProvider } from '../../../providers/assign';
|
import {
|
||||||
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||||
|
} from '../../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../../providers/assign-offline';
|
||||||
import { AddonModAssignHelperProvider } from '../../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../../providers/helper';
|
||||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||||
|
@ -46,7 +48,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
* @return Boolean or promise resolved with boolean: whether it can be edited in offline.
|
||||||
*/
|
*/
|
||||||
canEditOffline(assign: any, submission: any, plugin: any): boolean | Promise<boolean> {
|
canEditOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin): boolean | Promise<boolean> {
|
||||||
// This plugin uses Moodle filters, it cannot be edited in offline.
|
// This plugin uses Moodle filters, it cannot be edited in offline.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +65,9 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
copySubmissionData(assign: any, plugin: any, pluginData: any, userId?: number, siteId?: string): void | Promise<any> {
|
copySubmissionData(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin, pluginData: any,
|
||||||
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
const text = this.assignProvider.getSubmissionPluginText(plugin, true),
|
const text = this.assignProvider.getSubmissionPluginText(plugin, true),
|
||||||
files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
let promise;
|
let promise;
|
||||||
|
@ -93,7 +98,7 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param edit Whether the user is editing.
|
* @param edit Whether the user is editing.
|
||||||
* @return The component (or promise resolved with component) to use, undefined if not found.
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getComponent(injector: Injector, plugin: any, edit?: boolean): any | Promise<any> {
|
getComponent(injector: Injector, plugin: AddonModAssignPlugin, edit?: boolean): any | Promise<any> {
|
||||||
return AddonModAssignSubmissionOnlineTextComponent;
|
return AddonModAssignSubmissionOnlineTextComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +112,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return The files (or promise resolved with the files).
|
* @return The files (or promise resolved with the files).
|
||||||
*/
|
*/
|
||||||
getPluginFiles(assign: any, submission: any, plugin: any, siteId?: string): any[] | Promise<any[]> {
|
getPluginFiles(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, siteId?: string): any[] | Promise<any[]> {
|
||||||
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
return this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +124,7 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param plugin The plugin object.
|
* @param plugin The plugin object.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForCopy(assign: any, plugin: any): number | Promise<number> {
|
getSizeForCopy(assign: AddonModAssignAssign, plugin: AddonModAssignPlugin): number | Promise<number> {
|
||||||
const text = this.assignProvider.getSubmissionPluginText(plugin, true),
|
const text = this.assignProvider.getSubmissionPluginText(plugin, true),
|
||||||
files = this.assignProvider.getSubmissionPluginAttachments(plugin),
|
files = this.assignProvider.getSubmissionPluginAttachments(plugin),
|
||||||
promises = [];
|
promises = [];
|
||||||
|
@ -153,7 +159,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return The size (or promise resolved with size).
|
* @return The size (or promise resolved with size).
|
||||||
*/
|
*/
|
||||||
getSizeForEdit(assign: any, submission: any, plugin: any, inputData: any): number | Promise<number> {
|
getSizeForEdit(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): number | Promise<number> {
|
||||||
const text = this.assignProvider.getSubmissionPluginText(plugin, true);
|
const text = this.assignProvider.getSubmissionPluginText(plugin, true);
|
||||||
|
|
||||||
return text.length;
|
return text.length;
|
||||||
|
@ -182,7 +189,9 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param inputData Data entered by the user for the submission.
|
* @param inputData Data entered by the user for the submission.
|
||||||
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
* @return Boolean (or promise resolved with boolean): whether the data has changed.
|
||||||
*/
|
*/
|
||||||
hasDataChanged(assign: any, submission: any, plugin: any, inputData: any): boolean | Promise<boolean> {
|
hasDataChanged(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any): boolean | Promise<boolean> {
|
||||||
|
|
||||||
// Get the original text from plugin or offline.
|
// Get the original text from plugin or offline.
|
||||||
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
||||||
// No offline data found.
|
// No offline data found.
|
||||||
|
@ -234,7 +243,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSubmissionData(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
prepareSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
|
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean,
|
||||||
userId?: number, siteId?: string): void | Promise<any> {
|
userId?: number, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
let text = this.getTextToSubmit(plugin, inputData);
|
let text = this.getTextToSubmit(plugin, inputData);
|
||||||
|
@ -274,8 +284,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return If the function is async, it should return a Promise resolved when done.
|
* @return If the function is async, it should return a Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
prepareSyncData(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||||
: void | Promise<any> {
|
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||||
|
|
||||||
const textData = offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor;
|
const textData = offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor;
|
||||||
if (textData) {
|
if (textData) {
|
||||||
|
|
|
@ -121,7 +121,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
// Try to get the book data.
|
// Try to get the book data.
|
||||||
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
|
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
|
||||||
this.dataRetrieved.emit(book);
|
this.dataRetrieved.emit(book);
|
||||||
this.description = book.intro || this.description;
|
this.description = book.intro;
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors since this WS isn't available in some Moodle versions.
|
// Ignore errors since this WS isn't available in some Moodle versions.
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -25,38 +25,7 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
import { CoreTagItem } from '@core/tag/providers/tag';
|
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
/**
|
|
||||||
* A book chapter inside the toc list.
|
|
||||||
*/
|
|
||||||
export interface AddonModBookTocChapter {
|
|
||||||
/**
|
|
||||||
* ID to identify the chapter.
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chapter's title.
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The chapter's level.
|
|
||||||
*/
|
|
||||||
level: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of book contents. For each chapter it has its index URL and the list of paths of the files the chapter has. Each path
|
|
||||||
* is identified by the relative path in the book, and the value is the URL of the file.
|
|
||||||
*/
|
|
||||||
export type AddonModBookContentsMap = {
|
|
||||||
[chapter: string]: {
|
|
||||||
indexUrl?: string,
|
|
||||||
paths: {[path: string]: string},
|
|
||||||
tags?: CoreTagItem[]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for books.
|
* Service that provides some features for books.
|
||||||
|
@ -83,7 +52,7 @@ export class AddonModBookProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the book is retrieved.
|
* @return Promise resolved when the book is retrieved.
|
||||||
*/
|
*/
|
||||||
getBook(courseId: number, cmId: number, siteId?: string): Promise<any> {
|
getBook(courseId: number, cmId: number, siteId?: string): Promise<AddonModBookBook> {
|
||||||
return this.getBookByField(courseId, 'coursemodule', cmId, siteId);
|
return this.getBookByField(courseId, 'coursemodule', cmId, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +65,7 @@ export class AddonModBookProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the book is retrieved.
|
* @return Promise resolved when the book is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getBookByField(courseId: number, key: string, value: any, siteId?: string): Promise<any> {
|
protected getBookByField(courseId: number, key: string, value: any, siteId?: string): Promise<AddonModBookBook> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
courseids: [courseId]
|
courseids: [courseId]
|
||||||
|
@ -106,7 +75,9 @@ export class AddonModBookProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('mod_book_get_books_by_courses', params, preSets).then((response) => {
|
return site.read('mod_book_get_books_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModBookGetBooksByCoursesResult): any => {
|
||||||
|
|
||||||
// Search the book.
|
// Search the book.
|
||||||
if (response && response.books) {
|
if (response && response.books) {
|
||||||
for (const i in response.books) {
|
for (const i in response.books) {
|
||||||
|
@ -401,3 +372,66 @@ export class AddonModBookProvider {
|
||||||
{chapterid: chapterId}, siteId);
|
{chapterid: chapterId}, siteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A book chapter inside the toc list.
|
||||||
|
*/
|
||||||
|
export type AddonModBookTocChapter = {
|
||||||
|
/**
|
||||||
|
* ID to identify the chapter.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chapter's title.
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The chapter's level.
|
||||||
|
*/
|
||||||
|
level: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of book contents. For each chapter it has its index URL and the list of paths of the files the chapter has. Each path
|
||||||
|
* is identified by the relative path in the book, and the value is the URL of the file.
|
||||||
|
*/
|
||||||
|
export type AddonModBookContentsMap = {
|
||||||
|
[chapter: string]: {
|
||||||
|
indexUrl?: string,
|
||||||
|
paths: {[path: string]: string},
|
||||||
|
tags?: CoreTagItem[]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Book returned by mod_book_get_books_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModBookBook = {
|
||||||
|
id: number; // Book id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Book name.
|
||||||
|
intro: string; // The Book intro.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
numbering: number; // Book numbering configuration.
|
||||||
|
navstyle: number; // Book navigation style configuration.
|
||||||
|
customtitles: number; // Book custom titles type.
|
||||||
|
revision?: number; // Book revision.
|
||||||
|
timecreated?: number; // Time of creation.
|
||||||
|
timemodified?: number; // Time of last modification.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: boolean; // Visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_book_get_books_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModBookGetBooksByCoursesResult = {
|
||||||
|
books: AddonModBookBook[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Injector } from '@angular/core';
|
||||||
import { NavController } from 'ionic-angular';
|
import { NavController } from 'ionic-angular';
|
||||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { AddonModChatProvider } from '../../providers/chat';
|
import { AddonModChatProvider, AddonModChatChat } from '../../providers/chat';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a chat.
|
* Component that displays a chat.
|
||||||
|
@ -29,7 +29,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
component = AddonModChatProvider.COMPONENT;
|
component = AddonModChatProvider.COMPONENT;
|
||||||
moduleName = 'chat';
|
moduleName = 'chat';
|
||||||
|
|
||||||
chat: any;
|
chat: AddonModChatChat;
|
||||||
chatInfo: any;
|
chatInfo: any;
|
||||||
|
|
||||||
protected title: string;
|
protected title: string;
|
||||||
|
@ -66,7 +66,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||||
return this.chatProvider.getChat(this.courseId, this.module.id).then((chat) => {
|
return this.chatProvider.getChat(this.courseId, this.module.id).then((chat) => {
|
||||||
this.chat = chat;
|
this.chat = chat;
|
||||||
this.description = chat.intro || chat.description;
|
this.description = chat.intro;
|
||||||
|
|
||||||
const now = this.timeUtils.timestamp();
|
const now = this.timeUtils.timestamp();
|
||||||
const span = chat.chattime - now;
|
const span = chat.chattime - now;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonModChatProvider } from '../../providers/chat';
|
import { AddonModChatProvider, AddonModChatMessageWithUserData } from '../../providers/chat';
|
||||||
import { Network } from '@ionic-native/network';
|
import { Network } from '@ionic-native/network';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export class AddonModChatChatPage {
|
||||||
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
title: string;
|
title: string;
|
||||||
messages = [];
|
messages: AddonModChatMessageWithUserData[] = [];
|
||||||
newMessage: string;
|
newMessage: string;
|
||||||
polling: any;
|
polling: any;
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
|
@ -46,7 +46,7 @@ export class AddonModChatChatPage {
|
||||||
protected logger;
|
protected logger;
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected chatId: number;
|
protected chatId: number;
|
||||||
protected sessionId: number;
|
protected sessionId: string;
|
||||||
protected lastTime = 0;
|
protected lastTime = 0;
|
||||||
protected oldContentHeight = 0;
|
protected oldContentHeight = 0;
|
||||||
protected onlineObserver: any;
|
protected onlineObserver: any;
|
||||||
|
@ -131,9 +131,9 @@ export class AddonModChatChatPage {
|
||||||
/**
|
/**
|
||||||
* Convenience function to login the user.
|
* Convenience function to login the user.
|
||||||
*
|
*
|
||||||
* @return Resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected loginUser(): Promise<any> {
|
protected loginUser(): Promise<void> {
|
||||||
return this.chatProvider.loginUser(this.chatId).then((sessionId) => {
|
return this.chatProvider.loginUser(this.chatId).then((sessionId) => {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
});
|
});
|
||||||
|
@ -144,12 +144,12 @@ export class AddonModChatChatPage {
|
||||||
*
|
*
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchMessages(): Promise<any> {
|
protected fetchMessages(): Promise<void> {
|
||||||
return this.chatProvider.getLatestMessages(this.sessionId, this.lastTime).then((messagesInfo) => {
|
return this.chatProvider.getLatestMessages(this.sessionId, this.lastTime).then((messagesInfo) => {
|
||||||
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
||||||
|
|
||||||
return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => {
|
return this.chatProvider.getMessagesUserData(messagesInfo.messages, this.courseId).then((messages) => {
|
||||||
this.messages = this.messages.concat(messages);
|
this.messages = this.messages.concat(<AddonModChatMessageWithUserData[]> messages);
|
||||||
if (messages.length) {
|
if (messages.length) {
|
||||||
// New messages or beeps, scroll to bottom.
|
// New messages or beeps, scroll to bottom.
|
||||||
setTimeout(() => this.scrollToBottom());
|
setTimeout(() => this.scrollToBottom());
|
||||||
|
@ -190,7 +190,7 @@ export class AddonModChatChatPage {
|
||||||
*
|
*
|
||||||
* @return Promised resolved when done.
|
* @return Promised resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchMessagesInterval(): Promise<any> {
|
protected fetchMessagesInterval(): Promise<void> {
|
||||||
this.logger.debug('Polling for messages');
|
this.logger.debug('Polling for messages');
|
||||||
if (!this.isOnline || this.pollingRunning) {
|
if (!this.isOnline || this.pollingRunning) {
|
||||||
// Obviously we cannot check for new messages when the app is offline.
|
// Obviously we cannot check for new messages when the app is offline.
|
||||||
|
@ -225,7 +225,7 @@ export class AddonModChatChatPage {
|
||||||
* @param prevMessage Previous message object.
|
* @param prevMessage Previous message object.
|
||||||
* @return True if messages are from diferent days, false othetwise.
|
* @return True if messages are from diferent days, false othetwise.
|
||||||
*/
|
*/
|
||||||
showDate(message: any, prevMessage: any): boolean {
|
showDate(message: AddonModChatMessageWithUserData, prevMessage: AddonModChatMessageWithUserData): boolean {
|
||||||
if (!prevMessage) {
|
if (!prevMessage) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ export class AddonModChatChatPage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnect(): Promise<any> {
|
reconnect(): Promise<void> {
|
||||||
const modal = this.domUtils.showModalLoading();
|
const modal = this.domUtils.showModalLoading();
|
||||||
|
|
||||||
// Call startPolling would take a while for the first execution, so we'll execute it manually to check if it works now.
|
// Call startPolling would take a while for the first execution, so we'll execute it manually to check if it works now.
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModChatProvider } from '../../providers/chat';
|
import { AddonModChatProvider, AddonModChatSessionMessageWithUserData } from '../../providers/chat';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,7 @@ export class AddonModChatSessionMessagesPage {
|
||||||
protected sessionEnd: number;
|
protected sessionEnd: number;
|
||||||
protected groupId: number;
|
protected groupId: number;
|
||||||
protected loaded = false;
|
protected loaded = false;
|
||||||
protected messages = [];
|
protected messages: AddonModChatSessionMessageWithUserData[] = [];
|
||||||
|
|
||||||
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) {
|
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) {
|
||||||
this.courseId = navParams.get('courseId');
|
this.courseId = navParams.get('courseId');
|
||||||
|
@ -55,7 +55,7 @@ export class AddonModChatSessionMessagesPage {
|
||||||
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
|
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
|
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
|
||||||
this.messages = messages;
|
this.messages = <AddonModChatSessionMessageWithUserData[]> messages;
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
||||||
|
@ -84,7 +84,7 @@ export class AddonModChatSessionMessagesPage {
|
||||||
* @param prevMessage Previous message object.
|
* @param prevMessage Previous message object.
|
||||||
* @return True if messages are from diferent days, false othetwise.
|
* @return True if messages are from diferent days, false othetwise.
|
||||||
*/
|
*/
|
||||||
showDate(message: any, prevMessage: any): boolean {
|
showDate(message: AddonModChatSessionMessageWithUserData, prevMessage: AddonModChatSessionMessageWithUserData): boolean {
|
||||||
if (!prevMessage) {
|
if (!prevMessage) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { AddonModChatProvider } from '../../providers/chat';
|
import { AddonModChatProvider, AddonModChatSession, AddonModChatSessionUser } from '../../providers/chat';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays list of chat sessions.
|
* Page that displays list of chat sessions.
|
||||||
|
@ -73,13 +73,13 @@ export class AddonModChatSessionsPage {
|
||||||
this.groupId = this.groupsProvider.validateGroupId(this.groupId, groupInfo);
|
this.groupId = this.groupsProvider.validateGroupId(this.groupId, groupInfo);
|
||||||
|
|
||||||
return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll);
|
return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll);
|
||||||
}).then((sessions) => {
|
}).then((sessions: AddonModChatSessionFormatted[]) => {
|
||||||
// Fetch user profiles.
|
// Fetch user profiles.
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
sessions.forEach((session) => {
|
sessions.forEach((session) => {
|
||||||
session.duration = session.sessionend - session.sessionstart;
|
session.duration = session.sessionend - session.sessionstart;
|
||||||
session.sessionusers.forEach((sessionUser) => {
|
session.sessionusers.forEach((sessionUser: AddonModChatUserSessionFormatted) => {
|
||||||
if (!sessionUser.userfullname) {
|
if (!sessionUser.userfullname) {
|
||||||
// The WS does not return the user name, fetch user profile.
|
// The WS does not return the user name, fetch user profile.
|
||||||
promises.push(this.userProvider.getProfile(sessionUser.userid, this.courseId, true).then((user) => {
|
promises.push(this.userProvider.getProfile(sessionUser.userid, this.courseId, true).then((user) => {
|
||||||
|
@ -156,3 +156,18 @@ export class AddonModChatSessionsPage {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields added to chat session in this view.
|
||||||
|
*/
|
||||||
|
type AddonModChatSessionFormatted = AddonModChatSession & {
|
||||||
|
duration?: number; // Session duration.
|
||||||
|
allsessionusers?: AddonModChatUserSessionFormatted[]; // All session users.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields added to user session in this view.
|
||||||
|
*/
|
||||||
|
type AddonModChatUserSessionFormatted = AddonModChatSessionUser & {
|
||||||
|
userfullname?: string; // User full name.
|
||||||
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { AddonModChatProvider } from '../../providers/chat';
|
import { AddonModChatProvider, AddonModChatUser } from '../../providers/chat';
|
||||||
import { Network } from '@ionic-native/network';
|
import { Network } from '@ionic-native/network';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,12 +30,12 @@ import { Network } from '@ionic-native/network';
|
||||||
})
|
})
|
||||||
export class AddonModChatUsersPage {
|
export class AddonModChatUsersPage {
|
||||||
|
|
||||||
users = [];
|
users: AddonModChatUser[] = [];
|
||||||
usersLoaded = false;
|
usersLoaded = false;
|
||||||
currentUserId: number;
|
currentUserId: number;
|
||||||
isOnline: boolean;
|
isOnline: boolean;
|
||||||
|
|
||||||
protected sessionId: number;
|
protected sessionId: string;
|
||||||
protected onlineObserver: any;
|
protected onlineObserver: any;
|
||||||
|
|
||||||
constructor(navParams: NavParams, network: Network, zone: NgZone, private appProvider: CoreAppProvider,
|
constructor(navParams: NavParams, network: Network, zone: NgZone, private appProvider: CoreAppProvider,
|
||||||
|
@ -77,7 +77,7 @@ export class AddonModChatUsersPage {
|
||||||
*
|
*
|
||||||
* @param user User object.
|
* @param user User object.
|
||||||
*/
|
*/
|
||||||
talkTo(user: any): void {
|
talkTo(user: AddonModChatUser): void {
|
||||||
this.viewCtrl.dismiss({talkTo: user.fullname});
|
this.viewCtrl.dismiss({talkTo: user.fullname});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ export class AddonModChatUsersPage {
|
||||||
*
|
*
|
||||||
* @param user User object.
|
* @param user User object.
|
||||||
*/
|
*/
|
||||||
beepTo(user: any): void {
|
beepTo(user: AddonModChatUser): void {
|
||||||
this.viewCtrl.dismiss({beepTo: user.id});
|
this.viewCtrl.dismiss({beepTo: user.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for chats.
|
* Service that provides some features for chats.
|
||||||
|
@ -41,7 +42,7 @@ export class AddonModChatProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the chat is retrieved.
|
* @return Promise resolved when the chat is retrieved.
|
||||||
*/
|
*/
|
||||||
getChat(courseId: number, cmId: number, siteId?: string): Promise<any> {
|
getChat(courseId: number, cmId: number, siteId?: string): Promise<AddonModChatChat> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
courseids: [courseId]
|
courseids: [courseId]
|
||||||
|
@ -51,7 +52,9 @@ export class AddonModChatProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('mod_chat_get_chats_by_courses', params, preSets).then((response) => {
|
return site.read('mod_chat_get_chats_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModChatGetChatsByCoursesResult): any => {
|
||||||
|
|
||||||
if (response.chats) {
|
if (response.chats) {
|
||||||
const chat = response.chats.find((chat) => chat.coursemodule == cmId);
|
const chat = response.chats.find((chat) => chat.coursemodule == cmId);
|
||||||
if (chat) {
|
if (chat) {
|
||||||
|
@ -70,12 +73,14 @@ export class AddonModChatProvider {
|
||||||
* @param chatId Chat instance ID.
|
* @param chatId Chat instance ID.
|
||||||
* @return Promise resolved when the WS is executed.
|
* @return Promise resolved when the WS is executed.
|
||||||
*/
|
*/
|
||||||
loginUser(chatId: number): Promise<any> {
|
loginUser(chatId: number): Promise<string> {
|
||||||
const params = {
|
const params = {
|
||||||
chatid: chatId
|
chatid: chatId
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.sitesProvider.getCurrentSite().write('mod_chat_login_user', params).then((response) => {
|
return this.sitesProvider.getCurrentSite().write('mod_chat_login_user', params)
|
||||||
|
.then((response: AddonModChatLoginUserResult): any => {
|
||||||
|
|
||||||
if (response.chatsid) {
|
if (response.chatsid) {
|
||||||
return response.chatsid;
|
return response.chatsid;
|
||||||
}
|
}
|
||||||
|
@ -108,14 +113,16 @@ export class AddonModChatProvider {
|
||||||
* @param beepUserId Beep user ID.
|
* @param beepUserId Beep user ID.
|
||||||
* @return Promise resolved when the WS is executed.
|
* @return Promise resolved when the WS is executed.
|
||||||
*/
|
*/
|
||||||
sendMessage(sessionId: number, message: string, beepUserId: number): Promise<any> {
|
sendMessage(sessionId: string, message: string, beepUserId: number): Promise<number> {
|
||||||
const params = {
|
const params = {
|
||||||
chatsid: sessionId,
|
chatsid: sessionId,
|
||||||
messagetext: message,
|
messagetext: message,
|
||||||
beepid: beepUserId
|
beepid: beepUserId
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.sitesProvider.getCurrentSite().write('mod_chat_send_chat_message', params).then((response) => {
|
return this.sitesProvider.getCurrentSite().write('mod_chat_send_chat_message', params)
|
||||||
|
.then((response: AddonModChatSendChatMessageResult): any => {
|
||||||
|
|
||||||
if (response.messageid) {
|
if (response.messageid) {
|
||||||
return response.messageid;
|
return response.messageid;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +138,7 @@ export class AddonModChatProvider {
|
||||||
* @param lastTime Last time when messages were retrieved.
|
* @param lastTime Last time when messages were retrieved.
|
||||||
* @return Promise resolved when the WS is executed.
|
* @return Promise resolved when the WS is executed.
|
||||||
*/
|
*/
|
||||||
getLatestMessages(sessionId: number, lastTime: number): Promise<any> {
|
getLatestMessages(sessionId: string, lastTime: number): Promise<AddonModChatGetChatLatestMessagesResult> {
|
||||||
const params = {
|
const params = {
|
||||||
chatsid: sessionId,
|
chatsid: sessionId,
|
||||||
chatlasttime: lastTime
|
chatlasttime: lastTime
|
||||||
|
@ -149,8 +156,10 @@ export class AddonModChatProvider {
|
||||||
* @param courseId ID of the course the messages belong to.
|
* @param courseId ID of the course the messages belong to.
|
||||||
* @return Promise always resolved with the formatted messages.
|
* @return Promise always resolved with the formatted messages.
|
||||||
*/
|
*/
|
||||||
getMessagesUserData(messages: any[], courseId: number): Promise<any> {
|
getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number)
|
||||||
const promises = messages.map((message) => {
|
: Promise<(AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData)[]> {
|
||||||
|
|
||||||
|
const promises = messages.map((message: AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData) => {
|
||||||
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
||||||
message.userfullname = user.fullname;
|
message.userfullname = user.fullname;
|
||||||
message.userprofileimageurl = user.profileimageurl;
|
message.userprofileimageurl = user.profileimageurl;
|
||||||
|
@ -171,7 +180,7 @@ export class AddonModChatProvider {
|
||||||
* @param sessionId Chat sessiond ID.
|
* @param sessionId Chat sessiond ID.
|
||||||
* @return Promise resolved when the WS is executed.
|
* @return Promise resolved when the WS is executed.
|
||||||
*/
|
*/
|
||||||
getChatUsers(sessionId: number): Promise<any> {
|
getChatUsers(sessionId: string): Promise<AddonModChatGetChatUsersResult> {
|
||||||
const params = {
|
const params = {
|
||||||
chatsid: sessionId
|
chatsid: sessionId
|
||||||
};
|
};
|
||||||
|
@ -206,7 +215,8 @@ export class AddonModChatProvider {
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
getSessions(chatId: number, groupId: number = 0, showAll: boolean = false, ignoreCache: boolean = false, siteId?: string):
|
getSessions(chatId: number, groupId: number = 0, showAll: boolean = false, ignoreCache: boolean = false, siteId?: string):
|
||||||
Promise<any[]> {
|
Promise<AddonModChatSession[]> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
chatid: chatId,
|
chatid: chatId,
|
||||||
|
@ -222,7 +232,7 @@ export class AddonModChatProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_chat_get_sessions', params, preSets).then((response) => {
|
return site.read('mod_chat_get_sessions', params, preSets).then((response: AddonModChatGetSessionsResult): any => {
|
||||||
if (!response || !response.sessions) {
|
if (!response || !response.sessions) {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
|
@ -245,7 +255,8 @@ export class AddonModChatProvider {
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0, ignoreCache: boolean = false,
|
getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0, ignoreCache: boolean = false,
|
||||||
siteId?: string): Promise<any[]> {
|
siteId?: string): Promise<AddonModChatSessionMessage[]> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
chatid: chatId,
|
chatid: chatId,
|
||||||
|
@ -262,7 +273,9 @@ export class AddonModChatProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_chat_get_session_messages', params, preSets).then((response) => {
|
return site.read('mod_chat_get_session_messages', params, preSets)
|
||||||
|
.then((response: AddonModChatGetSessionMessagesResult): any => {
|
||||||
|
|
||||||
if (!response || !response.messages) {
|
if (!response || !response.messages) {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
|
@ -390,3 +403,152 @@ export class AddonModChatProvider {
|
||||||
return this.ROOT_CACHE_KEY + 'sessionsMessages:' + chatId + ':';
|
return this.ROOT_CACHE_KEY + 'sessionsMessages:' + chatId + ':';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat returned by mod_chat_get_chats_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModChatChat = {
|
||||||
|
id: number; // Chat id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Chat name.
|
||||||
|
intro: string; // The Chat intro.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
chatmethod?: string; // Chat method (sockets, ajax, header_js).
|
||||||
|
keepdays?: number; // Keep days.
|
||||||
|
studentlogs?: number; // Student logs visible to everyone.
|
||||||
|
chattime?: number; // Chat time.
|
||||||
|
schedule?: number; // Schedule type.
|
||||||
|
timemodified?: number; // Time of last modification.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: boolean; // Visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat user returned by mod_chat_get_chat_users.
|
||||||
|
*/
|
||||||
|
export type AddonModChatUser = {
|
||||||
|
id: number; // User id.
|
||||||
|
fullname: string; // User full name.
|
||||||
|
profileimageurl: string; // User picture URL.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meessage returned by mod_chat_get_chat_latest_messages.
|
||||||
|
*/
|
||||||
|
export type AddonModChatMessage = {
|
||||||
|
id: number; // Message id.
|
||||||
|
userid: number; // User id.
|
||||||
|
system: boolean; // True if is a system message (like user joined).
|
||||||
|
message: string; // Message text.
|
||||||
|
timestamp: number; // Timestamp for the message.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message with user data
|
||||||
|
*/
|
||||||
|
export type AddonModChatMessageWithUserData = AddonModChatMessage & AddonModChatMessageUserData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat session.
|
||||||
|
*/
|
||||||
|
export type AddonModChatSession = {
|
||||||
|
sessionstart: number; // Session start time.
|
||||||
|
sessionend: number; // Session end time.
|
||||||
|
sessionusers: AddonModChatSessionUser[]; // Session users.
|
||||||
|
iscomplete: boolean; // Whether the session is completed or not.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat user returned by mod_chat_get_sessions.
|
||||||
|
*/
|
||||||
|
export type AddonModChatSessionUser = {
|
||||||
|
userid: number; // User id.
|
||||||
|
messagecount: number; // Number of messages in the session.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message returned by mod_chat_get_session_messages.
|
||||||
|
*/
|
||||||
|
export type AddonModChatSessionMessage = {
|
||||||
|
id: number; // The message record id.
|
||||||
|
chatid: number; // The chat id.
|
||||||
|
userid: number; // The user who wrote the message.
|
||||||
|
groupid: number; // The group this message belongs to.
|
||||||
|
issystem: boolean; // Whether is a system message or not.
|
||||||
|
message: string; // The message text.
|
||||||
|
timestamp: number; // The message timestamp (indicates when the message was sent).
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message with user data
|
||||||
|
*/
|
||||||
|
export type AddonModChatSessionMessageWithUserData = AddonModChatSessionMessage & AddonModChatMessageUserData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_get_chats_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModChatGetChatsByCoursesResult = {
|
||||||
|
chats: AddonModChatChat[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_get_chat_users.
|
||||||
|
*/
|
||||||
|
export type AddonModChatGetChatUsersResult = {
|
||||||
|
users: AddonModChatUser[]; // List of users.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_get_sessions.
|
||||||
|
*/
|
||||||
|
export type AddonModChatGetSessionsResult = {
|
||||||
|
sessions: AddonModChatSession[]; // List of sessions.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_get_session_messages.
|
||||||
|
*/
|
||||||
|
export type AddonModChatGetSessionMessagesResult = {
|
||||||
|
messages: AddonModChatSessionMessage[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_send_chat_message.
|
||||||
|
*/
|
||||||
|
export type AddonModChatSendChatMessageResult = {
|
||||||
|
messageid: number; // Message sent id.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_get_chat_latest_messages.
|
||||||
|
*/
|
||||||
|
export type AddonModChatGetChatLatestMessagesResult = {
|
||||||
|
messages: AddonModChatMessage[]; // List of messages.
|
||||||
|
chatnewlasttime: number; // New last time.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_chat_login_user.
|
||||||
|
*/
|
||||||
|
export type AddonModChatLoginUserResult = {
|
||||||
|
chatsid: string; // Unique chat session id.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data added to messages.
|
||||||
|
*/
|
||||||
|
type AddonModChatMessageUserData = {
|
||||||
|
userfullname?: string; // Calculated in the app. Full name of the user who wrote the message.
|
||||||
|
userprofileimageurl?: string; // Calculated in the app. Full name of the user who wrote the message.
|
||||||
|
};
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonModChatProvider } from './chat';
|
import { AddonModChatProvider, AddonModChatChat } from './chat';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to prefetch chats.
|
* Handler to prefetch chats.
|
||||||
|
@ -116,12 +116,12 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
||||||
*/
|
*/
|
||||||
protected prefetchChat(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
|
protected prefetchChat(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
|
||||||
// Prefetch chat and group info.
|
// Prefetch chat and group info.
|
||||||
const promises = [
|
const promises: Promise<any>[] = [
|
||||||
this.chatProvider.getChat(courseId, module.id, siteId),
|
this.chatProvider.getChat(courseId, module.id, siteId),
|
||||||
this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId)
|
this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId)
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise.all(promises).then(([chat, groupInfo]: [any, CoreGroupInfo]) => {
|
return Promise.all(promises).then(([chat, groupInfo]: [AddonModChatChat, CoreGroupInfo]) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
let groupIds = [0];
|
let groupIds = [0];
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
<!-- Activity availability messages -->
|
<!-- Activity availability messages -->
|
||||||
<ion-card class="core-info-card" icon-start *ngIf="choiceNotOpenYet">
|
<ion-card class="core-info-card" icon-start *ngIf="choiceNotOpenYet">
|
||||||
<ion-icon name="information-circle"></ion-icon>
|
<ion-icon name="information-circle"></ion-icon>
|
||||||
<p *ngIf="options && options.length">{{ 'addon.mod_choice.previewonly' | translate:{$a: choice.openTimeReadable} }}</p>
|
<p *ngIf="options && options.length">{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}</p>
|
||||||
<p *ngIf="!options || !options.length">{{ 'addon.mod_choice.notopenyet' | translate:{$a: choice.openTimeReadable} }}</p>
|
<p *ngIf="!options || !options.length">{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}</p>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<ion-card class="core-info-card" icon-start *ngIf="choiceClosed">
|
<ion-card class="core-info-card" icon-start *ngIf="choiceClosed">
|
||||||
<ion-icon name="information-circle"></ion-icon>
|
<ion-icon name="information-circle"></ion-icon>
|
||||||
<p *ngIf="options && options.length">{{ 'addon.mod_choice.yourselection' | translate }} <core-format-text [text]="options[0].text"></core-format-text></p>
|
<p *ngIf="options && options.length">{{ 'addon.mod_choice.yourselection' | translate }} <core-format-text [text]="options[0].text"></core-format-text></p>
|
||||||
<p>{{ 'addon.mod_choice.expired' | translate:{$a: choice.closeTimeReadable} }}</p>
|
<p>{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}</p>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<!-- Choice done in offline but not synchronized -->
|
<!-- Choice done in offline but not synchronized -->
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
<ion-item-group *ngFor="let result of results">
|
<ion-item-group *ngFor="let result of results">
|
||||||
<ion-item-divider text-wrap>
|
<ion-item-divider text-wrap>
|
||||||
<h2><core-format-text [text]="result.text"></core-format-text></h2>
|
<h2><core-format-text [text]="result.text"></core-format-text></h2>
|
||||||
<p>{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamount} }})</p>
|
<p>{{ 'addon.mod_choice.numberofuser' | translate }}: {{ result.numberofuser }} ({{ 'core.percentagenumber' | translate: {$a: result.percentageamountfixed} }})</p>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<a ion-item *ngFor="let user of result.userresponses" core-user-link [courseId]="courseid" [userId]="user.userid" [title]="user.fullname" text-wrap>
|
<a ion-item *ngFor="let user of result.userresponses" core-user-link [courseId]="courseid" [userId]="user.userid" [title]="user.fullname" text-wrap>
|
||||||
<ion-avatar core-user-avatar [user]="user" item-start [courseId]="courseid"></ion-avatar>
|
<ion-avatar core-user-avatar [user]="user" item-start [courseId]="courseid"></ion-avatar>
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Optional, Injector } from '@angular/core';
|
||||||
import { Content } from 'ionic-angular';
|
import { Content } from 'ionic-angular';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||||
import { AddonModChoiceProvider } from '../../providers/choice';
|
import { AddonModChoiceProvider, AddonModChoiceChoice, AddonModChoiceOption, AddonModChoiceResult } from '../../providers/choice';
|
||||||
import { AddonModChoiceOfflineProvider } from '../../providers/offline';
|
import { AddonModChoiceOfflineProvider } from '../../providers/offline';
|
||||||
import { AddonModChoiceSyncProvider } from '../../providers/sync';
|
import { AddonModChoiceSyncProvider } from '../../providers/sync';
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
component = AddonModChoiceProvider.COMPONENT;
|
component = AddonModChoiceProvider.COMPONENT;
|
||||||
moduleName = 'choice';
|
moduleName = 'choice';
|
||||||
|
|
||||||
choice: any;
|
choice: AddonModChoiceChoice;
|
||||||
options = [];
|
options: AddonModChoiceOption[] = [];
|
||||||
selectedOption: any;
|
selectedOption: {id: number};
|
||||||
choiceNotOpenYet = false;
|
choiceNotOpenYet = false;
|
||||||
choiceClosed = false;
|
choiceClosed = false;
|
||||||
canEdit = false;
|
canEdit = false;
|
||||||
|
@ -43,6 +43,8 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
labels = [];
|
labels = [];
|
||||||
results = [];
|
results = [];
|
||||||
publishInfo: string; // Message explaining the user what will happen with his choices.
|
publishInfo: string; // Message explaining the user what will happen with his choices.
|
||||||
|
openTimeReadable: string;
|
||||||
|
closeTimeReadable: string;
|
||||||
|
|
||||||
protected userId: number;
|
protected userId: number;
|
||||||
protected syncEventName = AddonModChoiceSyncProvider.AUTO_SYNCED;
|
protected syncEventName = AddonModChoiceSyncProvider.AUTO_SYNCED;
|
||||||
|
@ -122,12 +124,12 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
|
|
||||||
return this.choiceProvider.getChoice(this.courseId, this.module.id).then((choice) => {
|
return this.choiceProvider.getChoice(this.courseId, this.module.id).then((choice) => {
|
||||||
this.choice = choice;
|
this.choice = choice;
|
||||||
this.choice.timeopen = parseInt(choice.timeopen) * 1000;
|
this.choice.timeopen = choice.timeopen * 1000;
|
||||||
this.choice.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
|
this.choice.timeclose = choice.timeclose * 1000;
|
||||||
this.choice.timeclose = parseInt(choice.timeclose) * 1000;
|
this.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
|
||||||
this.choice.closeTimeReadable = this.timeUtils.userDate(choice.timeclose);
|
this.closeTimeReadable = this.timeUtils.userDate(choice.timeclose);
|
||||||
|
|
||||||
this.description = choice.intro || choice.description;
|
this.description = choice.intro;
|
||||||
this.choiceNotOpenYet = choice.timeopen && choice.timeopen > this.now;
|
this.choiceNotOpenYet = choice.timeopen && choice.timeopen > this.now;
|
||||||
this.choiceClosed = choice.timeclose && choice.timeclose <= this.now;
|
this.choiceClosed = choice.timeclose && choice.timeclose <= this.now;
|
||||||
|
|
||||||
|
@ -175,7 +177,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
|
|
||||||
if (hasOffline) {
|
if (hasOffline) {
|
||||||
promise = this.choiceOffline.getResponse(this.choice.id).then((response) => {
|
promise = this.choiceOffline.getResponse(this.choice.id).then((response) => {
|
||||||
const optionsKeys = {};
|
const optionsKeys: {[id: number]: AddonModChoiceOption} = {};
|
||||||
options.forEach((option) => {
|
options.forEach((option) => {
|
||||||
optionsKeys[option.id] = option;
|
optionsKeys[option.id] = option;
|
||||||
});
|
});
|
||||||
|
@ -223,7 +225,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
promise = Promise.resolve(options);
|
promise = Promise.resolve(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then((options) => {
|
promise.then((options: AddonModChoiceOption[]) => {
|
||||||
const isOpen = this.isChoiceOpen();
|
const isOpen = this.isChoiceOpen();
|
||||||
|
|
||||||
let hasAnswered = false;
|
let hasAnswered = false;
|
||||||
|
@ -291,11 +293,11 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
let hasVotes = false;
|
let hasVotes = false;
|
||||||
this.data = [];
|
this.data = [];
|
||||||
this.labels = [];
|
this.labels = [];
|
||||||
results.forEach((result) => {
|
results.forEach((result: AddonModChoiceResultFormatted) => {
|
||||||
if (result.numberofuser > 0) {
|
if (result.numberofuser > 0) {
|
||||||
hasVotes = true;
|
hasVotes = true;
|
||||||
}
|
}
|
||||||
result.percentageamount = parseFloat(result.percentageamount).toFixed(1);
|
result.percentageamountfixed = result.percentageamount.toFixed(1);
|
||||||
this.data.push(result.numberofuser);
|
this.data.push(result.numberofuser);
|
||||||
this.labels.push(result.text);
|
this.labels.push(result.text);
|
||||||
});
|
});
|
||||||
|
@ -429,3 +431,10 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
return result.updated;
|
return result.updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choice result with some calculated data.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceResultFormatted = AddonModChoiceResult & {
|
||||||
|
percentageamountfixed: string; // Percentage of users answers with fixed decimals.
|
||||||
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { AddonModChoiceOfflineProvider } from './offline';
|
import { AddonModChoiceOfflineProvider } from './offline';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for choices.
|
* Service that provides some features for choices.
|
||||||
|
@ -118,7 +119,9 @@ export class AddonModChoiceProvider {
|
||||||
responses: responses
|
responses: responses
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.write('mod_choice_delete_choice_responses', params).then((response) => {
|
return site.write('mod_choice_delete_choice_responses', params)
|
||||||
|
.then((response: AddonModChoiceDeleteChoiceResponsesResult) => {
|
||||||
|
|
||||||
// Other errors ocurring.
|
// Other errors ocurring.
|
||||||
if (!response || response.status === false) {
|
if (!response || response.status === false) {
|
||||||
return Promise.reject(this.utils.createFakeWSError(''));
|
return Promise.reject(this.utils.createFakeWSError(''));
|
||||||
|
@ -179,7 +182,7 @@ export class AddonModChoiceProvider {
|
||||||
* @return Promise resolved when the choice is retrieved.
|
* @return Promise resolved when the choice is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getChoiceByDataKey(siteId: string, courseId: number, key: string, value: any, forceCache?: boolean,
|
protected getChoiceByDataKey(siteId: string, courseId: number, key: string, value: any, forceCache?: boolean,
|
||||||
ignoreCache?: boolean): Promise<any> {
|
ignoreCache?: boolean): Promise<AddonModChoiceChoice> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -198,7 +201,9 @@ export class AddonModChoiceProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_choice_get_choices_by_courses', params, preSets).then((response) => {
|
return site.read('mod_choice_get_choices_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModChoiceGetChoicesByCoursesResult): any => {
|
||||||
|
|
||||||
if (response && response.choices) {
|
if (response && response.choices) {
|
||||||
const currentChoice = response.choices.find((choice) => choice[key] == value);
|
const currentChoice = response.choices.find((choice) => choice[key] == value);
|
||||||
if (currentChoice) {
|
if (currentChoice) {
|
||||||
|
@ -221,7 +226,8 @@ export class AddonModChoiceProvider {
|
||||||
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
||||||
* @return Promise resolved when the choice is retrieved.
|
* @return Promise resolved when the choice is retrieved.
|
||||||
*/
|
*/
|
||||||
getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise<any> {
|
getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean)
|
||||||
|
: Promise<AddonModChoiceChoice> {
|
||||||
return this.getChoiceByDataKey(siteId, courseId, 'coursemodule', cmId, forceCache, ignoreCache);
|
return this.getChoiceByDataKey(siteId, courseId, 'coursemodule', cmId, forceCache, ignoreCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +241,8 @@ export class AddonModChoiceProvider {
|
||||||
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
|
||||||
* @return Promise resolved when the choice is retrieved.
|
* @return Promise resolved when the choice is retrieved.
|
||||||
*/
|
*/
|
||||||
getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise<any> {
|
getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean)
|
||||||
|
: Promise<AddonModChoiceChoice> {
|
||||||
return this.getChoiceByDataKey(siteId, courseId, 'id', choiceId, forceCache, ignoreCache);
|
return this.getChoiceByDataKey(siteId, courseId, 'id', choiceId, forceCache, ignoreCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +254,7 @@ export class AddonModChoiceProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with choice options.
|
* @return Promise resolved with choice options.
|
||||||
*/
|
*/
|
||||||
getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModChoiceOption[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
choiceid: choiceId
|
choiceid: choiceId
|
||||||
|
@ -262,7 +269,9 @@ export class AddonModChoiceProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_choice_get_choice_options', params, preSets).then((response) => {
|
return site.read('mod_choice_get_choice_options', params, preSets)
|
||||||
|
.then((response: AddonModChoiceGetChoiceOptionsResult): any => {
|
||||||
|
|
||||||
if (response.options) {
|
if (response.options) {
|
||||||
return response.options;
|
return response.options;
|
||||||
}
|
}
|
||||||
|
@ -280,7 +289,7 @@ export class AddonModChoiceProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved with choice results.
|
* @return Promise resolved with choice results.
|
||||||
*/
|
*/
|
||||||
getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise<AddonModChoiceResult[]> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
choiceid: choiceId
|
choiceid: choiceId
|
||||||
|
@ -294,7 +303,9 @@ export class AddonModChoiceProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_choice_get_choice_results', params, preSets).then((response) => {
|
return site.read('mod_choice_get_choice_results', params, preSets)
|
||||||
|
.then((response: AddonModChoiceGetChoiceResults): any => {
|
||||||
|
|
||||||
if (response.options) {
|
if (response.options) {
|
||||||
return response.options;
|
return response.options;
|
||||||
}
|
}
|
||||||
|
@ -456,3 +467,96 @@ export class AddonModChoiceProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choice returned by mod_choice_get_choices_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceChoice = {
|
||||||
|
id: number; // Choice instance id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Choice name.
|
||||||
|
intro: string; // The choice intro.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
publish?: boolean; // If choice is published.
|
||||||
|
showresults?: number; // 0 never, 1 after answer, 2 after close, 3 always.
|
||||||
|
display?: number; // Display mode (vertical, horizontal).
|
||||||
|
allowupdate?: boolean; // Allow update.
|
||||||
|
allowmultiple?: boolean; // Allow multiple choices.
|
||||||
|
showunanswered?: boolean; // Show users who not answered yet.
|
||||||
|
includeinactive?: boolean; // Include inactive users.
|
||||||
|
limitanswers?: boolean; // Limit unswers.
|
||||||
|
timeopen?: number; // Date of opening validity.
|
||||||
|
timeclose?: number; // Date of closing validity.
|
||||||
|
showpreview?: boolean; // Show preview before timeopen.
|
||||||
|
timemodified?: number; // Time of last modification.
|
||||||
|
completionsubmit?: boolean; // Completion on user submission.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: boolean; // Visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option returned by mod_choice_get_choice_options.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceOption = {
|
||||||
|
id: number; // Option id.
|
||||||
|
text: string; // Text of the choice.
|
||||||
|
maxanswers: number; // Maximum number of answers.
|
||||||
|
displaylayout: boolean; // True for orizontal, otherwise vertical.
|
||||||
|
countanswers: number; // Number of answers.
|
||||||
|
checked: boolean; // We already answered.
|
||||||
|
disabled: boolean; // Option disabled.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result returned by mod_choice_get_choice_results.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceResult = {
|
||||||
|
id: number; // Choice instance id.
|
||||||
|
text: string; // Text of the choice.
|
||||||
|
maxanswer: number; // Maximum number of answers.
|
||||||
|
userresponses: {
|
||||||
|
userid: number; // User id.
|
||||||
|
fullname: string; // User full name.
|
||||||
|
profileimageurl: string; // Profile user image url.
|
||||||
|
answerid?: number; // Answer id.
|
||||||
|
timemodified?: number; // Time of modification.
|
||||||
|
}[];
|
||||||
|
numberofuser: number; // Number of users answers.
|
||||||
|
percentageamount: number; // Percentage of users answers.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_choice_get_choices_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceGetChoicesByCoursesResult = {
|
||||||
|
choices: AddonModChoiceChoice[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_choice_get_choice_options.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceGetChoiceOptionsResult = {
|
||||||
|
options: AddonModChoiceOption[]; // Options.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_choice_get_choice_results.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceGetChoiceResults = {
|
||||||
|
options: AddonModChoiceResult[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_choice_delete_choice_responses.
|
||||||
|
*/
|
||||||
|
export type AddonModChoiceDeleteChoiceResponsesResult = {
|
||||||
|
status: boolean; // Status, true if everything went right.
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -34,6 +34,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
||||||
component = AddonModFolderProvider.COMPONENT;
|
component = AddonModFolderProvider.COMPONENT;
|
||||||
canGetFolder: boolean;
|
canGetFolder: boolean;
|
||||||
contents: any;
|
contents: any;
|
||||||
|
moduleContents: any;
|
||||||
|
|
||||||
constructor(injector: Injector, private folderProvider: AddonModFolderProvider, private courseProvider: CoreCourseProvider,
|
constructor(injector: Injector, private folderProvider: AddonModFolderProvider, private courseProvider: CoreCourseProvider,
|
||||||
private appProvider: CoreAppProvider, private folderHelper: AddonModFolderHelperProvider) {
|
private appProvider: CoreAppProvider, private folderHelper: AddonModFolderHelperProvider) {
|
||||||
|
@ -87,9 +88,9 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
||||||
|
|
||||||
if (this.path) {
|
if (this.path) {
|
||||||
// Subfolder.
|
// Subfolder.
|
||||||
this.contents = module.contents;
|
this.contents = this.moduleContents;
|
||||||
} else {
|
} else {
|
||||||
this.contents = this.folderHelper.formatContents(module.contents);
|
this.contents = this.folderHelper.formatContents(this.moduleContents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
||||||
if (this.canGetFolder) {
|
if (this.canGetFolder) {
|
||||||
promise = this.folderProvider.getFolder(this.courseId, this.module.id).then((folder) => {
|
promise = this.folderProvider.getFolder(this.courseId, this.module.id).then((folder) => {
|
||||||
return this.courseProvider.loadModuleContents(this.module, this.courseId).then(() => {
|
return this.courseProvider.loadModuleContents(this.module, this.courseId).then(() => {
|
||||||
folder.contents = this.module.contents;
|
this.moduleContents = this.module.contents;
|
||||||
|
|
||||||
return folder;
|
return folder;
|
||||||
});
|
});
|
||||||
|
@ -117,17 +118,13 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
||||||
folder.contents = this.module.contents;
|
folder.contents = this.module.contents;
|
||||||
}
|
}
|
||||||
this.module = folder;
|
this.module = folder;
|
||||||
|
this.moduleContents = folder.contents;
|
||||||
|
|
||||||
return folder;
|
return folder;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then((folder) => {
|
return promise.then((folder) => {
|
||||||
if (folder) {
|
|
||||||
this.description = folder.intro || folder.description;
|
|
||||||
this.dataRetrieved.emit(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showModuleData(folder);
|
this.showModuleData(folder);
|
||||||
|
|
||||||
// All data obtained, now fill the context menu.
|
// All data obtained, now fill the context menu.
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for folder.
|
* Service that provides some features for folder.
|
||||||
|
@ -43,7 +44,7 @@ export class AddonModFolderProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the book is retrieved.
|
* @return Promise resolved when the book is retrieved.
|
||||||
*/
|
*/
|
||||||
getFolder(courseId: number, cmId: number, siteId?: string): Promise<any> {
|
getFolder(courseId: number, cmId: number, siteId?: string): Promise<AddonModFolderFolder> {
|
||||||
return this.getFolderByKey(courseId, 'coursemodule', cmId, siteId);
|
return this.getFolderByKey(courseId, 'coursemodule', cmId, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ export class AddonModFolderProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the book is retrieved.
|
* @return Promise resolved when the book is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getFolderByKey(courseId: number, key: string, value: any, siteId?: string): Promise<any> {
|
protected getFolderByKey(courseId: number, key: string, value: any, siteId?: string): Promise<AddonModFolderFolder> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
courseids: [courseId]
|
courseids: [courseId]
|
||||||
|
@ -66,7 +67,9 @@ export class AddonModFolderProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('mod_folder_get_folders_by_courses', params, preSets).then((response) => {
|
return site.read('mod_folder_get_folders_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModFolderGetFoldersByCoursesResult): any => {
|
||||||
|
|
||||||
if (response && response.folders) {
|
if (response && response.folders) {
|
||||||
const currentFolder = response.folders.find((folder) => {
|
const currentFolder = response.folders.find((folder) => {
|
||||||
return folder[key] == value;
|
return folder[key] == value;
|
||||||
|
@ -147,3 +150,33 @@ export class AddonModFolderProvider {
|
||||||
{}, siteId);
|
{}, siteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folder returned by mod_folder_get_folders_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModFolderFolder = {
|
||||||
|
id: number; // Module id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Page name.
|
||||||
|
intro: string; // Summary.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles: CoreWSExternalFile[];
|
||||||
|
revision: number; // Incremented when after each file changes, to avoid cache.
|
||||||
|
timemodified: number; // Last time the folder was modified.
|
||||||
|
display: number; // Display type of folder contents on a separate page or inline.
|
||||||
|
showexpanded: number; // 1 = expanded, 0 = collapsed for sub-folders.
|
||||||
|
showdownloadfolder: number; // Whether to show the download folder button.
|
||||||
|
section: number; // Course section id.
|
||||||
|
visible: number; // Module visibility.
|
||||||
|
groupmode: number; // Group mode.
|
||||||
|
groupingid: number; // Grouping id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_folder_get_folders_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModFolderGetFoldersByCoursesResult = {
|
||||||
|
folders: AddonModFolderFolder[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -79,7 +79,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
promises.push(this.imscpProvider.getImscp(this.courseId, this.module.id).then((imscp) => {
|
promises.push(this.imscpProvider.getImscp(this.courseId, this.module.id).then((imscp) => {
|
||||||
this.description = imscp.intro || imscp.description;
|
this.description = imscp.intro;
|
||||||
this.dataRetrieved.emit(imscp);
|
this.dataRetrieved.emit(imscp);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for IMSCP.
|
* Service that provides some features for IMSCP.
|
||||||
|
@ -157,7 +158,7 @@ export class AddonModImscpProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the imscp is retrieved.
|
* @return Promise resolved when the imscp is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getImscpByKey(courseId: number, key: string, value: any, siteId?: string): Promise<any> {
|
protected getImscpByKey(courseId: number, key: string, value: any, siteId?: string): Promise<AddonModImscpImscp> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
courseids: [courseId]
|
courseids: [courseId]
|
||||||
|
@ -167,7 +168,9 @@ export class AddonModImscpProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.read('mod_imscp_get_imscps_by_courses', params, preSets).then((response) => {
|
return site.read('mod_imscp_get_imscps_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModImscpGetImscpsByCoursesResult): any => {
|
||||||
|
|
||||||
if (response && response.imscps) {
|
if (response && response.imscps) {
|
||||||
const currentImscp = response.imscps.find((imscp) => imscp[key] == value);
|
const currentImscp = response.imscps.find((imscp) => imscp[key] == value);
|
||||||
if (currentImscp) {
|
if (currentImscp) {
|
||||||
|
@ -188,7 +191,7 @@ export class AddonModImscpProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the imscp is retrieved.
|
* @return Promise resolved when the imscp is retrieved.
|
||||||
*/
|
*/
|
||||||
getImscp(courseId: number, cmId: number, siteId?: string): Promise<any> {
|
getImscp(courseId: number, cmId: number, siteId?: string): Promise<AddonModImscpImscp> {
|
||||||
return this.getImscpByKey(courseId, 'coursemodule', cmId, siteId);
|
return this.getImscpByKey(courseId, 'coursemodule', cmId, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,3 +327,32 @@ export class AddonModImscpProvider {
|
||||||
siteId);
|
siteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMSCP returned by mod_imscp_get_imscps_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModImscpImscp = {
|
||||||
|
id: number; // IMSCP id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Activity name.
|
||||||
|
intro?: string; // The IMSCP intro.
|
||||||
|
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
revision?: number; // Revision.
|
||||||
|
keepold?: number; // Number of old IMSCP to keep.
|
||||||
|
structure?: string; // IMSCP structure.
|
||||||
|
timemodified?: string; // Time of last modification.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: boolean; // If visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_imscp_get_imscps_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModImscpGetImscpsByCoursesResult = {
|
||||||
|
imscps: AddonModImscpImscp[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for labels.
|
* Service that provides some features for labels.
|
||||||
|
@ -52,7 +53,7 @@ export class AddonModLabelProvider {
|
||||||
* @return Promise resolved when the label is retrieved.
|
* @return Promise resolved when the label is retrieved.
|
||||||
*/
|
*/
|
||||||
protected getLabelByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean,
|
protected getLabelByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean,
|
||||||
siteId?: string): Promise<any> {
|
siteId?: string): Promise<AddonModLabelLabel> {
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -70,7 +71,9 @@ export class AddonModLabelProvider {
|
||||||
preSets.emergencyCache = false;
|
preSets.emergencyCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_label_get_labels_by_courses', params, preSets).then((response) => {
|
return site.read('mod_label_get_labels_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModLabelGetLabelsByCoursesResult): any => {
|
||||||
|
|
||||||
if (response && response.labels) {
|
if (response && response.labels) {
|
||||||
const currentLabel = response.labels.find((label) => label[key] == value);
|
const currentLabel = response.labels.find((label) => label[key] == value);
|
||||||
if (currentLabel) {
|
if (currentLabel) {
|
||||||
|
@ -93,7 +96,8 @@ export class AddonModLabelProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the label is retrieved.
|
* @return Promise resolved when the label is retrieved.
|
||||||
*/
|
*/
|
||||||
getLabel(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getLabel(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string)
|
||||||
|
: Promise<AddonModLabelLabel> {
|
||||||
return this.getLabelByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId);
|
return this.getLabelByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +111,8 @@ export class AddonModLabelProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when the label is retrieved.
|
* @return Promise resolved when the label is retrieved.
|
||||||
*/
|
*/
|
||||||
getLabelById(courseId: number, labelId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise<any> {
|
getLabelById(courseId: number, labelId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string)
|
||||||
|
: Promise<AddonModLabelLabel> {
|
||||||
return this.getLabelByField(courseId, 'id', labelId, forceCache, ignoreCache, siteId);
|
return this.getLabelByField(courseId, 'id', labelId, forceCache, ignoreCache, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,3 +175,29 @@ export class AddonModLabelProvider {
|
||||||
return site.wsAvailable('mod_label_get_labels_by_courses');
|
return site.wsAvailable('mod_label_get_labels_by_courses');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label returned by mod_label_get_labels_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModLabelLabel = {
|
||||||
|
id: number; // Module id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Label name.
|
||||||
|
intro: string; // Label contents.
|
||||||
|
introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles: CoreWSExternalFile[];
|
||||||
|
timemodified: number; // Last time the label was modified.
|
||||||
|
section: number; // Course section id.
|
||||||
|
visible: number; // Module visibility.
|
||||||
|
groupmode: number; // Group mode.
|
||||||
|
groupingid: number; // Grouping id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_label_get_labels_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModLabelGetLabelsByCoursesResult = {
|
||||||
|
labels: AddonModLabelLabel[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { Component, Optional, Injector } from '@angular/core';
|
import { Component, Optional, Injector } from '@angular/core';
|
||||||
import { Content } from 'ionic-angular';
|
import { Content } from 'ionic-angular';
|
||||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||||
import { AddonModLtiProvider } from '../../providers/lti';
|
import { AddonModLtiProvider, AddonModLtiLti } from '../../providers/lti';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays an LTI entry page.
|
* Component that displays an LTI entry page.
|
||||||
|
@ -28,7 +28,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo
|
||||||
component = AddonModLtiProvider.COMPONENT;
|
component = AddonModLtiProvider.COMPONENT;
|
||||||
moduleName = 'lti';
|
moduleName = 'lti';
|
||||||
|
|
||||||
lti: any; // The LTI object.
|
lti: AddonModLtiLti; // The LTI object.
|
||||||
|
|
||||||
protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti';
|
protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti';
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo
|
||||||
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||||
return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => {
|
return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => {
|
||||||
this.lti = ltiData;
|
this.lti = ltiData;
|
||||||
this.description = this.lti.intro || this.description;
|
this.description = this.lti.intro;
|
||||||
this.dataRetrieved.emit(this.lti);
|
this.dataRetrieved.emit(this.lti);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// All data obtained, now fill the context menu.
|
// All data obtained, now fill the context menu.
|
||||||
|
|
|
@ -22,11 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||||
export interface AddonModLtiParam {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for LTI.
|
* Service that provides some features for LTI.
|
||||||
|
@ -104,7 +100,7 @@ export class AddonModLtiProvider {
|
||||||
* @param cmId Course module ID.
|
* @param cmId Course module ID.
|
||||||
* @return Promise resolved when the LTI is retrieved.
|
* @return Promise resolved when the LTI is retrieved.
|
||||||
*/
|
*/
|
||||||
getLti(courseId: number, cmId: number): Promise<any> {
|
getLti(courseId: number, cmId: number): Promise<AddonModLtiLti> {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
courseids: [courseId]
|
courseids: [courseId]
|
||||||
};
|
};
|
||||||
|
@ -113,7 +109,9 @@ export class AddonModLtiProvider {
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY
|
updateFrequency: CoreSite.FREQUENCY_RARELY
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.sitesProvider.getCurrentSite().read('mod_lti_get_ltis_by_courses', params, preSets).then((response) => {
|
return this.sitesProvider.getCurrentSite().read('mod_lti_get_ltis_by_courses', params, preSets)
|
||||||
|
.then((response: AddonModLtiGetLtisByCoursesResult): any => {
|
||||||
|
|
||||||
if (response.ltis) {
|
if (response.ltis) {
|
||||||
const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId);
|
const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId);
|
||||||
if (currentLti) {
|
if (currentLti) {
|
||||||
|
@ -141,8 +139,8 @@ export class AddonModLtiProvider {
|
||||||
* @param id LTI id.
|
* @param id LTI id.
|
||||||
* @return Promise resolved when the launch data is retrieved.
|
* @return Promise resolved when the launch data is retrieved.
|
||||||
*/
|
*/
|
||||||
getLtiLaunchData(id: number): Promise<any> {
|
getLtiLaunchData(id: number): Promise<AddonModLtiGetToolLaunchDataResult> {
|
||||||
const params: any = {
|
const params = {
|
||||||
toolid: id
|
toolid: id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +152,9 @@ export class AddonModLtiProvider {
|
||||||
cacheKey: this.getLtiLaunchDataCacheKey(id)
|
cacheKey: this.getLtiLaunchDataCacheKey(id)
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.sitesProvider.getCurrentSite().read('mod_lti_get_tool_launch_data', params, preSets).then((response) => {
|
return this.sitesProvider.getCurrentSite().read('mod_lti_get_tool_launch_data', params, preSets)
|
||||||
|
.then((response: AddonModLtiGetToolLaunchDataResult): any => {
|
||||||
|
|
||||||
if (response.endpoint) {
|
if (response.endpoint) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -227,3 +227,66 @@ export class AddonModLtiProvider {
|
||||||
return this.logHelper.logSingle('mod_lti_view_lti', params, AddonModLtiProvider.COMPONENT, id, name, 'lti', {}, siteId);
|
return this.logHelper.logSingle('mod_lti_view_lti', params, AddonModLtiProvider.COMPONENT, id, name, 'lti', {}, siteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LTI returned by mod_lti_get_ltis_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModLtiLti = {
|
||||||
|
id: number; // External tool id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // LTI name.
|
||||||
|
intro?: string; // The LTI intro.
|
||||||
|
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
timecreated?: number; // Time of creation.
|
||||||
|
timemodified?: number; // Time of last modification.
|
||||||
|
typeid?: number; // Type id.
|
||||||
|
toolurl?: string; // Tool url.
|
||||||
|
securetoolurl?: string; // Secure tool url.
|
||||||
|
instructorchoicesendname?: string; // Instructor choice send name.
|
||||||
|
instructorchoicesendemailaddr?: number; // Instructor choice send mail address.
|
||||||
|
instructorchoiceallowroster?: number; // Instructor choice allow roster.
|
||||||
|
instructorchoiceallowsetting?: number; // Instructor choice allow setting.
|
||||||
|
instructorcustomparameters?: string; // Instructor custom parameters.
|
||||||
|
instructorchoiceacceptgrades?: number; // Instructor choice accept grades.
|
||||||
|
grade?: number; // Enable grades.
|
||||||
|
launchcontainer?: number; // Launch container mode.
|
||||||
|
resourcekey?: string; // Resource key.
|
||||||
|
password?: string; // Shared secret.
|
||||||
|
debuglaunch?: number; // Debug launch.
|
||||||
|
showtitlelaunch?: number; // Show title launch.
|
||||||
|
showdescriptionlaunch?: number; // Show description launch.
|
||||||
|
servicesalt?: string; // Service salt.
|
||||||
|
icon?: string; // Alternative icon URL.
|
||||||
|
secureicon?: string; // Secure icon URL.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: number; // Visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Param to send to the LTI.
|
||||||
|
*/
|
||||||
|
export type AddonModLtiParam = {
|
||||||
|
name: string; // Parameter name.
|
||||||
|
value: string; // Parameter value.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_lti_get_ltis_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModLtiGetLtisByCoursesResult = {
|
||||||
|
ltis: AddonModLtiLti[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of WS mod_lti_get_tool_launch_data.
|
||||||
|
*/
|
||||||
|
export type AddonModLtiGetToolLaunchDataResult = {
|
||||||
|
endpoint: string; // Endpoint URL.
|
||||||
|
parameters: AddonModLtiParam[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue