commit
391782253f
|
@ -21,16 +21,19 @@ function print_success {
|
|||
|
||||
function print_error {
|
||||
tput setaf 1; echo " ERROR: $1"
|
||||
tput setaf 0
|
||||
}
|
||||
|
||||
function print_ok {
|
||||
tput setaf 2; echo " OK: $1"
|
||||
echo
|
||||
tput setaf 0
|
||||
}
|
||||
|
||||
function print_message {
|
||||
tput setaf 3; echo "-------- $1"
|
||||
echo
|
||||
tput setaf 0
|
||||
}
|
||||
|
||||
function print_title {
|
||||
|
@ -38,4 +41,5 @@ function print_title {
|
|||
echo
|
||||
tput setaf 5; echo "$stepnumber $1"
|
||||
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-navbar core-back-button>
|
||||
<ion-title>{{badge.name}}</ion-title>
|
||||
<ion-title>{{badge && badge.name}}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
|
@ -9,7 +9,7 @@
|
|||
</ion-refresher>
|
||||
<core-loading [hideUntil]="badgeLoaded">
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-group *ngIf="badge">
|
||||
<ion-item text-wrap class="item-avatar-center">
|
||||
<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">
|
||||
|
@ -30,7 +30,7 @@
|
|||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-group *ngIf="badge">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.issuerdetails' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
|
@ -48,7 +48,7 @@
|
|||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-group *ngIf="badge">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.badgedetails' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<!-- Criteria (not yet avalaible) -->
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group>
|
||||
<ion-item-group *ngIf="badge">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.issuancedetails' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
|
@ -120,7 +120,7 @@
|
|||
</ion-item-group>
|
||||
|
||||
<!-- Endorsement -->
|
||||
<ion-item-group *ngIf="badge.endorsement">
|
||||
<ion-item-group *ngIf="badge && badge.endorsement">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.bendorsement' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
|
@ -159,7 +159,7 @@
|
|||
</ion-item-group>
|
||||
|
||||
<!-- Related badges -->
|
||||
<ion-item-group *ngIf="badge.relatedbadges">
|
||||
<ion-item-group *ngIf="badge && badge.relatedbadges">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.relatedbages' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
|
@ -172,14 +172,14 @@
|
|||
</ion-item-group>
|
||||
|
||||
<!-- Competencies alignment -->
|
||||
<ion-item-group *ngIf="badge.competencies">
|
||||
<ion-item-group *ngIf="badge && badge.alignment">
|
||||
<ion-item-divider>
|
||||
<h2>{{ 'addon.badges.alignment' | translate}}</h2>
|
||||
</ion-item-divider>
|
||||
<a ion-item text-wrap *ngFor="let competency of badge.competencies" [href]="competency.targeturl" core-link auto-login="no">
|
||||
<h2><core-format-text [text]="competency.targetname"></core-format-text></h2>
|
||||
<a ion-item text-wrap *ngFor="let alignment of badge.alignment" [href]="alignment.targeturl" core-link auto-login="no">
|
||||
<h2><core-format-text [text]="alignment.targetname"></core-format-text></h2>
|
||||
</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>
|
||||
</ion-item>
|
||||
</ion-item-group>
|
||||
|
|
|
@ -19,7 +19,7 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
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.
|
||||
|
@ -38,7 +38,7 @@ export class AddonBadgesIssuedBadgePage {
|
|||
|
||||
user: any = {};
|
||||
course: any = {};
|
||||
badge: any = {};
|
||||
badge: AddonBadgesUserBadge;
|
||||
|
||||
badgeLoaded = false;
|
||||
currentTime = 0;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
@ -36,7 +36,7 @@ export class AddonBadgesUserBadgesPage {
|
|||
userId: number;
|
||||
|
||||
badgesLoaded = false;
|
||||
badges = [];
|
||||
badges: AddonBadgesUserBadge[] = [];
|
||||
currentTime = 0;
|
||||
badgeHash: string;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreWSExternalWarning } from '@providers/ws';
|
||||
import { CoreSite } from '@classes/site';
|
||||
|
||||
/**
|
||||
|
@ -70,7 +71,7 @@ export class AddonBadgesProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
|
||||
|
@ -85,8 +86,13 @@ export class AddonBadgesProvider {
|
|||
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) {
|
||||
// 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;
|
||||
} else {
|
||||
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 { CoreSitesProvider } from '@providers/sites';
|
||||
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 { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
|
||||
|
@ -28,7 +30,7 @@ import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/hel
|
|||
templateUrl: 'addon-block-recentlyaccesseditems.html'
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit {
|
||||
items = [];
|
||||
items: AddonBlockRecentlyAccessedItemsItem[] = [];
|
||||
|
||||
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.
|
||||
* @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) => {
|
||||
const preSets = {
|
||||
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) => {
|
||||
const modicon = item.icon && this.domUtils.getHTMLElementAttribute(item.icon, 'src');
|
||||
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 { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
|
||||
import { AddonBlockTimelineProvider } from '../../providers/timeline';
|
||||
import { AddonCalendarEvent } from '@addon/calendar/providers/calendar';
|
||||
|
||||
/**
|
||||
* Component to render a timeline block.
|
||||
|
@ -34,9 +35,9 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen
|
|||
filter = 'next30days';
|
||||
currentSite: any;
|
||||
timeline = {
|
||||
events: [],
|
||||
events: <AddonCalendarEvent[]> [],
|
||||
loaded: false,
|
||||
canLoadMore: undefined
|
||||
canLoadMore: <number> undefined
|
||||
};
|
||||
timelineCourses = {
|
||||
courses: [],
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreCoursesDashboardProvider } from '@core/courses/providers/dashboard';
|
||||
import { AddonCalendarEvents, AddonCalendarEventsGroupedByCourse, AddonCalendarEvent } from '@addon/calendar/providers/calendar';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -38,7 +39,7 @@ export class AddonBlockTimelineProvider {
|
|||
* @return Promise resolved when the info is retrieved.
|
||||
*/
|
||||
getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string):
|
||||
Promise<{ events: any[], canLoadMore: number }> {
|
||||
Promise<{ events: AddonCalendarEvent[], canLoadMore: number }> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||
|
@ -55,7 +56,9 @@ export class AddonBlockTimelineProvider {
|
|||
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) {
|
||||
return this.treatCourseEvents(courseEvents, time);
|
||||
}
|
||||
|
@ -82,8 +85,9 @@ export class AddonBlockTimelineProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @return Promise resolved when the info is retrieved.
|
||||
*/
|
||||
getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [s: string]:
|
||||
{ events: any[], canLoadMore: number } }> {
|
||||
getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [courseId: string]:
|
||||
{ events: AddonCalendarEvent[], canLoadMore: number } }> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||
data = {
|
||||
|
@ -95,7 +99,9 @@ export class AddonBlockTimelineProvider {
|
|||
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) {
|
||||
const courseEvents = {};
|
||||
|
||||
|
@ -127,7 +133,9 @@ export class AddonBlockTimelineProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @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) => {
|
||||
const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
|
||||
data: any = {
|
||||
|
@ -144,12 +152,14 @@ export class AddonBlockTimelineProvider {
|
|||
data.aftereventid = afterEventId;
|
||||
}
|
||||
|
||||
return site.read('core_calendar_get_action_events_by_timesort', data, preSets).then((events): any => {
|
||||
if (events && events.events) {
|
||||
const canLoadMore = events.events.length >= data.limitnum ? events.lastid : undefined;
|
||||
return site.read('core_calendar_get_action_events_by_timesort', data, preSets)
|
||||
.then((result: AddonCalendarEvents): any => {
|
||||
|
||||
if (result && result.events) {
|
||||
const canLoadMore = result.events.length >= data.limitnum ? result.lastid : undefined;
|
||||
|
||||
// Filter events by time in case it uses cache.
|
||||
events = events.events.filter((element) => {
|
||||
const events = result.events.filter((element) => {
|
||||
return element.timesort >= time;
|
||||
});
|
||||
|
||||
|
@ -236,7 +246,9 @@ export class AddonBlockTimelineProvider {
|
|||
* @param timeFrom Current time to filter events from.
|
||||
* @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 =
|
||||
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 { CoreSitesProvider } from '@providers/sites';
|
||||
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 { CoreTagProvider } from '@core/tag/providers/tag';
|
||||
|
||||
|
@ -48,7 +48,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
loaded = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
entries = [];
|
||||
entries: AddonBlogPostFormatted[] = [];
|
||||
currentUserId: number;
|
||||
showMyEntriesToggle = false;
|
||||
onlyMyEntries = false;
|
||||
|
@ -118,7 +118,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
|
||||
|
||||
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) {
|
||||
case 'draft':
|
||||
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 { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||
|
||||
/**
|
||||
* Service to handle blog entries.
|
||||
|
@ -68,7 +70,7 @@ export class AddonBlogProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const data = {
|
||||
filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value'),
|
||||
|
@ -105,7 +107,7 @@ export class AddonBlogProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
|
||||
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 { AddonBlogEntriesComponent } from '../components/entries/entries';
|
||||
import { AddonBlogProvider } from './blog';
|
||||
import { CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Course nav handler.
|
||||
|
@ -100,7 +101,7 @@ export class AddonBlogCourseOptionHandler implements CoreCourseOptionsHandler {
|
|||
|
||||
return this.blogProvider.getEntries({courseid: course.id}).then((result) => {
|
||||
return result.entries.map((entry) => {
|
||||
let files = [];
|
||||
let files: CoreWSExternalFile[] = [];
|
||||
|
||||
if (entry.attachmentfiles && entry.attachmentfiles.length) {
|
||||
files = entry.attachmentfiles;
|
||||
|
|
|
@ -19,7 +19,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
||||
import { AddonCalendarProvider, AddonCalendarWeek } from '../../providers/calendar';
|
||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
|
@ -44,7 +44,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
|
|||
|
||||
periodName: string;
|
||||
weekDays: any[];
|
||||
weeks: any[];
|
||||
weeks: AddonCalendarWeek[];
|
||||
loaded = false;
|
||||
timeFormat: string;
|
||||
isCurrentMonth: boolean;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<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]">
|
||||
<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>
|
||||
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
||||
<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 { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
||||
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar';
|
||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
|
@ -43,8 +43,8 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges,
|
|||
protected categoriesRetrieved = false;
|
||||
protected categories = {};
|
||||
protected currentSiteId: string;
|
||||
protected events = []; // Events (both online and offline).
|
||||
protected onlineEvents = [];
|
||||
protected events: AddonCalendarCalendarEvent[] = []; // Events (both online and offline).
|
||||
protected onlineEvents: AddonCalendarCalendarEvent[] = [];
|
||||
protected offlineEvents = []; // Offline events.
|
||||
protected deletedEvents = []; // Events deleted in offline.
|
||||
protected lookAhead: number;
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<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]">
|
||||
<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>
|
||||
<p><core-format-text [text]="event.formattedtime"></core-format-text></p>
|
||||
<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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
||||
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from '../../providers/calendar';
|
||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||
|
@ -45,7 +45,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
|||
protected day: number;
|
||||
protected categories = {};
|
||||
protected events = []; // Events (both online and offline).
|
||||
protected onlineEvents = [];
|
||||
protected onlineEvents: AddonCalendarCalendarEvent[] = [];
|
||||
protected offlineEvents = {}; // Offline events.
|
||||
protected offlineEditedEventsIds = []; // IDs of events edited 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) => {
|
||||
if (!this.appProvider.isOnline()) {
|
||||
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
||||
return Promise.resolve({ events: [] });
|
||||
return Promise.resolve({ events: <AddonCalendarCalendarEvent[]> [] });
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
<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>
|
||||
<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-item>
|
||||
<ion-item>
|
||||
|
|
|
@ -27,7 +27,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
|||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts';
|
||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
||||
import { AddonCalendarProvider, AddonCalendarGetAccessInfoResult, AddonCalendarEvent } from '../../providers/calendar';
|
||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||
|
@ -58,7 +58,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
courseGroupSet = false;
|
||||
advanced = false;
|
||||
errors: any;
|
||||
event: any; // The event object (when editing an event).
|
||||
event: AddonCalendarEvent; // The event object (when editing an event).
|
||||
otherEventsCount: number;
|
||||
|
||||
// Form variables.
|
||||
eventForm: FormGroup;
|
||||
|
@ -70,7 +71,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
protected courseId: number;
|
||||
protected originalData: any;
|
||||
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 isDestroyed = false;
|
||||
protected error = false;
|
||||
|
@ -152,7 +153,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchData(refresh?: boolean): Promise<any> {
|
||||
let accessInfo;
|
||||
let accessInfo: AddonCalendarGetAccessInfoResult;
|
||||
|
||||
this.error = false;
|
||||
|
||||
|
@ -197,7 +198,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
promises.push(this.calendarProvider.getEventById(this.eventId).then((event) => {
|
||||
this.event = event;
|
||||
if (event && event.repeatid) {
|
||||
event.othereventscount = event.eventcount ? event.eventcount - 1 : '';
|
||||
this.otherEventsCount = event.eventcount ? event.eventcount - 1 : 0;
|
||||
}
|
||||
|
||||
return event;
|
||||
|
@ -489,7 +490,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
|
||||
// Send the data.
|
||||
const modal = this.domUtils.showModalLoading('core.sending', true);
|
||||
let event;
|
||||
let event: AddonCalendarEvent;
|
||||
|
||||
this.calendarProvider.submitEvent(this.eventId, data).then((result) => {
|
||||
event = result.event;
|
||||
|
@ -497,7 +498,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
|
|||
if (result.sent) {
|
||||
// Event created or edited, invalidate right days & months.
|
||||
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(() => {
|
||||
// Ignore errors.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<ion-navbar core-back-button>
|
||||
<ion-title>
|
||||
<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>
|
||||
</ion-title>
|
||||
<ion-buttons end>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<ion-card-content *ngIf="event">
|
||||
<ion-item text-wrap *ngIf="isSplitViewOn">
|
||||
<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>
|
||||
<p><core-format-text [text]="event.name"></core-format-text></p>
|
||||
<ion-note item-end *ngIf="event.deleted">
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
</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]">
|
||||
<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>
|
||||
<p>
|
||||
{{ event.timestart * 1000 | coreFormatDate: "strftimetime" }}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core';
|
||||
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 { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
|
||||
|
@ -62,7 +62,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
|||
protected manualSyncObserver: any;
|
||||
protected onlineObserver: any;
|
||||
protected currentSiteId: string;
|
||||
protected onlineEvents = [];
|
||||
protected onlineEvents: AddonCalendarGetEventsEvent[] = [];
|
||||
protected offlineEvents = [];
|
||||
protected deletedEvents = [];
|
||||
|
||||
|
@ -70,7 +70,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
|||
eventsLoaded = false;
|
||||
events = []; // Events (both online and offline).
|
||||
notificationsEnabled = false;
|
||||
filteredEvents = [];
|
||||
filteredEvents: AddonCalendarGetEventsEvent[] = [];
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
courseId: number;
|
||||
|
@ -402,7 +402,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
|||
*
|
||||
* @return Filtered events.
|
||||
*/
|
||||
protected getFilteredEvents(): any[] {
|
||||
protected getFilteredEvents(): AddonCalendarGetEventsEvent[] {
|
||||
if (!this.courseId) {
|
||||
// No filter, display everything.
|
||||
return this.events;
|
||||
|
@ -581,7 +581,7 @@ export class AddonCalendarListPage implements OnDestroy {
|
|||
* @param event Event info.
|
||||
* @return If date has changed and should be shown.
|
||||
*/
|
||||
protected endsSameDay(event: any): boolean {
|
||||
protected endsSameDay(event: AddonCalendarGetEventsEvent): boolean {
|
||||
if (!event.timeduration) {
|
||||
// No duration.
|
||||
return true;
|
||||
|
|
|
@ -31,6 +31,7 @@ import { SQLiteDB } from '@classes/sqlitedb';
|
|||
import { AddonCalendarOfflineProvider } from './calendar-offline';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreWSExternalWarning, CoreWSDate } from '@providers/ws';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -489,7 +490,7 @@ export class AddonCalendarProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
|
||||
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.
|
||||
*
|
||||
|
@ -562,8 +547,8 @@ export class AddonCalendarProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the formatted event time.
|
||||
*/
|
||||
formatEventTime(event: any, format: string, useCommonWords: boolean = true, seenDay?: number, showTime: number = 0,
|
||||
siteId?: string): Promise<string> {
|
||||
formatEventTime(event: AddonCalendarAnyEvent, format: string, useCommonWords: boolean = true, seenDay?: number,
|
||||
showTime: number = 0, siteId?: string): Promise<string> {
|
||||
|
||||
const start = event.timestart * 1000,
|
||||
end = (event.timestart + event.timeduration) * 1000;
|
||||
|
@ -635,7 +620,7 @@ export class AddonCalendarProvider {
|
|||
* @return Promise resolved with object with access information.
|
||||
* @since 3.7
|
||||
*/
|
||||
getAccessInformation(courseId?: number, siteId?: string): Promise<any> {
|
||||
getAccessInformation(courseId?: number, siteId?: string): Promise<AddonCalendarGetAccessInfoResult> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params: any = {},
|
||||
preSets = {
|
||||
|
@ -680,7 +665,7 @@ export class AddonCalendarProvider {
|
|||
* @return Promise resolved with an object indicating the types.
|
||||
* @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) => {
|
||||
const params: any = {},
|
||||
preSets = {
|
||||
|
@ -691,7 +676,8 @@ export class AddonCalendarProvider {
|
|||
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.
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
* @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) => {
|
||||
const preSets = {
|
||||
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.
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
* @return Promise resolved when the event data is retrieved.
|
||||
* @since 3.4
|
||||
*/
|
||||
getEventById(id: number, siteId?: string): Promise<any> {
|
||||
getEventById(id: number, siteId?: string): Promise<AddonCalendarEvent> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const preSets = {
|
||||
cacheKey: this.getEventCacheKey(id),
|
||||
|
@ -864,7 +849,8 @@ export class AddonCalendarProvider {
|
|||
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;
|
||||
}).catch((error) => {
|
||||
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.
|
||||
* @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) => {
|
||||
const reminder = {
|
||||
eventid: event.id,
|
||||
|
@ -976,7 +962,7 @@ export class AddonCalendarProvider {
|
|||
* @return Promise resolved with the response.
|
||||
*/
|
||||
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) => {
|
||||
|
||||
|
@ -1003,7 +989,7 @@ export class AddonCalendarProvider {
|
|||
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);
|
||||
|
||||
return response;
|
||||
|
@ -1071,10 +1057,10 @@ export class AddonCalendarProvider {
|
|||
* @param daysToStart Number of days from now to start getting events.
|
||||
* @param daysInterval Number of days between timestart and timeend.
|
||||
* @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,
|
||||
siteId?: string): Promise<any[]> {
|
||||
siteId?: string): Promise<AddonCalendarGetEventsEvent[]> {
|
||||
|
||||
initialTime = initialTime || this.timeUtils.timestamp();
|
||||
|
||||
|
@ -1122,7 +1108,9 @@ export class AddonCalendarProvider {
|
|||
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)) {
|
||||
// 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);
|
||||
|
@ -1178,7 +1166,7 @@ export class AddonCalendarProvider {
|
|||
* @return Promise resolved with the response.
|
||||
*/
|
||||
getMonthlyEvents(year: number, month: number, courseId?: number, categoryId?: number, ignoreCache?: boolean, siteId?: string)
|
||||
: Promise<any> {
|
||||
: Promise<AddonCalendarMonth> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
||||
|
@ -1210,7 +1198,9 @@ export class AddonCalendarProvider {
|
|||
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) => {
|
||||
week.days.forEach((day) => {
|
||||
this.storeEventsInLocalDB(day.events, siteId);
|
||||
|
@ -1270,7 +1260,8 @@ export class AddonCalendarProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
|
||||
|
@ -1293,7 +1284,7 @@ export class AddonCalendarProvider {
|
|||
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);
|
||||
|
||||
return response;
|
||||
|
@ -1604,11 +1595,14 @@ export class AddonCalendarProvider {
|
|||
* If local notification plugin is not enabled, resolve the promise.
|
||||
*
|
||||
* @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 siteId Site ID the event belongs to. If not defined, use current site.
|
||||
* @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()) {
|
||||
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.
|
||||
* @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()) {
|
||||
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 forceOffline True to always save it in offline.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the event and a boolean indicating if data was
|
||||
* sent to server or stored in offline.
|
||||
* @return Promise resolved with the event and a boolean indicating if data was sent to server or stored in offline.
|
||||
*/
|
||||
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();
|
||||
|
||||
|
@ -1847,7 +1840,7 @@ export class AddonCalendarProvider {
|
|||
* @param siteId Site ID. If not provided, current site.
|
||||
* @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) => {
|
||||
// Add data that is "hidden" in web.
|
||||
formData.id = eventId || 0;
|
||||
|
@ -1865,10 +1858,12 @@ export class AddonCalendarProvider {
|
|||
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) {
|
||||
// Simulate a WS error.
|
||||
return Promise.reject({
|
||||
return <any> Promise.reject({
|
||||
message: this.translate.instant('core.invalidformdata'),
|
||||
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 { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { AddonCalendarProvider } from './calendar';
|
||||
import { AddonCalendarProvider, AddonCalendarCalendarEvent } from './calendar';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { CoreConfigProvider } from '@providers/config';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
|
@ -130,11 +130,11 @@ export class AddonCalendarHelperProvider {
|
|||
*
|
||||
* @param e Event to format.
|
||||
*/
|
||||
formatEventData(e: any): void {
|
||||
e.icon = this.EVENTICONS[e.eventtype] || false;
|
||||
if (!e.icon) {
|
||||
e.icon = this.courseProvider.getModuleIconSrc(e.modulename);
|
||||
e.moduleIcon = e.icon;
|
||||
formatEventData(e: AddonCalendarCalendarEvent): void {
|
||||
e.eventIcon = this.EVENTICONS[e.eventtype] || '';
|
||||
if (!e.eventIcon) {
|
||||
e.eventIcon = this.courseProvider.getModuleIconSrc(e.modulename);
|
||||
e.moduleIcon = e.eventIcon;
|
||||
}
|
||||
|
||||
e.formattedType = this.calendarProvider.getEventType(e);
|
||||
|
@ -160,7 +160,7 @@ export class AddonCalendarHelperProvider {
|
|||
* @param eventTypes Result of getAllowedEventTypes.
|
||||
* @return Options.
|
||||
*/
|
||||
getEventTypeOptions(eventTypes: any): {name: string, value: string}[] {
|
||||
getEventTypeOptions(eventTypes: {[name: string]: boolean}): {name: string, value: string}[] {
|
||||
const options = [];
|
||||
|
||||
if (eventTypes.user) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, ViewChild, Input } from '@angular/core';
|
|||
import { Content, NavController } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||
import { AddonCompetencyProvider, AddonCompetencyDataForCourseCompetenciesPageResult } from '../../providers/competency';
|
||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ export class AddonCompetencyCourseComponent {
|
|||
@Input() userId: number;
|
||||
|
||||
competenciesLoaded = false;
|
||||
competencies: any;
|
||||
competencies: AddonCompetencyDataForCourseCompetenciesPageResult;
|
||||
user: any;
|
||||
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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.
|
||||
|
@ -36,7 +39,7 @@ export class AddonCompetencyCompetenciesPage {
|
|||
protected userId: number;
|
||||
|
||||
competenciesLoaded = false;
|
||||
competencies = [];
|
||||
competencies: AddonCompetencyDataForPlanPageCompetency[] | AddonCompetencyDataForCourseCompetenciesPageCompetency[] = [];
|
||||
title: string;
|
||||
|
||||
constructor(navParams: NavParams, private translate: TranslateService, private domUtils: CoreDomUtilsProvider,
|
||||
|
@ -59,7 +62,7 @@ export class AddonCompetencyCompetenciesPage {
|
|||
this.fetchCompetencies().then(() => {
|
||||
if (!this.competencyId && this.splitviewCtrl.isOn() && this.competencies.length > 0) {
|
||||
// Take first and load it.
|
||||
this.openCompetency(this.competencies[0].id);
|
||||
this.openCompetency(this.competencies[0].competency.id);
|
||||
}
|
||||
}).finally(() => {
|
||||
this.competenciesLoaded = true;
|
||||
|
@ -72,7 +75,7 @@ export class AddonCompetencyCompetenciesPage {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchCompetencies(): Promise<void> {
|
||||
let promise;
|
||||
let promise: Promise<AddonCompetencyDataForPlanPageResult | AddonCompetencyDataForCourseCompetenciesPageResult>;
|
||||
|
||||
if (this.planId) {
|
||||
promise = this.competencyProvider.getLearningPlan(this.planId);
|
||||
|
@ -83,13 +86,16 @@ export class AddonCompetencyCompetenciesPage {
|
|||
}
|
||||
|
||||
return promise.then((response) => {
|
||||
if (response.competencycount <= 0) {
|
||||
return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound'));
|
||||
}
|
||||
|
||||
if (this.planId) {
|
||||
this.title = response.plan.name;
|
||||
this.userId = response.plan.userid;
|
||||
const resp = <AddonCompetencyDataForPlanPageResult> response;
|
||||
|
||||
if (resp.competencycount <= 0) {
|
||||
return Promise.reject(this.translate.instant('addon.competency.errornocompetenciesfound'));
|
||||
}
|
||||
|
||||
this.title = resp.plan.name;
|
||||
this.userId = resp.plan.userid;
|
||||
} else {
|
||||
this.title = this.translate.instant('addon.competency.coursecompetencies');
|
||||
}
|
||||
|
|
|
@ -51,22 +51,22 @@
|
|||
<core-format-text [text]="activity.name"></core-format-text>
|
||||
</a>
|
||||
</ion-item>
|
||||
<ion-item text-wrap *ngIf="competency.usercompetency.status">
|
||||
<ion-item text-wrap *ngIf="userCompetency.status">
|
||||
<strong>{{ 'addon.competency.reviewstatus' | translate }}</strong>
|
||||
{{ competency.usercompetency.statusname }}
|
||||
{{ userCompetency.statusname }}
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<strong>{{ 'addon.competency.proficient' | translate }}</strong>
|
||||
<ion-badge color="success" *ngIf="competency.usercompetency.proficiency">
|
||||
<ion-badge color="success" *ngIf="userCompetency.proficiency">
|
||||
{{ 'core.yes' | translate }}
|
||||
</ion-badge>
|
||||
<ion-badge color="danger" *ngIf="!competency.usercompetency.proficiency">
|
||||
<ion-badge color="danger" *ngIf="!userCompetency.proficiency">
|
||||
{{ 'core.no' | translate }}
|
||||
</ion-badge>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<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-card>
|
||||
|
||||
|
|
|
@ -18,8 +18,14 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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 { CoreUserSummary } from '@core/user/providers/user';
|
||||
import { CoreCourseModuleSummary } from '@core/course/providers/course';
|
||||
|
||||
/**
|
||||
* Page that displays a learning plan.
|
||||
|
@ -36,9 +42,10 @@ export class AddonCompetencyCompetencyPage {
|
|||
courseId: number;
|
||||
userId: number;
|
||||
planStatus: number;
|
||||
coursemodules: any;
|
||||
user: any;
|
||||
competency: any;
|
||||
coursemodules: CoreCourseModuleSummary[];
|
||||
user: CoreUserSummary;
|
||||
competency: AddonCompetencyUserCompetencySummary;
|
||||
userCompetency: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse;
|
||||
|
||||
constructor(private navCtrl: NavController, navParams: NavParams, private translate: TranslateService,
|
||||
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
|
@ -79,7 +86,8 @@ export class AddonCompetencyCompetencyPage {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchCompetency(): Promise<void> {
|
||||
let promise;
|
||||
let promise: Promise<AddonCompetencyUserCompetencySummaryInPlan | AddonCompetencyUserCompetencySummaryInCourse>;
|
||||
|
||||
if (this.planId) {
|
||||
this.planStatus = null;
|
||||
promise = this.competencyProvider.getCompetencyInPlan(this.planId, this.competencyId);
|
||||
|
@ -90,23 +98,21 @@ export class AddonCompetencyCompetencyPage {
|
|||
}
|
||||
|
||||
return promise.then((competency) => {
|
||||
competency.usercompetencysummary.usercompetency = competency.usercompetencysummary.usercompetencyplan ||
|
||||
competency.usercompetencysummary.usercompetency;
|
||||
|
||||
this.competency = competency.usercompetencysummary;
|
||||
this.userCompetency = this.competency.usercompetencyplan || this.competency.usercompetency;
|
||||
|
||||
if (this.planId) {
|
||||
this.planStatus = competency.plan.status;
|
||||
this.planStatus = (<AddonCompetencyUserCompetencySummaryInPlan> competency).plan.status;
|
||||
this.competency.usercompetency.statusname =
|
||||
this.competencyHelperProvider.getCompetencyStatusName(this.competency.usercompetency.status);
|
||||
} else {
|
||||
this.competency.usercompetency = this.competency.usercompetencycourse;
|
||||
this.coursemodules = competency.coursemodules;
|
||||
this.userCompetency = this.competency.usercompetencycourse;
|
||||
this.coursemodules = (<AddonCompetencyUserCompetencySummaryInCourse> competency).coursemodules;
|
||||
}
|
||||
|
||||
if (this.competency.user.id != this.sitesProvider.getCurrentSiteUserId()) {
|
||||
this.competency.user.profileimageurl = this.competency.user.profileimageurl || true;
|
||||
|
||||
// Get the user profile image from the returned object.
|
||||
// Get the user profile from the returned object.
|
||||
this.user = this.competency.user;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Optional } from '@angular/core';
|
|||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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.
|
||||
|
@ -29,7 +29,7 @@ import { AddonCompetencyProvider } from '../../providers/competency';
|
|||
export class AddonCompetencyCompetencySummaryPage {
|
||||
competencyLoaded = false;
|
||||
competencyId: number;
|
||||
competency: any;
|
||||
competency: AddonCompetencySummary;
|
||||
|
||||
constructor(private navCtrl: NavController, navParams: NavParams, private domUtils: CoreDomUtilsProvider,
|
||||
@Optional() private svComponent: CoreSplitViewComponent, private competencyProvider: AddonCompetencyProvider) {
|
||||
|
@ -41,8 +41,7 @@ export class AddonCompetencyCompetencySummaryPage {
|
|||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.fetchCompetency().then(() => {
|
||||
const name = this.competency.competency && this.competency.competency.competency &&
|
||||
this.competency.competency.competency.shortname;
|
||||
const name = this.competency.competency && this.competency.competency.shortname;
|
||||
|
||||
this.competencyProvider.logCompetencyView(this.competencyId, name).catch(() => {
|
||||
// Ignore errors.
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
</ion-item>
|
||||
<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>
|
||||
<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>
|
||||
</ion-list>
|
||||
</ion-card>
|
||||
|
|
|
@ -17,7 +17,7 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
|||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||
import { AddonCompetencyProvider, AddonCompetencyDataForPlanPageResult } from '../../providers/competency';
|
||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
|||
export class AddonCompetencyPlanPage {
|
||||
protected planId: number;
|
||||
planLoaded = false;
|
||||
plan: any;
|
||||
plan: AddonCompetencyDataForPlanPageResult;
|
||||
user: any;
|
||||
|
||||
constructor(private navCtrl: NavController, navParams: NavParams, private appProvider: CoreAppProvider,
|
||||
|
@ -62,9 +62,6 @@ export class AddonCompetencyPlanPage {
|
|||
this.user = user;
|
||||
});
|
||||
|
||||
plan.competencies.forEach((competency) => {
|
||||
competency.usercompetency = competency.usercompetencyplan || competency.usercompetency;
|
||||
});
|
||||
this.plan = plan;
|
||||
}).catch((message) => {
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { AddonCompetencyProvider } from '../../providers/competency';
|
||||
import { AddonCompetencyProvider, AddonCompetencyPlan } from '../../providers/competency';
|
||||
import { AddonCompetencyHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ export class AddonCompetencyPlanListPage {
|
|||
protected userId: number;
|
||||
protected planId: number;
|
||||
plansLoaded = false;
|
||||
plans = [];
|
||||
plans: AddonCompetencyPlan[] = [];
|
||||
|
||||
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private competencyProvider: AddonCompetencyProvider,
|
||||
private competencyHelperProvider: AddonCompetencyHelperProvider) {
|
||||
|
@ -66,7 +66,7 @@ export class AddonCompetencyPlanListPage {
|
|||
*/
|
||||
protected fetchLearningPlans(): Promise<void> {
|
||||
return this.competencyProvider.getLearningPlans(this.userId).then((plans) => {
|
||||
plans.forEach((plan) => {
|
||||
plans.forEach((plan: AddonCompetencyPlanFormatted) => {
|
||||
plan.statusname = this.competencyHelperProvider.getPlanStatusName(plan.status);
|
||||
switch (plan.status) {
|
||||
case AddonCompetencyProvider.STATUS_ACTIVE:
|
||||
|
@ -109,3 +109,10 @@ export class AddonCompetencyPlanListPage {
|
|||
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 { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||
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.
|
||||
|
@ -147,7 +150,7 @@ export class AddonCompetencyProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
@ -161,7 +164,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
return response.plans;
|
||||
}
|
||||
|
@ -176,9 +181,9 @@ export class AddonCompetencyProvider {
|
|||
*
|
||||
* @param planId ID of the plan.
|
||||
* @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) => {
|
||||
|
||||
this.logger.debug('Get plan ' + planId);
|
||||
|
@ -191,7 +196,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -207,9 +214,11 @@ export class AddonCompetencyProvider {
|
|||
* @param planId ID of the plan.
|
||||
* @param competencyId ID of the competency.
|
||||
* @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) => {
|
||||
|
||||
this.logger.debug('Get competency ' + competencyId + ' in plan ' + planId);
|
||||
|
@ -223,7 +232,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -241,10 +252,10 @@ export class AddonCompetencyProvider {
|
|||
* @param userId ID of the user. If not defined, current user.
|
||||
* @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).
|
||||
* @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)
|
||||
: Promise<any> {
|
||||
: Promise<AddonCompetencyUserCompetencySummaryInCourse> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -266,7 +277,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -283,9 +296,11 @@ export class AddonCompetencyProvider {
|
|||
* @param userId ID of the user. If not defined, current user.
|
||||
* @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).
|
||||
* @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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
@ -305,7 +320,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
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).
|
||||
* @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) => {
|
||||
|
||||
this.logger.debug('Get course competencies for course ' + courseId);
|
||||
|
@ -342,7 +361,9 @@ export class AddonCompetencyProvider {
|
|||
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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -356,11 +377,13 @@ export class AddonCompetencyProvider {
|
|||
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)
|
||||
);
|
||||
|
||||
return Promise.all(promises).then((responses: any[]) => {
|
||||
return Promise.all(promises).then((responses: AddonCompetencyUserCompetencySummaryInCourse[]) => {
|
||||
responses.forEach((resp, index) => {
|
||||
response.competencies[index].usercompetencycourse = resp.usercompetencysummary.usercompetencycourse;
|
||||
});
|
||||
|
@ -486,7 +509,7 @@ export class AddonCompetencyProvider {
|
|||
* @return Promise resolved when the WS call is successful.
|
||||
*/
|
||||
logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number,
|
||||
siteId?: string): Promise<any> {
|
||||
siteId?: string): Promise<void> {
|
||||
if (planId && competencyId) {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
@ -509,7 +532,11 @@ export class AddonCompetencyProvider {
|
|||
userid: userId
|
||||
}, 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.
|
||||
*/
|
||||
logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string)
|
||||
: Promise<any> {
|
||||
: Promise<void> {
|
||||
|
||||
if (courseId && competencyId) {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
@ -548,7 +575,11 @@ export class AddonCompetencyProvider {
|
|||
userid: userId
|
||||
}, 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.
|
||||
* @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) {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
|
@ -576,10 +607,401 @@ export class AddonCompetencyProvider {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-item text-wrap>
|
||||
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
||||
<p>{{ completion.statusText | translate }}</p>
|
||||
<p>{{ statusText | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<h2>{{ 'addon.coursecompletion.required' | translate }}</h2>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonCourseCompletionProvider } from '../../providers/coursecompletion';
|
||||
import { AddonCourseCompletionProvider, AddonCourseCompletionCourseCompletionStatus } from '../../providers/coursecompletion';
|
||||
|
||||
/**
|
||||
* Component that displays the course completion report.
|
||||
|
@ -29,9 +29,10 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
|||
@Input() userId: number;
|
||||
|
||||
completionLoaded = false;
|
||||
completion: any;
|
||||
completion: AddonCourseCompletionCourseCompletionStatus;
|
||||
showSelfComplete: boolean;
|
||||
tracked = true; // Whether completion is tracked.
|
||||
statusText: string;
|
||||
|
||||
constructor(
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
|
@ -59,7 +60,7 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
|||
protected fetchCompletion(): Promise<any> {
|
||||
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.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service to handle course completion.
|
||||
|
@ -43,7 +44,7 @@ export class AddonCourseCompletionProvider {
|
|||
* @param completion Course completion.
|
||||
* @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,
|
||||
alreadyMarked = false;
|
||||
|
||||
|
@ -68,7 +69,7 @@ export class AddonCourseCompletionProvider {
|
|||
* @param completion Course completion.
|
||||
* @return Language code of the text to show.
|
||||
*/
|
||||
getCompletedStatusText(completion: any): string {
|
||||
getCompletedStatusText(completion: AddonCourseCompletionCourseCompletionStatus): string {
|
||||
if (completion.completed) {
|
||||
return 'addon.coursecompletion.completed';
|
||||
} else {
|
||||
|
@ -96,7 +97,9 @@ export class AddonCourseCompletionProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @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) => {
|
||||
userId = userId || site.getUserId();
|
||||
preSets = preSets || {};
|
||||
|
@ -112,7 +115,9 @@ export class AddonCourseCompletionProvider {
|
|||
preSets.updateFrequency = preSets.updateFrequency || CoreSite.FREQUENCY_SOMETIMES;
|
||||
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) {
|
||||
return data.completionstatus;
|
||||
}
|
||||
|
@ -243,17 +248,56 @@ export class AddonCourseCompletionProvider {
|
|||
* Mark a course as self completed.
|
||||
*
|
||||
* @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 = {
|
||||
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) {
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { AddonFilesProvider } from '../../providers/files';
|
||||
import { AddonFilesProvider, AddonFilesFile, AddonFilesGetUserPrivateFilesInfoResult } from '../../providers/files';
|
||||
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'.
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
filesLoaded: boolean; // Whether the files are loaded.
|
||||
|
||||
|
@ -147,7 +147,7 @@ export class AddonFilesListPage implements OnDestroy {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchFiles(): Promise<any> {
|
||||
let promise;
|
||||
let promise: Promise<AddonFilesFile[]>;
|
||||
|
||||
if (!this.path) {
|
||||
// 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 { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service to handle my files and site files.
|
||||
|
@ -73,7 +74,7 @@ export class AddonFilesProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const preSets = {
|
||||
|
@ -82,15 +83,15 @@ export class AddonFilesProvider {
|
|||
};
|
||||
|
||||
return site.read('core_files_get_files', params, preSets);
|
||||
}).then((result) => {
|
||||
const entries = [];
|
||||
}).then((result: AddonFilesGetFilesResult) => {
|
||||
const entries: AddonFilesFile[] = [];
|
||||
|
||||
if (result.files) {
|
||||
result.files.forEach((entry) => {
|
||||
if (entry.isdir) {
|
||||
// Create a "link" to load the folder.
|
||||
entry.link = {
|
||||
contextid: entry.contextid || '',
|
||||
contextid: entry.contextid || null,
|
||||
component: entry.component || '',
|
||||
filearea: entry.filearea || '',
|
||||
itemid: entry.itemid || 0,
|
||||
|
@ -135,7 +136,7 @@ export class AddonFilesProvider {
|
|||
*
|
||||
* @return Promise resolved with the files.
|
||||
*/
|
||||
getPrivateFiles(): Promise<any[]> {
|
||||
getPrivateFiles(): Promise<AddonFilesFile[]> {
|
||||
return this.getFiles(this.getPrivateFilesRootParams());
|
||||
}
|
||||
|
||||
|
@ -164,7 +165,7 @@ export class AddonFilesProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
@ -204,7 +205,7 @@ export class AddonFilesProvider {
|
|||
*
|
||||
* @return Promise resolved with the files.
|
||||
*/
|
||||
getSiteFiles(): Promise<any[]> {
|
||||
getSiteFiles(): Promise<AddonFilesFile[]> {
|
||||
return this.getFiles(this.getSiteFilesRootParams());
|
||||
}
|
||||
|
||||
|
@ -388,7 +389,7 @@ export class AddonFilesProvider {
|
|||
* @param siteid ID of the site. If not defined, use current site.
|
||||
* @return Promise resolved in success, rejected otherwise.
|
||||
*/
|
||||
moveFromDraftToPrivate(draftId: number, siteId?: string): Promise<any> {
|
||||
moveFromDraftToPrivate(draftId: number, siteId?: string): Promise<null> {
|
||||
const params = {
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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.
|
||||
|
@ -28,7 +28,7 @@ import { AddonMessageOutputAirnotifierProvider } from '../../providers/airnotifi
|
|||
})
|
||||
export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
||||
|
||||
devices = [];
|
||||
devices: AddonMessageOutputAirnotifierDeviceFormatted[] = [];
|
||||
devicesLoaded = false;
|
||||
|
||||
protected updateTimeout: any;
|
||||
|
@ -54,7 +54,7 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
|||
const pushId = this.pushNotificationsProvider.getPushId();
|
||||
|
||||
// Convert enabled to boolean and search current device.
|
||||
devices.forEach((device) => {
|
||||
devices.forEach((device: AddonMessageOutputAirnotifierDeviceFormatted) => {
|
||||
device.enable = !!device.enable;
|
||||
device.current = pushId && pushId == device.pushid;
|
||||
});
|
||||
|
@ -110,8 +110,9 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnDestroy {
|
|||
* @param device The device object.
|
||||
* @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;
|
||||
|
||||
this.airnotifierProivder.enableDevice(device.id, enable).then(() => {
|
||||
// Update the list of devices since it was modified.
|
||||
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 { CoreConfigConstants } from '../../../../configconstants';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service to handle Airnotifier message output.
|
||||
|
@ -39,14 +40,16 @@ export class AddonMessageOutputAirnotifierProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const data = {
|
||||
deviceid: deviceId,
|
||||
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) {
|
||||
// Fail. Reject with warning message if any.
|
||||
if (result.warnings && result.warnings.length) {
|
||||
|
@ -74,7 +77,7 @@ export class AddonMessageOutputAirnotifierProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @return Promise resolved with the devices.
|
||||
*/
|
||||
getUserDevices(siteId?: string): Promise<any> {
|
||||
getUserDevices(siteId?: string): Promise<AddonMessageOutputAirnotifierDevice[]> {
|
||||
this.logger.debug('Get user devices');
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
@ -86,7 +89,8 @@ export class AddonMessageOutputAirnotifierProvider {
|
|||
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;
|
||||
});
|
||||
});
|
||||
|
@ -115,3 +119,36 @@ export class AddonMessageOutputAirnotifierProvider {
|
|||
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 { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ export class AddonMessagesConfirmedContactsComponent implements OnInit, OnDestro
|
|||
loaded = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
contacts = [];
|
||||
contacts: AddonMessagesConversationMember[] = [];
|
||||
selectedUserId: number;
|
||||
|
||||
protected memberInfoObserver;
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@
|
|||
import { Content } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import { AddonMessagesProvider, AddonMessagesConversationMember } from '../../providers/messages';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ export class AddonMessagesContactRequestsComponent implements OnInit, OnDestroy
|
|||
loaded = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
requests = [];
|
||||
requests: AddonMessagesConversationMember[] = [];
|
||||
selectedUserId: number;
|
||||
|
||||
protected memberInfoObserver;
|
||||
|
|
|
@ -16,7 +16,9 @@ import { Component } from '@angular/core';
|
|||
import { NavParams } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import {
|
||||
AddonMessagesProvider, AddonMessagesGetContactsResult, AddonMessagesSearchContactsContact
|
||||
} from '../../providers/messages';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
|
@ -42,7 +44,10 @@ export class AddonMessagesContactsComponent {
|
|||
searchType = 'search';
|
||||
loadingMessage = '';
|
||||
hasContacts = false;
|
||||
contacts = {
|
||||
contacts: AddonMessagesGetContactsFormatted = {
|
||||
online: [],
|
||||
offline: [],
|
||||
strangers: [],
|
||||
search: []
|
||||
};
|
||||
searchString = '';
|
||||
|
@ -205,7 +210,7 @@ export class AddonMessagesContactsComponent {
|
|||
this.searchString = query;
|
||||
this.contactTypes = ['search'];
|
||||
|
||||
this.contacts['search'] = this.sortUsers(result);
|
||||
this.contacts.search = this.sortUsers(result);
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingcontacts', true);
|
||||
});
|
||||
|
@ -234,3 +239,10 @@ export class AddonMessagesContactsComponent {
|
|||
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 { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import {
|
||||
AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMember
|
||||
} from '../../providers/messages';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
||||
/**
|
||||
|
@ -28,8 +30,8 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
|||
export class AddonMessagesConversationInfoPage implements OnInit {
|
||||
|
||||
loaded = false;
|
||||
conversation: any;
|
||||
members = [];
|
||||
conversation: AddonMessagesConversationFormatted;
|
||||
members: AddonMessagesConversationMember[] = [];
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
|
||||
|
|
|
@ -17,7 +17,10 @@ import { IonicPage, NavParams, NavController, Content, ModalController } from 'i
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
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 { AddonMessagesSyncProvider } from '../../providers/sync';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
|
@ -54,7 +57,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
protected messagesBeingSent = 0;
|
||||
protected pagesLoaded = 1;
|
||||
protected lastMessage = {text: '', timecreated: 0};
|
||||
protected keepMessageMap = {};
|
||||
protected keepMessageMap: {[hash: string]: boolean} = {};
|
||||
protected syncObserver: any;
|
||||
protected oldContentHeight = 0;
|
||||
protected keyboardObserver: any;
|
||||
|
@ -64,7 +67,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
protected showLoadingModal = false; // Whether to show a loading modal while fetching data.
|
||||
|
||||
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).
|
||||
currentUserId: number;
|
||||
title: string;
|
||||
|
@ -74,18 +77,18 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
showKeyboard = false;
|
||||
canLoadMore = false;
|
||||
loadMoreError = false;
|
||||
messages = [];
|
||||
messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[] = [];
|
||||
showDelete = false;
|
||||
canDelete = false;
|
||||
groupMessagingEnabled: boolean;
|
||||
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';
|
||||
favouriteIconSlash = false;
|
||||
deleteIcon = 'trash';
|
||||
blockIcon = 'close-circle';
|
||||
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';
|
||||
requestContactSent = false;
|
||||
requestContactReceived = false;
|
||||
|
@ -139,7 +142,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param message Message to be added.
|
||||
* @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
|
||||
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 + '#' +
|
||||
|
@ -158,7 +163,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
*
|
||||
* @param hash Hash of the message to be removed.
|
||||
*/
|
||||
protected removeMessage(hash: any): void {
|
||||
protected removeMessage(hash: string): void {
|
||||
if (this.keepMessageMap[hash]) {
|
||||
// Selected to keep it, clear the flag.
|
||||
this.keepMessageMap[hash] = false;
|
||||
|
@ -261,10 +266,11 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
if (!this.title && this.messages.length) {
|
||||
// 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.
|
||||
if (this.messages[0].useridto != this.currentUserId) {
|
||||
this.title = this.messages[0].usertofullname || '';
|
||||
const firstMessage = <AddonMessagesGetMessagesMessageFormatted> this.messages[0];
|
||||
if (firstMessage.useridto != this.currentUserId) {
|
||||
this.title = firstMessage.usertofullname || '';
|
||||
} else {
|
||||
this.title = this.messages[0].userfromfullname || '';
|
||||
this.title = firstMessage.userfromfullname || '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -302,7 +308,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
*
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
protected fetchMessages(): Promise<any> {
|
||||
protected fetchMessages(): Promise<void> {
|
||||
this.loadMoreError = false;
|
||||
|
||||
if (this.messagesBeingSent > 0) {
|
||||
|
@ -341,7 +347,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
return this.getDiscussionMessages(this.pagesLoaded);
|
||||
});
|
||||
}
|
||||
}).then((messages) => {
|
||||
}).then((messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[]) => {
|
||||
this.loadMessages(messages);
|
||||
}).finally(() => {
|
||||
this.fetching = false;
|
||||
|
@ -353,7 +359,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
*
|
||||
* @param messages Messages to load.
|
||||
*/
|
||||
protected loadMessages(messages: any[]): void {
|
||||
protected loadMessages(messages: (AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted)[])
|
||||
: void {
|
||||
|
||||
if (this.viewDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
@ -382,7 +390,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
this.messagesProvider.sortMessages(this.messages);
|
||||
|
||||
// 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.showUserData = this.showUserData(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.
|
||||
*/
|
||||
protected getConversation(conversationId: number, userId: number): Promise<boolean> {
|
||||
let promise,
|
||||
fallbackConversation;
|
||||
let promise: Promise<number>,
|
||||
fallbackConversation: AddonMessagesConversationFormatted;
|
||||
|
||||
// Try to get the conversationId if we don't have it.
|
||||
if (conversationId) {
|
||||
promise = Promise.resolve(conversationId);
|
||||
} else {
|
||||
let subPromise: Promise<AddonMessagesConversationFormatted>;
|
||||
|
||||
if (userId == this.currentUserId && this.messagesProvider.isSelfConversationEnabled()) {
|
||||
promise = this.messagesProvider.getSelfConversation();
|
||||
subPromise = this.messagesProvider.getSelfConversation();
|
||||
} 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;
|
||||
|
||||
return conversation.id;
|
||||
|
@ -437,14 +447,14 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
// Ignore errors.
|
||||
}).then(() => {
|
||||
return this.messagesProvider.getConversation(conversationId, undefined, true);
|
||||
}).catch((error) => {
|
||||
}).catch((error): any => {
|
||||
// Get conversation failed, use the fallback one if we have it.
|
||||
if (fallbackConversation) {
|
||||
return fallbackConversation;
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}).then((conversation) => {
|
||||
}).then((conversation: AddonMessagesConversationFormatted) => {
|
||||
this.conversation = conversation;
|
||||
|
||||
if (conversation) {
|
||||
|
@ -495,7 +505,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param offset Offset for message list.
|
||||
* @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;
|
||||
|
||||
return this.messagesProvider.getConversationMessages(this.conversationId, excludePending, offset).then((result) => {
|
||||
|
@ -535,7 +547,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
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".
|
||||
const excludePending = lfReceivedUnread > 0 || lfReceivedRead > 0 || lfSentUnread > 0 || lfSentRead > 0;
|
||||
|
@ -547,7 +559,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
pagesToLoad--;
|
||||
if (pagesToLoad > 0 && result.canLoadMore) {
|
||||
// More pages to load. Calculate new limit froms.
|
||||
result.messages.forEach((message) => {
|
||||
result.messages.forEach((message: AddonMessagesGetMessagesMessageFormatted) => {
|
||||
if (!message.pending) {
|
||||
if (message.useridfrom == this.userId) {
|
||||
if (message.read) {
|
||||
|
@ -598,7 +610,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
for (const x in this.messages) {
|
||||
const message = this.messages[x];
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
|
@ -616,7 +629,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
promise = this.messagesProvider.markAllMessagesRead(this.userId).then(() => {
|
||||
// Mark all messages as read.
|
||||
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.
|
||||
this.messages.forEach((message) => {
|
||||
// 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(() => {
|
||||
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) {
|
||||
found++;
|
||||
if (found == this.conversation.unreadcount) {
|
||||
this.unreadMessageFrom = parseInt(message.id, 10);
|
||||
this.unreadMessageFrom = Number(message.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -713,13 +726,13 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
let previousMessageRead = false;
|
||||
|
||||
for (const x in this.messages) {
|
||||
const message = this.messages[x];
|
||||
const message = <AddonMessagesGetMessagesMessageFormatted> this.messages[x];
|
||||
if (message.useridfrom != this.currentUserId) {
|
||||
const unreadFrom = message.read == 0 && previousMessageRead;
|
||||
|
||||
if (unreadFrom) {
|
||||
// Save where the label is placed.
|
||||
this.unreadMessageFrom = parseInt(message.id, 10);
|
||||
this.unreadMessageFrom = Number(message.id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -808,8 +821,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
*
|
||||
* @param message Message to be copied.
|
||||
*/
|
||||
copyMessage(message: any): void {
|
||||
const text = this.textUtils.decodeHTMLEntities(message.smallmessage || message.text || '');
|
||||
copyMessage(message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted): void {
|
||||
const text = this.textUtils.decodeHTMLEntities(
|
||||
(<AddonMessagesGetMessagesMessageFormatted> message).smallmessage || message.text || '');
|
||||
this.utils.copyToClipboard(text);
|
||||
}
|
||||
|
||||
|
@ -819,7 +833,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param message Message object to delete.
|
||||
* @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,
|
||||
langKey = message.pending || canDeleteAll || this.isSelf ? 'core.areyousure' :
|
||||
'addon.messages.deletemessageconfirmation',
|
||||
|
@ -860,7 +876,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
loadPrevious(infiniteComplete?: any): Promise<any> {
|
||||
loadPrevious(infiniteComplete?: any): Promise<void> {
|
||||
let infiniteHeight = this.infinite ? this.infinite.getHeight() : 0;
|
||||
const scrollHeight = this.domUtils.getScrollHeight(this.content);
|
||||
|
||||
|
@ -962,7 +978,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param text Message text.
|
||||
*/
|
||||
sendMessage(text: string): void {
|
||||
let message;
|
||||
let message: AddonMessagesConversationMessageFormatted | AddonMessagesGetMessagesMessageFormatted;
|
||||
|
||||
this.hideUnreadLabel();
|
||||
|
||||
|
@ -970,6 +986,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
this.scrollBottom = true;
|
||||
|
||||
message = {
|
||||
id: null,
|
||||
pending: true,
|
||||
sending: true,
|
||||
useridfrom: this.currentUserId,
|
||||
|
@ -985,7 +1002,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
// 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.
|
||||
this.waitForFetch().finally(() => {
|
||||
let promise;
|
||||
let promise: Promise<{sent: boolean, message: any}>;
|
||||
|
||||
if (this.conversationId) {
|
||||
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.
|
||||
* @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) {
|
||||
// First message, show it.
|
||||
return true;
|
||||
|
@ -1068,7 +1087,9 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param prevMessage Previous message.
|
||||
* @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] &&
|
||||
(!prevMessage || prevMessage.useridfrom != message.useridfrom || message.showDate);
|
||||
}
|
||||
|
@ -1080,7 +1101,8 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
* @param nextMessage Next message.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -1422,3 +1444,26 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
|
|||
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 { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonMessagesProvider } from '../../providers/messages';
|
||||
import {
|
||||
AddonMessagesProvider, AddonMessagesConversationFormatted, AddonMessagesConversationMessage
|
||||
} from '../../providers/messages';
|
||||
import { AddonMessagesOfflineProvider } from '../../providers/messages-offline';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
|
@ -45,19 +47,19 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
selectedConversationId: number;
|
||||
selectedUserId: number;
|
||||
contactRequestsCount = 0;
|
||||
favourites: any = {
|
||||
favourites: AddonMessagesGroupConversationOption = {
|
||||
type: null,
|
||||
favourites: true,
|
||||
count: 0,
|
||||
unread: 0
|
||||
unread: 0,
|
||||
};
|
||||
group: any = {
|
||||
group: AddonMessagesGroupConversationOption = {
|
||||
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_GROUP,
|
||||
favourites: false,
|
||||
count: 0,
|
||||
unread: 0
|
||||
};
|
||||
individual: any = {
|
||||
individual: AddonMessagesGroupConversationOption = {
|
||||
type: AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
favourites: false,
|
||||
count: 0,
|
||||
|
@ -331,7 +333,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchDataForExpandedOption(): Promise<any> {
|
||||
protected fetchDataForExpandedOption(): Promise<void> {
|
||||
const expandedOption = this.getExpandedOption();
|
||||
|
||||
if (expandedOption) {
|
||||
|
@ -349,12 +351,12 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param getCounts Whether to get counts data.
|
||||
* @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;
|
||||
|
||||
const limitFrom = loadingMore ? option.conversations.length : 0,
|
||||
promises = [];
|
||||
let data,
|
||||
let data: {conversations: AddonMessagesConversationForList[], canLoadMore: boolean},
|
||||
offlineMessages;
|
||||
|
||||
// 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.
|
||||
* @return Conversation.
|
||||
*/
|
||||
protected findConversation(conversationId: number, userId?: number, option?: any): any {
|
||||
protected findConversation(conversationId: number, userId?: number, option?: AddonMessagesGroupConversationOption)
|
||||
: AddonMessagesConversationForList {
|
||||
|
||||
if (conversationId) {
|
||||
const conversations = option ? (option.conversations || []) : ((this.favourites.conversations || [])
|
||||
.concat(this.group.conversations || []).concat(this.individual.conversations || []));
|
||||
|
@ -445,7 +449,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @return Option currently expanded.
|
||||
*/
|
||||
protected getExpandedOption(): any {
|
||||
protected getExpandedOption(): AddonMessagesGroupConversationOption {
|
||||
if (this.favourites.expanded) {
|
||||
return this.favourites;
|
||||
} else if (this.group.expanded) {
|
||||
|
@ -495,9 +499,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param option The option to fetch data for.
|
||||
* @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) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true);
|
||||
option.loadMoreError = true;
|
||||
|
@ -513,7 +517,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param messages Offline messages.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected loadOfflineMessages(option: any, messages: any[]): Promise<any> {
|
||||
protected loadOfflineMessages(option: AddonMessagesGroupConversationOption, messages: any[]): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
messages.forEach((message) => {
|
||||
|
@ -588,7 +592,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param conversation Conversation where to put the last message.
|
||||
* @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.lastmessagedate = message.timecreated / 1000;
|
||||
conversation.lastmessagepending = true;
|
||||
|
@ -601,7 +605,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param conversation Conversation to check.
|
||||
* @return Option object.
|
||||
*/
|
||||
protected getConversationOption(conversation: any): any {
|
||||
protected getConversationOption(conversation: AddonMessagesConversationForList): AddonMessagesGroupConversationOption {
|
||||
if (conversation.isfavourite) {
|
||||
return this.favourites;
|
||||
} 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.
|
||||
* @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.
|
||||
const promises = [
|
||||
this.messagesProvider.invalidateContactRequestsCountCache(this.siteId)
|
||||
|
@ -638,7 +642,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param option The option to expand/collapse.
|
||||
*/
|
||||
toggle(option: any): void {
|
||||
toggle(option: AddonMessagesGroupConversationOption): void {
|
||||
if (option.expanded) {
|
||||
// Already expanded, close it.
|
||||
option.expanded = false;
|
||||
|
@ -658,7 +662,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param getCounts Whether to get counts data.
|
||||
* @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.
|
||||
this.favourites.expanded = false;
|
||||
this.group.expanded = false;
|
||||
|
@ -715,3 +719,25 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
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 { CoreEventsProvider } from '@providers/events';
|
||||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
|
@ -38,21 +38,21 @@ export class AddonMessagesSearchPage implements OnDestroy {
|
|||
contacts = {
|
||||
type: 'contacts',
|
||||
titleString: 'addon.messages.contacts',
|
||||
results: [],
|
||||
results: <AddonMessagesConversationMember[]> [],
|
||||
canLoadMore: false,
|
||||
loadingMore: false
|
||||
};
|
||||
nonContacts = {
|
||||
type: 'noncontacts',
|
||||
titleString: 'addon.messages.noncontacts',
|
||||
results: [],
|
||||
results: <AddonMessagesConversationMember[]> [],
|
||||
canLoadMore: false,
|
||||
loadingMore: false
|
||||
};
|
||||
messages = {
|
||||
type: 'messages',
|
||||
titleString: 'addon.messages.messages',
|
||||
results: [],
|
||||
results: <AddonMessagesMessageAreaContact[]> [],
|
||||
canLoadMore: false,
|
||||
loadingMore: false,
|
||||
loadMoreError: false
|
||||
|
@ -116,9 +116,9 @@ export class AddonMessagesSearchPage implements OnDestroy {
|
|||
this.displaySearching = !loadMore;
|
||||
|
||||
const promises = [];
|
||||
let newContacts = [];
|
||||
let newNonContacts = [];
|
||||
let newMessages = [];
|
||||
let newContacts: AddonMessagesConversationMember[] = [];
|
||||
let newNonContacts: AddonMessagesConversationMember[] = [];
|
||||
let newMessages: AddonMessagesMessageAreaContact[] = [];
|
||||
let canLoadMoreContacts = false;
|
||||
let canLoadMoreNonContacts = false;
|
||||
let canLoadMoreMessages = false;
|
||||
|
|
|
@ -14,7 +14,10 @@
|
|||
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
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 { CoreAppProvider } from '@providers/app';
|
||||
import { CoreConfigProvider } from '@providers/config';
|
||||
|
@ -34,7 +37,7 @@ import { CoreConstants } from '@core/constants';
|
|||
export class AddonMessagesSettingsPage implements OnDestroy {
|
||||
protected updateTimeout: any;
|
||||
|
||||
preferences: any;
|
||||
preferences: AddonMessagesMessagePreferences;
|
||||
preferencesLoaded: boolean;
|
||||
contactablePrivacy: number | boolean;
|
||||
advancedContactable = false; // Whether the site supports "advanced" contactable privacy.
|
||||
|
@ -78,9 +81,9 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
/**
|
||||
* 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) => {
|
||||
if (this.groupMessagingEnabled) {
|
||||
// Simplify the preferences.
|
||||
|
@ -90,11 +93,12 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
return notification.preferencekey == AddonMessagesProvider.NOTIFICATION_PREFERENCES_KEY;
|
||||
});
|
||||
|
||||
for (const notification of component.notifications) {
|
||||
for (const processor of notification.processors) {
|
||||
component.notifications.forEach((notification) => {
|
||||
notification.processors.forEach(
|
||||
(processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => {
|
||||
processor.checked = processor.loggedin.checked || processor.loggedoff.checked;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,14 +172,16 @@ export class AddonMessagesSettingsPage implements OnDestroy {
|
|||
* @param state State name, ['loggedin', 'loggedoff'].
|
||||
* @param processor Notification processor.
|
||||
*/
|
||||
changePreference(notification: any, state: string, processor: any): void {
|
||||
changePreference(notification: AddonMessagesMessagePreferencesNotificationFormatted, state: string,
|
||||
processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted): void {
|
||||
|
||||
if (this.groupMessagingEnabled) {
|
||||
// Update both states at the same time.
|
||||
const valueArray = [],
|
||||
promises = [];
|
||||
let value = 'none';
|
||||
|
||||
notification.processors.forEach((processor) => {
|
||||
notification.processors.forEach((processor: AddonMessagesMessagePreferencesNotificationProcessorFormatted) => {
|
||||
if (processor.checked) {
|
||||
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(),
|
||||
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) {
|
||||
// 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 { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service to handle messages.
|
||||
|
@ -89,9 +90,9 @@ export class AddonMessagesProvider {
|
|||
*
|
||||
* @param userId User ID of the person to block.
|
||||
* @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) => {
|
||||
let promise;
|
||||
if (site.wsAvailable('core_message_block_user')) {
|
||||
|
@ -313,7 +314,9 @@ export class AddonMessagesProvider {
|
|||
* @param userId User ID viewing the conversation.
|
||||
* @return Formatted conversation.
|
||||
*/
|
||||
protected formatConversation(conversation: any, userId: number): any {
|
||||
protected formatConversation(conversation: AddonMessagesConversationFormatted, userId: number)
|
||||
: AddonMessagesConversationFormatted {
|
||||
|
||||
const numMessages = conversation.messages.length,
|
||||
lastMessage = numMessages ? conversation.messages[numMessages - 1] : null;
|
||||
|
||||
|
@ -536,10 +539,10 @@ export class AddonMessagesProvider {
|
|||
* Get all the contacts of the current user.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
getAllContacts(siteId?: string): Promise<any> {
|
||||
getAllContacts(siteId?: string): Promise<AddonMessagesGetContactsResult> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.getContacts(siteId).then((contacts) => {
|
||||
|
@ -562,9 +565,9 @@ export class AddonMessagesProvider {
|
|||
* Get all the users blocked by the current user.
|
||||
*
|
||||
* @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) => {
|
||||
const userId = site.getUserId(),
|
||||
params = {
|
||||
|
@ -585,19 +588,24 @@ export class AddonMessagesProvider {
|
|||
* This excludes the blocked users.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
getContacts(siteId?: string): Promise<any> {
|
||||
getContacts(siteId?: string): Promise<AddonMessagesGetContactsResult> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const preSets = {
|
||||
cacheKey: this.getCacheKeyForContacts(),
|
||||
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.
|
||||
const validContacts = {};
|
||||
const validContacts: AddonMessagesGetContactsResult = {
|
||||
online: [],
|
||||
offline: [],
|
||||
strangers: []
|
||||
};
|
||||
|
||||
for (const typeName in contacts) {
|
||||
if (!validContacts[typeName]) {
|
||||
validContacts[typeName] = [];
|
||||
|
@ -621,11 +629,11 @@ export class AddonMessagesProvider {
|
|||
* @param limitFrom Position of the first contact to fetch.
|
||||
* @param limitNum Number of contacts to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||
* @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
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
|
@ -638,7 +646,9 @@ export class AddonMessagesProvider {
|
|||
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) {
|
||||
return { contacts: [], canLoadMore: false };
|
||||
}
|
||||
|
@ -663,11 +673,11 @@ export class AddonMessagesProvider {
|
|||
* @param limitFrom Position of the first contact request to fetch.
|
||||
* @param limitNum Number of contact requests to fetch. Default is AddonMessagesProvider.LIMIT_CONTACTS.
|
||||
* @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
|
||||
*/
|
||||
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) => {
|
||||
const data = {
|
||||
|
@ -680,7 +690,9 @@ export class AddonMessagesProvider {
|
|||
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) {
|
||||
return { requests: [], canLoadMore: false };
|
||||
}
|
||||
|
@ -716,7 +728,7 @@ export class AddonMessagesProvider {
|
|||
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.
|
||||
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,
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -765,7 +777,7 @@ export class AddonMessagesProvider {
|
|||
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);
|
||||
});
|
||||
});
|
||||
|
@ -792,7 +804,8 @@ export class AddonMessagesProvider {
|
|||
*/
|
||||
getConversationBetweenUsers(otherUserId: number, includeContactRequests?: boolean, includePrivacyInfo?: boolean,
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -813,7 +826,8 @@ export class AddonMessagesProvider {
|
|||
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);
|
||||
});
|
||||
});
|
||||
|
@ -826,12 +840,11 @@ export class AddonMessagesProvider {
|
|||
* @param limitFrom Offset for members list.
|
||||
* @param limitTo Limit of members.
|
||||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @param userId User ID. If not defined, current user in the site.
|
||||
* @return Promise resolved with the response.
|
||||
* @param userId User ID. If not defined, current user in
|
||||
* @since 3.6
|
||||
*/
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -853,18 +866,21 @@ export class AddonMessagesProvider {
|
|||
includeprivacyinfo: 1,
|
||||
};
|
||||
|
||||
return site.read('core_message_get_conversation_members', params, preSets).then((members) => {
|
||||
const result: any = {};
|
||||
return site.read('core_message_get_conversation_members', params, preSets)
|
||||
.then((members: AddonMessagesConversationMember[]) => {
|
||||
|
||||
if (limitTo < 1) {
|
||||
result.canLoadMore = false;
|
||||
result.members = members;
|
||||
return {
|
||||
canLoadMore: false,
|
||||
members: members
|
||||
};
|
||||
} else {
|
||||
result.canLoadMore = members.length > limitTo;
|
||||
result.members = members.slice(0, limitTo);
|
||||
return {
|
||||
canLoadMore: members.length > limitTo,
|
||||
members: members.slice(0, limitTo)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -884,7 +900,8 @@ export class AddonMessagesProvider {
|
|||
* @since 3.6
|
||||
*/
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -913,7 +930,9 @@ export class AddonMessagesProvider {
|
|||
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) {
|
||||
result.canLoadMore = false;
|
||||
result.messages = result.messages;
|
||||
|
@ -975,7 +994,8 @@ export class AddonMessagesProvider {
|
|||
* @since 3.6
|
||||
*/
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -1017,7 +1037,7 @@ export class AddonMessagesProvider {
|
|||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}).then((response) => {
|
||||
}).then((response: AddonMessagesGetConversationsResult) => {
|
||||
// Format the conversations, adding some calculated fields.
|
||||
const conversations = response.conversations.slice(0, this.LIMIT_MESSAGES).map((conversation) => {
|
||||
return this.formatConversation(conversation, userId);
|
||||
|
@ -1053,7 +1073,9 @@ export class AddonMessagesProvider {
|
|||
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 = {
|
||||
favourites: result.favourites,
|
||||
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.
|
||||
*/
|
||||
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) => {
|
||||
const result = {},
|
||||
const result = {
|
||||
messages: <AddonMessagesGetMessagesMessage[]> [],
|
||||
canLoadMore: false
|
||||
},
|
||||
preSets = {
|
||||
cacheKey: this.getCacheKeyForDiscussion(userId)
|
||||
},
|
||||
|
@ -1107,7 +1133,7 @@ export class AddonMessagesProvider {
|
|||
// Get message received by current user.
|
||||
return this.getRecentMessages(params, preSets, lfReceivedUnread, lfReceivedRead, toDisplay, site.getId())
|
||||
.then((response) => {
|
||||
result['messages'] = response;
|
||||
result.messages = response;
|
||||
params.useridto = userId;
|
||||
params.useridfrom = site.getUserId();
|
||||
hasReceived = response.length > 0;
|
||||
|
@ -1115,16 +1141,16 @@ export class AddonMessagesProvider {
|
|||
// Get message sent by current user.
|
||||
return this.getRecentMessages(params, preSets, lfSentUnread, lfSentRead, toDisplay, siteId);
|
||||
}).then((response) => {
|
||||
result['messages'] = result['messages'].concat(response);
|
||||
result.messages = result.messages.concat(response);
|
||||
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.
|
||||
result['canLoadMore'] = true;
|
||||
result['messages'] = this.sortMessages(result['messages']);
|
||||
result['messages'] = result['messages'].slice(-this.LIMIT_MESSAGES);
|
||||
result.canLoadMore = true;
|
||||
result.messages = this.sortMessages(result['messages']);
|
||||
result.messages = result.messages.slice(-this.LIMIT_MESSAGES);
|
||||
} else {
|
||||
result['canLoadMore'] = result['messages'].length == this.LIMIT_MESSAGES && (!hasReceived || !hasSent);
|
||||
result.canLoadMore = result.messages.length == this.LIMIT_MESSAGES && (!hasReceived || !hasSent);
|
||||
}
|
||||
|
||||
if (excludePending) {
|
||||
|
@ -1140,7 +1166,7 @@ export class AddonMessagesProvider {
|
|||
message.text = message.smallmessage;
|
||||
});
|
||||
|
||||
result['messages'] = result['messages'].concat(offlineMessages);
|
||||
result.messages = result.messages.concat(offlineMessages);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
@ -1153,11 +1179,11 @@ export class AddonMessagesProvider {
|
|||
* If the site is 3.6 or higher, please use getConversations.
|
||||
*
|
||||
* @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) => {
|
||||
const discussions = {},
|
||||
const discussions: {[userId: number]: AddonMessagesDiscussion} = {},
|
||||
currentUserId = site.getUserId(),
|
||||
params = {
|
||||
useridto: currentUserId,
|
||||
|
@ -1171,7 +1197,7 @@ export class AddonMessagesProvider {
|
|||
/**
|
||||
* 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') {
|
||||
discussions[userId] = {
|
||||
fullname: userFullname,
|
||||
|
@ -1272,7 +1298,7 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved with the member info.
|
||||
* @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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
@ -1287,7 +1313,9 @@ export class AddonMessagesProvider {
|
|||
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) {
|
||||
// Should never happen.
|
||||
return Promise.reject(null);
|
||||
|
@ -1313,7 +1341,7 @@ export class AddonMessagesProvider {
|
|||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @return Promise resolved with the message preferences.
|
||||
*/
|
||||
getMessagePreferences(siteId?: string): Promise<any> {
|
||||
getMessagePreferences(siteId?: string): Promise<AddonMessagesMessagePreferences> {
|
||||
this.logger.debug('Get message preferences');
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
@ -1322,7 +1350,9 @@ export class AddonMessagesProvider {
|
|||
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) {
|
||||
data.preferences.blocknoncontacts = data.blocknoncontacts;
|
||||
|
||||
|
@ -1341,15 +1371,18 @@ export class AddonMessagesProvider {
|
|||
* @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 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['newestfirst'] = 1;
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
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) => {
|
||||
message.read = params.read == 0 ? 0 : 1;
|
||||
// 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 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.
|
||||
* @return Promise resolved with the data.
|
||||
*/
|
||||
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;
|
||||
limitFromRead = limitFromRead || 0;
|
||||
|
||||
|
@ -1427,7 +1461,7 @@ export class AddonMessagesProvider {
|
|||
* @since 3.7
|
||||
*/
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -1442,7 +1476,8 @@ export class AddonMessagesProvider {
|
|||
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);
|
||||
});
|
||||
});
|
||||
|
@ -1466,7 +1501,8 @@ export class AddonMessagesProvider {
|
|||
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 {
|
||||
favourites: result.favourites,
|
||||
individual: result.types[AddonMessagesProvider.MESSAGE_CONVERSATION_TYPE_INDIVIDUAL],
|
||||
|
@ -1485,7 +1521,7 @@ export class AddonMessagesProvider {
|
|||
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 };
|
||||
});
|
||||
} else {
|
||||
|
@ -1536,7 +1572,7 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved with the message unread count.
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
read: 0,
|
||||
|
@ -2049,7 +2085,7 @@ export class AddonMessagesProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
messageid: messageId,
|
||||
|
@ -2067,7 +2103,7 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved if success.
|
||||
* @since 3.6
|
||||
*/
|
||||
markAllConversationMessagesRead(conversationId?: number): Promise<any> {
|
||||
markAllConversationMessagesRead(conversationId?: number): Promise<null> {
|
||||
const params = {
|
||||
userid: this.sitesProvider.getCurrentSiteUserId(),
|
||||
conversationid: conversationId
|
||||
|
@ -2085,7 +2121,7 @@ export class AddonMessagesProvider {
|
|||
* @param userIdFrom User Id for the sender.
|
||||
* @return Promise resolved with boolean marking success or not.
|
||||
*/
|
||||
markAllMessagesRead(userIdFrom?: number): Promise<any> {
|
||||
markAllMessagesRead(userIdFrom?: number): Promise<boolean> {
|
||||
const params = {
|
||||
useridto: this.sitesProvider.getCurrentSiteUserId(),
|
||||
useridfrom: userIdFrom
|
||||
|
@ -2217,8 +2253,9 @@ export class AddonMessagesProvider {
|
|||
* @param query The query string.
|
||||
* @param limit The number of results to return, 0 for none.
|
||||
* @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) => {
|
||||
const data = {
|
||||
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.
|
||||
};
|
||||
|
||||
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) {
|
||||
contacts = contacts.splice(0, limit);
|
||||
}
|
||||
|
@ -2250,7 +2289,7 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved with the results.
|
||||
*/
|
||||
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) => {
|
||||
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.
|
||||
};
|
||||
|
||||
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) {
|
||||
return { messages: [], canLoadMore: false };
|
||||
}
|
||||
|
||||
result.contacts.forEach((result) => {
|
||||
result.id = result.userid;
|
||||
result.contacts.forEach((contact) => {
|
||||
contact.id = contact.userid;
|
||||
});
|
||||
|
||||
this.userProvider.storeUsers(result.contacts, site.id);
|
||||
|
@ -2297,7 +2338,8 @@ export class AddonMessagesProvider {
|
|||
* @since 3.6
|
||||
*/
|
||||
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) => {
|
||||
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.
|
||||
};
|
||||
|
||||
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 nonContacts = result.noncontacts || [];
|
||||
|
||||
|
@ -2341,7 +2383,9 @@ export class AddonMessagesProvider {
|
|||
* - sent (Boolean) True if message was sent to server, false if stored in device.
|
||||
* - 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.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
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.
|
||||
* @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();
|
||||
|
||||
const messages = [
|
||||
|
@ -2430,7 +2474,7 @@ export class AddonMessagesProvider {
|
|||
* @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.
|
||||
*/
|
||||
sendMessagesOnline(messages: any, siteId?: string): Promise<any> {
|
||||
sendMessagesOnline(messages: any[], siteId?: string): Promise<AddonMessagesSendInstantMessagesMessage[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const data = {
|
||||
messages: messages
|
||||
|
@ -2451,7 +2495,9 @@ export class AddonMessagesProvider {
|
|||
* - message (any) If sent=false, contains the stored message.
|
||||
* @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.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.messagesOffline.saveConversationMessage(conversation, message, siteId).then((entry) => {
|
||||
|
@ -2506,7 +2552,8 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved if success, rejected if failure.
|
||||
* @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();
|
||||
|
||||
const messages = [
|
||||
|
@ -2534,7 +2581,9 @@ export class AddonMessagesProvider {
|
|||
* @return Promise resolved if success, rejected if failure.
|
||||
* @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) => {
|
||||
const params = {
|
||||
conversationid: conversationId,
|
||||
|
@ -2603,10 +2652,10 @@ export class AddonMessagesProvider {
|
|||
* @param conversations Array of conversations.
|
||||
* @return Conversations sorted with most recent last.
|
||||
*/
|
||||
sortConversations(conversations: any[]): any[] {
|
||||
sortConversations(conversations: AddonMessagesConversationFormatted[]): AddonMessagesConversationFormatted[] {
|
||||
return conversations.sort((a, b) => {
|
||||
const timeA = parseInt(a.lastmessagedate, 10),
|
||||
timeB = parseInt(b.lastmessagedate, 10);
|
||||
const timeA = Number(a.lastmessagedate),
|
||||
timeB = Number(b.lastmessagedate);
|
||||
|
||||
if (timeA == timeB && a.id) {
|
||||
// Same time, sort by ID.
|
||||
|
@ -2651,7 +2700,9 @@ export class AddonMessagesProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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;
|
||||
|
||||
// Get the last received message.
|
||||
|
@ -2675,7 +2726,7 @@ export class AddonMessagesProvider {
|
|||
*
|
||||
* @param contactTypes List of contacts grouped in types.
|
||||
*/
|
||||
protected storeUsersFromAllContacts(contactTypes: any): void {
|
||||
protected storeUsersFromAllContacts(contactTypes: AddonMessagesGetContactsResult): void {
|
||||
for (const x in contactTypes) {
|
||||
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 { CoreAppProvider } from '@providers/app';
|
||||
import { AddonMessagesOfflineProvider } from './messages-offline';
|
||||
import { AddonMessagesProvider } from './messages';
|
||||
import { AddonMessagesProvider, AddonMessagesConversationFormatted } from './messages';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
|
@ -258,7 +258,7 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
// Get conversation name and add errors to warnings array.
|
||||
return this.messagesProvider.getConversation(conversationId, false, false).catch(() => {
|
||||
// Ignore errors.
|
||||
return {};
|
||||
return <AddonMessagesConversationFormatted> {};
|
||||
}).then((conversation) => {
|
||||
errors.forEach((error) => {
|
||||
warnings.push(this.translate.instant('addon.messages.warningconversationmessagenotsent', {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injector } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AddonModAssignFeedbackHandler } from '../providers/feedback-delegate';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||
|
||||
/**
|
||||
* Base handler for feedback plugins.
|
||||
|
@ -48,7 +49,7 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -74,7 +75,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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 [];
|
||||
}
|
||||
|
||||
|
@ -84,7 +86,7 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param plugin The plugin object.
|
||||
* @return The plugin name.
|
||||
*/
|
||||
getPluginName(plugin: any): string {
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
// Check if there's a translated string for the plugin.
|
||||
const translationId = 'addon.mod_assign_feedback_' + plugin.type + '.pluginname',
|
||||
translation = this.translate.instant(translationId);
|
||||
|
@ -109,7 +111,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param inputData Data entered by the user for the feedback.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -144,7 +147,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
@ -158,7 +162,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -172,7 +177,8 @@ export class AddonModAssignBaseFeedbackHandler implements AddonModAssignFeedback
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { Injector } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AddonModAssignSubmissionHandler } from '../providers/submission-delegate';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||
|
||||
/**
|
||||
* Base handler for submission plugins.
|
||||
|
@ -38,7 +39,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -65,7 +68,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -79,7 +83,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -92,7 +97,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param edit Whether the user is editing.
|
||||
* @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.
|
||||
}
|
||||
|
||||
|
@ -106,7 +111,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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 [];
|
||||
}
|
||||
|
||||
|
@ -116,7 +122,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @return The plugin name.
|
||||
*/
|
||||
getPluginName(plugin: any): string {
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
// Check if there's a translated string for the plugin.
|
||||
const translationId = 'addon.mod_assign_submission_' + plugin.type + '.pluginname',
|
||||
translation = this.translate.instant(translationId);
|
||||
|
@ -139,7 +145,7 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* @param assign The assignment.
|
||||
* @param submission The submission.
|
||||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -163,7 +171,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -194,7 +203,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
@ -211,7 +221,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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> {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
@ -228,8 +239,8 @@ export class AddonModAssignBaseSubmissionHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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)
|
||||
: void | Promise<any> {
|
||||
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,14 +14,15 @@
|
|||
|
||||
import { Input } from '@angular/core';
|
||||
import { ModalController } from 'ionic-angular';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||
|
||||
/**
|
||||
* Base class for component to render a feedback plugin.
|
||||
*/
|
||||
export class AddonModAssignFeedbackPluginComponentBase {
|
||||
@Input() assign: any; // The assignment.
|
||||
@Input() submission: any; // The submission.
|
||||
@Input() plugin: any; // The plugin object.
|
||||
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() userId: number; // The user ID of the submission.
|
||||
@Input() configs: any; // The configs for the plugin.
|
||||
@Input() canEdit: boolean; // Whether the user can edit.
|
||||
|
|
|
@ -13,15 +13,16 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Input } from '@angular/core';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from '../providers/assign';
|
||||
|
||||
/**
|
||||
* Base class for component to render a submission plugin.
|
||||
*/
|
||||
export class AddonModAssignSubmissionPluginComponent {
|
||||
@Input() assign: any; // The assignment.
|
||||
@Input() submission: any; // The submission.
|
||||
@Input() plugin: any; // The plugin object.
|
||||
@Input() configs: any; // The configs for the plugin.
|
||||
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() configs: {[name: string]: string}; // The configs for the plugin.
|
||||
@Input() edit: boolean; // Whether the user is editing.
|
||||
@Input() allowOffline: boolean; // Whether to allow offline.
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
// limitations under the License.
|
||||
|
||||
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 { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||
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 {
|
||||
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
||||
|
||||
@Input() assign: any; // The assignment.
|
||||
@Input() submission: any; // The submission.
|
||||
@Input() plugin: any; // The plugin object.
|
||||
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() userId: number; // The user ID of the submission.
|
||||
@Input() canEdit: boolean | string; // Whether the user can edit.
|
||||
@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 { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
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 { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||
import { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
||||
|
@ -36,13 +36,13 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
component = AddonModAssignProvider.COMPONENT;
|
||||
moduleName = 'assign';
|
||||
|
||||
assign: any; // The assign object.
|
||||
assign: AddonModAssignAssign; // The assign object.
|
||||
canViewAllSubmissions: boolean; // Whether the user can view all submissions.
|
||||
canViewOwnSubmission: boolean; // Whether the user can view their own submission.
|
||||
timeRemaining: string; // Message about time remaining to submit.
|
||||
lateSubmissions: string; // Message about late submissions.
|
||||
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.
|
||||
|
||||
groupInfo: CoreGroupInfo = {
|
||||
|
@ -153,7 +153,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
this.assign = assignData;
|
||||
|
||||
this.dataRetrieved.emit(this.assign);
|
||||
this.description = this.assign.intro || this.description;
|
||||
this.description = this.assign.intro;
|
||||
|
||||
if (sync) {
|
||||
// Try to synchronize the assign.
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
// limitations under the License.
|
||||
|
||||
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 { AddonModAssignSubmissionDelegate } from '../../providers/submission-delegate';
|
||||
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 {
|
||||
@ViewChild(CoreDynamicComponent) dynamicComponent: CoreDynamicComponent;
|
||||
|
||||
@Input() assign: any; // The assignment.
|
||||
@Input() submission: any; // The submission.
|
||||
@Input() plugin: any; // The plugin object.
|
||||
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() edit: boolean | string; // Whether the user is editing.
|
||||
@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 { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||
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 { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||
|
@ -55,11 +58,11 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
|
||||
loaded: boolean; // Whether data has been loaded.
|
||||
selectedTab: number; // Tab selected on start.
|
||||
assign: any; // The assignment the submission belongs to.
|
||||
userSubmission: any; // The submission object.
|
||||
assign: AddonModAssignAssign; // The assignment the submission belongs to.
|
||||
userSubmission: AddonModAssignSubmission; // The submission object.
|
||||
isSubmittedForGrading: boolean; // Whether the submission has been submitted 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.
|
||||
submittedOffline: boolean; // Whether it was submitted in offline.
|
||||
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.
|
||||
blindMarking: boolean; // Whether blind marking is enabled.
|
||||
user: any; // The user.
|
||||
lastAttempt: any; // The last attempt.
|
||||
lastAttempt: AddonModAssignSubmissionAttemptFormatted; // The last attempt.
|
||||
membersToSubmit: any[]; // Team members that need to submit the assignment.
|
||||
canSubmit: boolean; // Whether the user can submit for grading.
|
||||
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.
|
||||
gradingColor: string; // Color to apply to the grading 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.
|
||||
timeRemainingClass: string; // Class to apply to time remaining message.
|
||||
statusTranslated: string; // Status.
|
||||
|
@ -99,7 +102,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
|
||||
protected siteId: string; // Current site 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 originalGrades: any = {}; // Object with the original grade data, to check for changes.
|
||||
protected isDestroyed: boolean; // Whether the component has been destroyed.
|
||||
|
@ -209,7 +212,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
return this.goToEdit();
|
||||
}
|
||||
|
||||
const previousSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, this.previousAttempt);
|
||||
const previousSubmission = this.previousAttempt.submission;
|
||||
let modal = this.domUtils.showModalLoading();
|
||||
|
||||
this.assignHelper.getSubmissionSizeForCopy(this.assign, previousSubmission).catch(() => {
|
||||
|
@ -303,7 +306,8 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
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.
|
||||
return false;
|
||||
});
|
||||
|
@ -438,7 +442,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
// Check if there's any unsupported plugin for editing.
|
||||
if (!this.userSubmission || !this.userSubmission.plugins) {
|
||||
// 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');
|
||||
}
|
||||
|
||||
|
@ -461,7 +465,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
* @param feedback The feedback data from the submission status.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected loadFeedback(feedback: any): Promise<any> {
|
||||
protected loadFeedback(feedback: AddonModAssignSubmissionFeedback): Promise<any> {
|
||||
this.grade = {
|
||||
method: false,
|
||||
grade: false,
|
||||
|
@ -571,7 +575,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
|
||||
if (!this.feedback || !this.feedback.plugins) {
|
||||
// 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');
|
||||
}
|
||||
|
||||
|
@ -885,7 +889,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
|||
// Show error if submission statement should be shown but it couldn't be retrieved.
|
||||
this.showErrorStatementEdit = submissionStatementMissing && !this.assign.submissiondrafts &&
|
||||
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);
|
||||
|
||||
|
@ -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 { CoreSitesProvider } from '@providers/sites';
|
||||
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 { AddonModAssignFeedbackHandler } from '../../../providers/feedback-delegate';
|
||||
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.
|
||||
*
|
||||
* @param injector Injector.
|
||||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -101,7 +103,8 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -135,7 +138,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
|||
* @param userId User ID of the submission.
|
||||
* @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.
|
||||
return this.assignOfflineProvider.getSubmissionGrade(assign.id, userId).catch(() => {
|
||||
// No offline data found.
|
||||
|
@ -191,7 +196,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
|
||||
if (draft) {
|
||||
|
@ -212,7 +219,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) {
|
||||
this.drafts[this.getDraftId(assignId, userId, siteId)] = data;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
// limitations under the License.
|
||||
|
||||
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 { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf';
|
||||
|
||||
|
@ -29,14 +31,14 @@ export class AddonModAssignFeedbackEditPdfHandler implements AddonModAssignFeedb
|
|||
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.
|
||||
*
|
||||
* @param injector Injector.
|
||||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,8 @@ export class AddonModAssignFeedbackEditPdfHandler implements AddonModAssignFeedb
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
// limitations under the License.
|
||||
|
||||
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 { AddonModAssignFeedbackFileComponent } from '../component/file';
|
||||
|
||||
|
@ -29,14 +31,14 @@ export class AddonModAssignFeedbackFileHandler implements AddonModAssignFeedback
|
|||
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.
|
||||
*
|
||||
* @param injector Injector.
|
||||
* @param plugin The plugin object.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,8 @@ export class AddonModAssignFeedbackFileHandler implements AddonModAssignFeedback
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ import { IonicPage, ViewController, NavParams } from 'ionic-angular';
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate';
|
||||
import {
|
||||
AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||
} from '../../providers/assign';
|
||||
|
||||
/**
|
||||
* Modal that allows editing a feedback plugin.
|
||||
|
@ -28,9 +31,9 @@ import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegat
|
|||
})
|
||||
export class AddonModAssignEditFeedbackModalPage {
|
||||
|
||||
@Input() assign: any; // The assignment.
|
||||
@Input() submission: any; // The submission.
|
||||
@Input() plugin: any; // The plugin object.
|
||||
@Input() assign: AddonModAssignAssign; // The assignment.
|
||||
@Input() submission: AddonModAssignSubmission; // The submission.
|
||||
@Input() plugin: AddonModAssignPlugin; // The plugin object.
|
||||
@Input() userId: number; // The user ID of the submission.
|
||||
|
||||
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.
|
||||
*/
|
||||
protected hasDataChanged(): Promise<boolean> {
|
||||
return this.feedbackDelegate.hasPluginDataChanged(this.assign, this.userId, this.plugin, this.getInputData(), this.userId)
|
||||
.catch(() => {
|
||||
return this.feedbackDelegate.hasPluginDataChanged(this.assign, this.submission, this.plugin, this.getInputData(),
|
||||
this.userId).catch(() => {
|
||||
// Ignore errors.
|
||||
return true;
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
import { CoreSyncProvider } from '@providers/sync';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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 { AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||
|
@ -35,9 +35,9 @@ import { AddonModAssignHelperProvider } from '../../providers/helper';
|
|||
})
|
||||
export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
||||
title: string; // Title to display.
|
||||
assign: any; // Assignment.
|
||||
assign: AddonModAssignAssign; // Assignment.
|
||||
courseId: number; // Course ID the assignment belongs to.
|
||||
userSubmission: any; // The user submission.
|
||||
userSubmission: AddonModAssignSubmission; // The user submission.
|
||||
allowOffline: boolean; // Whether offline is allowed.
|
||||
submissionStatement: string; // The submission statement.
|
||||
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);
|
||||
|
||||
// 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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
|||
} else {
|
||||
// Try to send it to server.
|
||||
promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
||||
this.userSubmission.timemodified, this.assign.submissiondrafts, this.userId);
|
||||
this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId);
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
|
|
|
@ -19,9 +19,11 @@ import { CoreEventsProvider } from '@providers/events';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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 { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from '../../providers/helper';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
|
||||
/**
|
||||
|
@ -36,7 +38,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
|||
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||
|
||||
title: string; // Title to display.
|
||||
assign: any; // Assignment.
|
||||
assign: AddonModAssignAssign; // Assignment.
|
||||
submissions: any[]; // List of submissions
|
||||
loaded: boolean; // Whether data has 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 selectedStatus: string; // The status to see.
|
||||
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,
|
||||
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),
|
||||
];
|
||||
|
||||
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.
|
||||
const getNeedGrading = this.selectedStatus == AddonModAssignProvider.NEED_GRADING,
|
||||
searchStatus = getNeedGrading ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : this.selectedStatus,
|
||||
promises = [],
|
||||
showSubmissions = [];
|
||||
|
||||
submissions.forEach((submission) => {
|
||||
submissions.forEach((submission: AddonModAssignSubmissionForList) => {
|
||||
if (!searchStatus || searchStatus == submission.status) {
|
||||
promises.push(this.assignOfflineProvider.getSubmissionGrade(this.assign.id, submission.userid).catch(() => {
|
||||
// Ignore errors.
|
||||
|
@ -213,7 +215,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
|||
submission.statusTranslated = this.translate.instant('addon.mod_assign.submissionstatus_' +
|
||||
submission.status);
|
||||
} else {
|
||||
submission.statusTranslated = false;
|
||||
submission.statusTranslated = '';
|
||||
}
|
||||
|
||||
if (notSynced) {
|
||||
|
@ -224,7 +226,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
|||
submission.gradingStatusTranslationId =
|
||||
this.assignProvider.getSubmissionGradingStatusTranslationId(submission.gradingstatus);
|
||||
} else {
|
||||
submission.gradingStatusTranslationId = false;
|
||||
submission.gradingStatusTranslationId = '';
|
||||
}
|
||||
|
||||
showSubmissions.push(submission);
|
||||
|
@ -299,3 +301,13 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
|||
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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { AddonModAssignProvider } from '../../providers/assign';
|
||||
import { AddonModAssignProvider, AddonModAssignAssign } from '../../providers/assign';
|
||||
import { AddonModAssignSubmissionComponent } from '../../components/submission/submission';
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit {
|
|||
loaded: boolean; // Whether data has been loaded.
|
||||
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 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 { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { AddonModAssignProvider } from './assign';
|
||||
import { AddonModAssignProvider, AddonModAssignAssign } from './assign';
|
||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||
|
||||
|
@ -169,14 +169,14 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
|||
syncAssign(assignId: number, siteId?: string): Promise<AddonModAssignSyncResult> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const promises = [],
|
||||
const promises: Promise<any>[] = [],
|
||||
result: AddonModAssignSyncResult = {
|
||||
warnings: [],
|
||||
updated: false
|
||||
};
|
||||
let assign,
|
||||
courseId,
|
||||
syncPromise;
|
||||
let assign: AddonModAssignAssign,
|
||||
courseId: number,
|
||||
syncPromise: Promise<any>;
|
||||
|
||||
if (this.isSyncing(assignId, siteId)) {
|
||||
// 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.
|
||||
* @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,
|
||||
pluginData = {};
|
||||
let discardError,
|
||||
|
@ -358,8 +358,8 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved if success, rejected otherwise.
|
||||
*/
|
||||
protected syncSubmissionGrade(assign: any, offlineData: any, warnings: string[], courseId: number, siteId?: string)
|
||||
: Promise<any> {
|
||||
protected syncSubmissionGrade(assign: AddonModAssignAssign, offlineData: any, warnings: string[], courseId: number,
|
||||
siteId?: string): Promise<any> {
|
||||
|
||||
const userId = offlineData.userid;
|
||||
let discardError;
|
||||
|
|
|
@ -27,6 +27,7 @@ import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
|||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreInterceptor } from '@classes/interceptor';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some functions for assign.
|
||||
|
@ -123,7 +124,7 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -138,7 +139,7 @@ export class AddonModAssignProvider {
|
|||
* @return Promise resolved when the assignment is retrieved.
|
||||
*/
|
||||
protected getAssignmentByField(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string)
|
||||
: Promise<any> {
|
||||
: Promise<AddonModAssignAssign> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
|
@ -161,7 +162,7 @@ export class AddonModAssignProvider {
|
|||
delete params.includenotenrolledcourses;
|
||||
|
||||
return site.read('mod_assign_get_assignments', params, preSets);
|
||||
}).then((response) => {
|
||||
}).then((response: AddonModAssignGetAssignmentsResult): any => {
|
||||
// Search the assignment to return.
|
||||
if (response.courses && response.courses.length) {
|
||||
const assignments = response.courses[0].assignments;
|
||||
|
@ -187,7 +188,7 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -225,7 +226,9 @@ export class AddonModAssignProvider {
|
|||
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.
|
||||
if (response.assignments && response.assignments.length) {
|
||||
if (!userId || userId < 0) {
|
||||
|
@ -271,7 +274,7 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
assignmentids: [assignId]
|
||||
|
@ -285,7 +288,7 @@ export class AddonModAssignProvider {
|
|||
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.
|
||||
if (response.assignments && response.assignments.length) {
|
||||
const assignment = response.assignments[0];
|
||||
|
@ -294,7 +297,7 @@ export class AddonModAssignProvider {
|
|||
return assignment.grades;
|
||||
}
|
||||
} else if (response.warnings && response.warnings.length) {
|
||||
if (response.warnings[0].warningcode == 3) {
|
||||
if (response.warnings[0].warningcode == '3') {
|
||||
// No grades found.
|
||||
return [];
|
||||
}
|
||||
|
@ -362,7 +365,9 @@ export class AddonModAssignProvider {
|
|||
* @param attempt Attempt.
|
||||
* @return Submission object or null.
|
||||
*/
|
||||
getSubmissionObjectFromAttempt(assign: any, attempt: any): any {
|
||||
getSubmissionObjectFromAttempt(assign: AddonModAssignAssign, attempt: AddonModAssignSubmissionAttempt)
|
||||
: AddonModAssignSubmission {
|
||||
|
||||
if (!attempt) {
|
||||
return null;
|
||||
}
|
||||
|
@ -432,7 +437,7 @@ export class AddonModAssignProvider {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
getSubmissions(assignId: number, ignoreCache?: boolean, siteId?: string)
|
||||
: Promise<{canviewsubmissions: boolean, submissions?: any[]}> {
|
||||
: Promise<{canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]}> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
|
@ -448,9 +453,11 @@ export class AddonModAssignProvider {
|
|||
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.
|
||||
if (response.warnings.length > 0 && response.warnings[0].warningcode == 1) {
|
||||
if (response.warnings.length > 0 && response.warnings[0].warningcode == '1') {
|
||||
return {canviewsubmissions: false};
|
||||
}
|
||||
|
||||
|
@ -489,7 +496,7 @@ export class AddonModAssignProvider {
|
|||
* @return Promise always resolved with the user submission status.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
@ -540,7 +547,7 @@ export class AddonModAssignProvider {
|
|||
* @return Promise always resolved with the user submission status.
|
||||
*/
|
||||
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) => {
|
||||
const userSubmission = this.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
||||
|
@ -630,7 +637,9 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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;
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
|
@ -1051,14 +1060,14 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
assignmentid: assignId,
|
||||
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) {
|
||||
// The WebService returned warnings, reject.
|
||||
return Promise.reject(warnings[0]);
|
||||
|
@ -1120,14 +1129,14 @@ export class AddonModAssignProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
assignmentid: assignId,
|
||||
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) {
|
||||
// The WebService returned warnings, reject.
|
||||
return Promise.reject(warnings[0]);
|
||||
|
@ -1169,7 +1178,10 @@ export class AddonModAssignProvider {
|
|||
return this.isGradingOfflineEnabled(siteId).then((enabled) => {
|
||||
if (!enabled) {
|
||||
return this.submitGradingFormOnline(assignId, userId, grade, attemptNumber, addAttempt, workflowState,
|
||||
applyToAll, outcomes, pluginData, siteId);
|
||||
applyToAll, outcomes, pluginData, siteId).then(() => {
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.appProvider.isOnline()) {
|
||||
|
@ -1212,7 +1224,7 @@ export class AddonModAssignProvider {
|
|||
* @return Promise resolved when submitted, rejected otherwise.
|
||||
*/
|
||||
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) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
@ -1243,7 +1255,7 @@ export class AddonModAssignProvider {
|
|||
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) {
|
||||
// The WebService returned warnings, reject.
|
||||
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 { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { AddonModAssignDefaultFeedbackHandler } from './default-feedback-handler';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
|
||||
|
||||
/**
|
||||
* Interface that all feedback handlers must implement.
|
||||
|
@ -47,7 +48,7 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
|
@ -69,7 +70,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -77,7 +79,7 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @return The plugin name.
|
||||
*/
|
||||
getPluginName?(plugin: any): string;
|
||||
getPluginName?(plugin: AddonModAssignPlugin): string;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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.
|
||||
|
@ -111,7 +114,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -123,7 +127,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -135,7 +140,8 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -171,7 +178,7 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param plugin The plugin object.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -184,7 +191,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -198,7 +206,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -208,7 +217,7 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param plugin Plugin to get the name for.
|
||||
* @return Human readable name.
|
||||
*/
|
||||
getPluginName(plugin: any): string {
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
||||
}
|
||||
|
||||
|
@ -222,7 +231,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param userId User ID of the submission.
|
||||
* @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',
|
||||
[assign, submission, plugin, inputData, userId]));
|
||||
}
|
||||
|
@ -236,7 +246,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -259,7 +270,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -273,7 +285,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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',
|
||||
[assignId, userId, plugin, pluginData, siteId]));
|
||||
|
@ -289,7 +302,8 @@ export class AddonModAssignFeedbackDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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',
|
||||
[assignId, userId, plugin, inputData, siteId]));
|
||||
}
|
||||
|
|
|
@ -21,7 +21,10 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
|||
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
|
||||
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||
import { AddonModAssignProvider } from './assign';
|
||||
import {
|
||||
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignParticipant,
|
||||
AddonModAssignSubmissionFeedback
|
||||
} from './assign';
|
||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||
|
||||
/**
|
||||
|
@ -46,7 +49,7 @@ export class AddonModAssignHelperProvider {
|
|||
* @param submission Submission.
|
||||
* @return Whether it can be edited offline.
|
||||
*/
|
||||
canEditSubmissionOffline(assign: any, submission: any): Promise<boolean> {
|
||||
canEditSubmissionOffline(assign: AddonModAssignAssign, submission: AddonModAssignSubmission): Promise<boolean> {
|
||||
if (!submission) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
@ -81,7 +84,7 @@ export class AddonModAssignHelperProvider {
|
|||
* @param submission Submission to clear the data for.
|
||||
* @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) => {
|
||||
this.submissionDelegate.clearTmpData(assign, submission, plugin, inputData);
|
||||
});
|
||||
|
@ -95,7 +98,7 @@ export class AddonModAssignHelperProvider {
|
|||
* @param previousSubmission Submission to copy.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
copyPreviousAttempt(assign: any, previousSubmission: any): Promise<any> {
|
||||
copyPreviousAttempt(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<any> {
|
||||
const pluginData = {},
|
||||
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.
|
||||
*
|
||||
|
@ -136,7 +169,9 @@ export class AddonModAssignHelperProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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 = [];
|
||||
|
||||
feedback.plugins.forEach((plugin) => {
|
||||
|
@ -155,7 +190,9 @@ export class AddonModAssignHelperProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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;
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
|
@ -167,7 +204,7 @@ export class AddonModAssignHelperProvider {
|
|||
// If no participants returned and all groups specified, get participants by groups.
|
||||
return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((info) => {
|
||||
const promises = [],
|
||||
participants = {};
|
||||
participants: {[id: number]: AddonModAssignParticipant} = {};
|
||||
|
||||
info.groups.forEach((userGroup) => {
|
||||
promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, ignoreCache, siteId)
|
||||
|
@ -194,8 +231,8 @@ export class AddonModAssignHelperProvider {
|
|||
* @param type Name of the subplugin.
|
||||
* @return Object containing all configurations of the subplugin selected.
|
||||
*/
|
||||
getPluginConfig(assign: any, subtype: string, type: string): any {
|
||||
const configs = {};
|
||||
getPluginConfig(assign: AddonModAssignAssign, subtype: string, type: string): {[name: string]: string} {
|
||||
const configs: {[name: string]: string} = {};
|
||||
|
||||
assign.configs.forEach((config) => {
|
||||
if (config.subtype == subtype && config.plugin == type) {
|
||||
|
@ -213,7 +250,7 @@ export class AddonModAssignHelperProvider {
|
|||
* @param subtype Subtype name (assignsubmission or assignfeedback)
|
||||
* @return List of enabled plugins for the assign.
|
||||
*/
|
||||
getPluginsEnabled(assign: any, subtype: string): any[] {
|
||||
getPluginsEnabled(assign: AddonModAssignAssign, subtype: string): any[] {
|
||||
const enabled = [];
|
||||
|
||||
assign.configs.forEach((config) => {
|
||||
|
@ -250,7 +287,7 @@ export class AddonModAssignHelperProvider {
|
|||
* @param previousSubmission Submission to copy.
|
||||
* @return Promise resolved with the size.
|
||||
*/
|
||||
getSubmissionSizeForCopy(assign: any, previousSubmission: any): Promise<number> {
|
||||
getSubmissionSizeForCopy(assign: AddonModAssignAssign, previousSubmission: AddonModAssignSubmission): Promise<number> {
|
||||
const promises = [];
|
||||
let totalSize = 0;
|
||||
|
||||
|
@ -273,7 +310,8 @@ export class AddonModAssignHelperProvider {
|
|||
* @param inputData Data entered in the submission form.
|
||||
* @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 = [];
|
||||
let totalSize = 0;
|
||||
|
||||
|
@ -298,14 +336,14 @@ export class AddonModAssignHelperProvider {
|
|||
* @param siteId Site id (empty for current site).
|
||||
* @return Promise always resolved. Resolve param is the formatted submissions.
|
||||
*/
|
||||
getSubmissionsUserData(assign: any, submissions: any[], groupId?: number, ignoreCache?: boolean, siteId?: string):
|
||||
Promise<any[]> {
|
||||
return this.getParticipants(assign, groupId).then((participants) => {
|
||||
getSubmissionsUserData(assign: AddonModAssignAssign, submissions: AddonModAssignSubmissionFormatted[], groupId?: number,
|
||||
ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignSubmissionFormatted[]> {
|
||||
|
||||
return this.getParticipants(assign, groupId).then((parts) => {
|
||||
const blind = assign.blindmarking && !assign.revealidentities;
|
||||
const promises = [];
|
||||
const result = [];
|
||||
|
||||
participants = this.utils.arrayToObject(participants, 'id');
|
||||
const result: AddonModAssignSubmissionFormatted[] = [];
|
||||
const participants: {[id: number]: AddonModAssignParticipant} = this.utils.arrayToObject(parts, 'id');
|
||||
|
||||
submissions.forEach((submission) => {
|
||||
submission.submitid = submission.userid > 0 ? submission.userid : submission.blindid;
|
||||
|
@ -356,10 +394,10 @@ export class AddonModAssignHelperProvider {
|
|||
|
||||
return Promise.all(promises).then(() => {
|
||||
// Create a submission for each participant left in the list (the participants already treated were removed).
|
||||
this.utils.objectToArray(participants).forEach((participant) => {
|
||||
const submission: any = {
|
||||
submitid: participant.id
|
||||
};
|
||||
this.utils.objectToArray(participants).forEach((participant: AddonModAssignParticipant) => {
|
||||
const submission = this.createEmptySubmission();
|
||||
|
||||
submission.submitid = participant.id;
|
||||
|
||||
if (!blind) {
|
||||
submission.userid = participant.id;
|
||||
|
@ -390,17 +428,20 @@ export class AddonModAssignHelperProvider {
|
|||
* Check if the feedback data has changed for a certain submission and assign.
|
||||
*
|
||||
* @param assign Assignment.
|
||||
* @param userId User Id.
|
||||
* @param submission The submission.
|
||||
* @param feedback Feedback data.
|
||||
* @param userId The user ID.
|
||||
* @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 = [];
|
||||
let hasChanged = false;
|
||||
|
||||
feedback.plugins.forEach((plugin) => {
|
||||
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) {
|
||||
hasChanged = true;
|
||||
}
|
||||
|
@ -423,7 +464,9 @@ export class AddonModAssignHelperProvider {
|
|||
* @param inputData Data entered in the submission form.
|
||||
* @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 = [];
|
||||
let hasChanged = false;
|
||||
|
||||
|
@ -451,7 +494,9 @@ export class AddonModAssignHelperProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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 = {},
|
||||
promises = [];
|
||||
|
||||
|
@ -473,7 +518,9 @@ export class AddonModAssignHelperProvider {
|
|||
* @param offline True to prepare the data for an offline submission, false otherwise.
|
||||
* @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 = {},
|
||||
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 { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonModAssignProvider } from './assign';
|
||||
import { AddonModAssignHelperProvider } from './helper';
|
||||
import { AddonModAssignProvider, AddonModAssignGetSubmissionStatusResult, AddonModAssignSubmission } from './assign';
|
||||
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from './helper';
|
||||
import { AddonModAssignSyncProvider } from './assign-sync';
|
||||
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
|
||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||
|
@ -106,7 +106,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
|||
if (data.canviewsubmissions) {
|
||||
// Teacher, get all submissions.
|
||||
return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, false, siteId)
|
||||
.then((submissions) => {
|
||||
.then((submissions: AddonModAssignSubmissionFormatted[]) => {
|
||||
|
||||
const promises = [];
|
||||
|
||||
|
@ -161,9 +161,10 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
|||
return this.assignProvider.getSubmissionStatusWithRetry(assign, submitId, undefined, blindMarking, true, false, siteId)
|
||||
.then((response) => {
|
||||
const promises = [];
|
||||
let userSubmission: AddonModAssignSubmission;
|
||||
|
||||
if (response.lastattempt) {
|
||||
const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
||||
userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(assign, response.lastattempt);
|
||||
if (userSubmission && userSubmission.plugins) {
|
||||
// Add submission plugin files.
|
||||
userSubmission.plugins.forEach((plugin) => {
|
||||
|
@ -175,7 +176,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
|||
if (response.feedback && response.feedback.plugins) {
|
||||
// Add feedback plugin files.
|
||||
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) => {
|
||||
groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, true, siteId)
|
||||
.then((submissions) => {
|
||||
.then((submissions: AddonModAssignSubmissionFormatted[]) => {
|
||||
|
||||
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.
|
||||
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,
|
||||
false, true, true, siteId).then((subm) => {
|
||||
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.
|
||||
* @return Promise resolved when prefetched, rejected otherwise.
|
||||
*/
|
||||
protected prefetchSubmission(assign: any, courseId: number, moduleId: number, submission: any, userId?: number,
|
||||
siteId?: string): Promise<any> {
|
||||
protected prefetchSubmission(assign: any, courseId: number, moduleId: number,
|
||||
submission: AddonModAssignGetSubmissionStatusResult, userId?: number, siteId?: string): Promise<any> {
|
||||
|
||||
const promises = [],
|
||||
blindMarking = assign.blindmarking && !assign.revealidentities;
|
||||
let userIds = [];
|
||||
let userIds = [],
|
||||
userSubmission: AddonModAssignSubmission;
|
||||
|
||||
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.
|
||||
if (!blindMarking && submission.lastattempt.submissiongroupmemberswhoneedtosubmit) {
|
||||
|
@ -440,10 +443,10 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
|
|||
if (submission.feedback.plugins) {
|
||||
submission.feedback.plugins.forEach((plugin) => {
|
||||
// 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.
|
||||
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);
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { AddonModAssignDefaultSubmissionHandler } from './default-submission-handler';
|
||||
import { AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin } from './assign';
|
||||
|
||||
/**
|
||||
* Interface that all submission handlers must implement.
|
||||
|
@ -39,7 +40,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
|
@ -49,7 +51,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
|
@ -62,7 +65,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -74,7 +78,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -85,7 +90,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param edit Whether the user is editing.
|
||||
* @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.
|
||||
|
@ -97,7 +102,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -105,7 +111,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
|
@ -114,7 +120,7 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
|
@ -125,7 +131,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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.
|
||||
|
@ -136,7 +143,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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.
|
||||
|
@ -155,7 +163,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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.
|
||||
|
@ -170,8 +179,9 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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,
|
||||
userId?: number, siteId?: string): void | Promise<any>;
|
||||
prepareSubmissionData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean,
|
||||
userId?: number, siteId?: string): void | Promise<any>;
|
||||
|
||||
/**
|
||||
* Prepare and add to pluginData the data to send to the server based on the offline data stored.
|
||||
|
@ -185,8 +195,8 @@ export interface AddonModAssignSubmissionHandler extends CoreDelegateHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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)
|
||||
: void | Promise<any>;
|
||||
prepareSyncData?(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
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.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -222,7 +233,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param plugin The plugin object.
|
||||
* @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]);
|
||||
}
|
||||
|
||||
|
@ -236,7 +248,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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',
|
||||
[assign, plugin, pluginData, userId, siteId]));
|
||||
}
|
||||
|
@ -251,7 +264,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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',
|
||||
[assign, submission, plugin, offlineData, siteId]));
|
||||
}
|
||||
|
@ -264,7 +278,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param edit Whether the user is editing.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -278,7 +292,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -288,7 +303,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param plugin Plugin to get the name for.
|
||||
* @return Human readable name.
|
||||
*/
|
||||
getPluginName(plugin: any): string {
|
||||
getPluginName(plugin: AddonModAssignPlugin): string {
|
||||
return this.executeFunctionOnEnabled(plugin.type, 'getPluginName', [plugin]);
|
||||
}
|
||||
|
||||
|
@ -299,7 +314,7 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param plugin The plugin object.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -312,7 +327,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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',
|
||||
[assign, submission, plugin, inputData]));
|
||||
}
|
||||
|
@ -326,7 +342,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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',
|
||||
[assign, submission, plugin, inputData]));
|
||||
}
|
||||
|
@ -360,7 +377,8 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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]));
|
||||
}
|
||||
|
||||
|
@ -377,8 +395,9 @@ export class AddonModAssignSubmissionDelegate extends CoreDelegate {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when data has been gathered.
|
||||
*/
|
||||
preparePluginSubmissionData(assign: any, submission: any, plugin: any, inputData: any, pluginData: any, offline?: boolean,
|
||||
userId?: number, siteId?: string): Promise<any> {
|
||||
preparePluginSubmissionData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, inputData: any, pluginData: any, offline?: boolean, userId?: number,
|
||||
siteId?: string): Promise<any> {
|
||||
|
||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSubmissionData',
|
||||
[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.
|
||||
* @return Promise resolved when data has been gathered.
|
||||
*/
|
||||
preparePluginSyncData(assign: any, submission: any, plugin: any, offlineData: any, pluginData: any, siteId?: string)
|
||||
: Promise<any> {
|
||||
preparePluginSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): Promise<any> {
|
||||
|
||||
return Promise.resolve(this.executeFunctionOnEnabled(plugin.type, 'prepareSyncData',
|
||||
[assign, submission, plugin, offlineData, pluginData, siteId]));
|
||||
|
|
|
@ -17,6 +17,9 @@ import { Injectable, Injector } from '@angular/core';
|
|||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||
import { AddonModAssignSubmissionCommentsComponent } from '../component/comments';
|
||||
import {
|
||||
AddonModAssignAssign, AddonModAssignSubmission, AddonModAssignPlugin
|
||||
} from '../../../providers/assign';
|
||||
|
||||
/**
|
||||
* Handler for comments submission plugin.
|
||||
|
@ -38,7 +41,8 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
return true;
|
||||
}
|
||||
|
@ -52,7 +56,7 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
|||
* @param edit Whether the user is editing.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -84,7 +88,9 @@ export class AddonModAssignSubmissionCommentsHandler implements AddonModAssignSu
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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,
|
||||
'submission_comments', 0, siteId).catch(() => {
|
||||
// Fail silently (Moodle < 3.1.1, 3.2)
|
||||
|
|
|
@ -21,7 +21,9 @@ import { CoreSitesProvider } from '@providers/sites';
|
|||
import { CoreWSProvider } from '@providers/ws';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
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 { AddonModAssignHelperProvider } from '../../../providers/helper';
|
||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||
|
@ -53,7 +55,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
return true;
|
||||
}
|
||||
|
@ -66,7 +69,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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);
|
||||
|
||||
// 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.
|
||||
* @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.
|
||||
const files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||
|
||||
|
@ -105,7 +111,7 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param edit Whether the user is editing.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -119,7 +125,9 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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,
|
||||
submission.userid, siteId).catch(() => {
|
||||
// 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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -147,7 +156,7 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param plugin The plugin object.
|
||||
* @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),
|
||||
promises = [];
|
||||
let totalSize = 0;
|
||||
|
@ -177,7 +186,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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();
|
||||
|
||||
// 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.
|
||||
* @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.
|
||||
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
||||
// No offline data found.
|
||||
|
@ -299,7 +311,8 @@ export class AddonModAssignSubmissionFileHandler implements AddonModAssignSubmis
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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> {
|
||||
|
||||
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.
|
||||
* @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)
|
||||
: void | Promise<any> {
|
||||
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||
|
||||
const filesData = offlineData && offlineData.plugindata && offlineData.plugindata.files_filemanager;
|
||||
if (filesData) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<!-- Edit -->
|
||||
<div *ngIf="edit && loaded">
|
||||
<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>
|
||||
<p>{{ 'core.numwords' | translate: {'$a': words + ' / ' + configs.wordlimit} }}</p>
|
||||
</ion-item>
|
||||
|
|
|
@ -34,6 +34,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
|||
component = AddonModAssignProvider.COMPONENT;
|
||||
text: string;
|
||||
loaded: boolean;
|
||||
wordLimitEnabled: boolean;
|
||||
|
||||
protected wordCountTimeout: any;
|
||||
protected element: HTMLElement;
|
||||
|
@ -61,9 +62,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
|||
// No offline data found, return online text.
|
||||
return this.assignProvider.getSubmissionPluginText(this.plugin);
|
||||
}).then((text) => {
|
||||
// We receive them as strings, convert to int.
|
||||
this.configs.wordlimit = parseInt(this.configs.wordlimit, 10);
|
||||
this.configs.wordlimitenabled = parseInt(this.configs.wordlimitenabled, 10);
|
||||
this.wordLimitEnabled = !!parseInt(this.configs.wordlimitenabled, 10);
|
||||
|
||||
// Set the text.
|
||||
this.text = text;
|
||||
|
@ -85,7 +84,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
|||
}
|
||||
|
||||
// Calculate initial words.
|
||||
if (this.configs.wordlimitenabled) {
|
||||
if (this.wordLimitEnabled) {
|
||||
this.words = this.textUtils.countWords(text);
|
||||
}
|
||||
}).finally(() => {
|
||||
|
@ -100,7 +99,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
|
|||
*/
|
||||
onChange(text: string): void {
|
||||
// Count words if needed.
|
||||
if (this.configs.wordlimitenabled) {
|
||||
if (this.wordLimitEnabled) {
|
||||
// Cancel previous wait.
|
||||
clearTimeout(this.wordCountTimeout);
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreWSProvider } from '@providers/ws';
|
||||
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 { AddonModAssignHelperProvider } from '../../../providers/helper';
|
||||
import { AddonModAssignSubmissionHandler } from '../../../providers/submission-delegate';
|
||||
|
@ -46,7 +48,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param plugin The plugin object.
|
||||
* @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.
|
||||
return false;
|
||||
}
|
||||
|
@ -62,7 +65,9 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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),
|
||||
files = this.assignProvider.getSubmissionPluginAttachments(plugin);
|
||||
let promise;
|
||||
|
@ -93,7 +98,7 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param edit Whether the user is editing.
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
@ -107,7 +112,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -118,7 +124,7 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param plugin The plugin object.
|
||||
* @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),
|
||||
files = this.assignProvider.getSubmissionPluginAttachments(plugin),
|
||||
promises = [];
|
||||
|
@ -153,7 +159,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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);
|
||||
|
||||
return text.length;
|
||||
|
@ -182,7 +189,9 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param inputData Data entered by the user for the submission.
|
||||
* @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.
|
||||
return this.assignOfflineProvider.getSubmission(assign.id, submission.userid).catch(() => {
|
||||
// No offline data found.
|
||||
|
@ -234,7 +243,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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> {
|
||||
|
||||
let text = this.getTextToSubmit(plugin, inputData);
|
||||
|
@ -274,8 +284,8 @@ export class AddonModAssignSubmissionOnlineTextHandler implements AddonModAssign
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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)
|
||||
: void | Promise<any> {
|
||||
prepareSyncData(assign: AddonModAssignAssign, submission: AddonModAssignSubmission,
|
||||
plugin: AddonModAssignPlugin, offlineData: any, pluginData: any, siteId?: string): void | Promise<any> {
|
||||
|
||||
const textData = offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor;
|
||||
if (textData) {
|
||||
|
|
|
@ -121,7 +121,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
// Try to get the book data.
|
||||
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
|
||||
this.dataRetrieved.emit(book);
|
||||
this.description = book.intro || this.description;
|
||||
this.description = book.intro;
|
||||
}).catch(() => {
|
||||
// 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 { CoreSite } from '@classes/site';
|
||||
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||
|
||||
/**
|
||||
* 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[]
|
||||
}
|
||||
};
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for books.
|
||||
|
@ -83,7 +52,7 @@ export class AddonModBookProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -96,7 +65,7 @@ export class AddonModBookProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
|
@ -106,7 +75,9 @@ export class AddonModBookProvider {
|
|||
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.
|
||||
if (response && response.books) {
|
||||
for (const i in response.books) {
|
||||
|
@ -401,3 +372,66 @@ export class AddonModBookProvider {
|
|||
{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 { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { AddonModChatProvider, AddonModChatChat } from '../../providers/chat';
|
||||
|
||||
/**
|
||||
* Component that displays a chat.
|
||||
|
@ -29,7 +29,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
component = AddonModChatProvider.COMPONENT;
|
||||
moduleName = 'chat';
|
||||
|
||||
chat: any;
|
||||
chat: AddonModChatChat;
|
||||
chatInfo: any;
|
||||
|
||||
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> {
|
||||
return this.chatProvider.getChat(this.courseId, this.module.id).then((chat) => {
|
||||
this.chat = chat;
|
||||
this.description = chat.intro || chat.description;
|
||||
this.description = chat.intro;
|
||||
|
||||
const now = this.timeUtils.timestamp();
|
||||
const span = chat.chattime - now;
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
|||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { AddonModChatProvider, AddonModChatMessageWithUserData } from '../../providers/chat';
|
||||
import { Network } from '@ionic-native/network';
|
||||
import * as moment from 'moment';
|
||||
|
||||
|
@ -37,7 +37,7 @@ export class AddonModChatChatPage {
|
|||
|
||||
loaded = false;
|
||||
title: string;
|
||||
messages = [];
|
||||
messages: AddonModChatMessageWithUserData[] = [];
|
||||
newMessage: string;
|
||||
polling: any;
|
||||
isOnline: boolean;
|
||||
|
@ -46,7 +46,7 @@ export class AddonModChatChatPage {
|
|||
protected logger;
|
||||
protected courseId: number;
|
||||
protected chatId: number;
|
||||
protected sessionId: number;
|
||||
protected sessionId: string;
|
||||
protected lastTime = 0;
|
||||
protected oldContentHeight = 0;
|
||||
protected onlineObserver: any;
|
||||
|
@ -131,9 +131,9 @@ export class AddonModChatChatPage {
|
|||
/**
|
||||
* 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) => {
|
||||
this.sessionId = sessionId;
|
||||
});
|
||||
|
@ -144,12 +144,12 @@ export class AddonModChatChatPage {
|
|||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected fetchMessages(): Promise<any> {
|
||||
protected fetchMessages(): Promise<void> {
|
||||
return this.chatProvider.getLatestMessages(this.sessionId, this.lastTime).then((messagesInfo) => {
|
||||
this.lastTime = messagesInfo.chatnewlasttime || 0;
|
||||
|
||||
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) {
|
||||
// New messages or beeps, scroll to bottom.
|
||||
setTimeout(() => this.scrollToBottom());
|
||||
|
@ -190,7 +190,7 @@ export class AddonModChatChatPage {
|
|||
*
|
||||
* @return Promised resolved when done.
|
||||
*/
|
||||
protected fetchMessagesInterval(): Promise<any> {
|
||||
protected fetchMessagesInterval(): Promise<void> {
|
||||
this.logger.debug('Polling for messages');
|
||||
if (!this.isOnline || this.pollingRunning) {
|
||||
// Obviously we cannot check for new messages when the app is offline.
|
||||
|
@ -225,7 +225,7 @@ export class AddonModChatChatPage {
|
|||
* @param prevMessage Previous message object.
|
||||
* @return True if messages are from diferent days, false othetwise.
|
||||
*/
|
||||
showDate(message: any, prevMessage: any): boolean {
|
||||
showDate(message: AddonModChatMessageWithUserData, prevMessage: AddonModChatMessageWithUserData): boolean {
|
||||
if (!prevMessage) {
|
||||
return true;
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ export class AddonModChatChatPage {
|
|||
});
|
||||
}
|
||||
|
||||
reconnect(): Promise<any> {
|
||||
reconnect(): Promise<void> {
|
||||
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.
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { IonicPage, NavParams } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { AddonModChatProvider, AddonModChatSessionMessageWithUserData } from '../../providers/chat';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -34,7 +34,7 @@ export class AddonModChatSessionMessagesPage {
|
|||
protected sessionEnd: number;
|
||||
protected groupId: number;
|
||||
protected loaded = false;
|
||||
protected messages = [];
|
||||
protected messages: AddonModChatSessionMessageWithUserData[] = [];
|
||||
|
||||
constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private chatProvider: AddonModChatProvider) {
|
||||
this.courseId = navParams.get('courseId');
|
||||
|
@ -55,7 +55,7 @@ export class AddonModChatSessionMessagesPage {
|
|||
return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId)
|
||||
.then((messages) => {
|
||||
return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => {
|
||||
this.messages = messages;
|
||||
this.messages = <AddonModChatSessionMessageWithUserData[]> messages;
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
||||
|
@ -84,7 +84,7 @@ export class AddonModChatSessionMessagesPage {
|
|||
* @param prevMessage Previous message object.
|
||||
* @return True if messages are from diferent days, false othetwise.
|
||||
*/
|
||||
showDate(message: any, prevMessage: any): boolean {
|
||||
showDate(message: AddonModChatSessionMessageWithUserData, prevMessage: AddonModChatSessionMessageWithUserData): boolean {
|
||||
if (!prevMessage) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CoreUserProvider } from '@core/user/providers/user';
|
|||
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
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.
|
||||
|
@ -73,13 +73,13 @@ export class AddonModChatSessionsPage {
|
|||
this.groupId = this.groupsProvider.validateGroupId(this.groupId, groupInfo);
|
||||
|
||||
return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll);
|
||||
}).then((sessions) => {
|
||||
}).then((sessions: AddonModChatSessionFormatted[]) => {
|
||||
// Fetch user profiles.
|
||||
const promises = [];
|
||||
|
||||
sessions.forEach((session) => {
|
||||
session.duration = session.sessionend - session.sessionstart;
|
||||
session.sessionusers.forEach((sessionUser) => {
|
||||
session.sessionusers.forEach((sessionUser: AddonModChatUserSessionFormatted) => {
|
||||
if (!sessionUser.userfullname) {
|
||||
// The WS does not return the user name, fetch user profile.
|
||||
promises.push(this.userProvider.getProfile(sessionUser.userid, this.courseId, true).then((user) => {
|
||||
|
@ -156,3 +156,18 @@ export class AddonModChatSessionsPage {
|
|||
$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 { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonModChatProvider } from '../../providers/chat';
|
||||
import { AddonModChatProvider, AddonModChatUser } from '../../providers/chat';
|
||||
import { Network } from '@ionic-native/network';
|
||||
|
||||
/**
|
||||
|
@ -30,12 +30,12 @@ import { Network } from '@ionic-native/network';
|
|||
})
|
||||
export class AddonModChatUsersPage {
|
||||
|
||||
users = [];
|
||||
users: AddonModChatUser[] = [];
|
||||
usersLoaded = false;
|
||||
currentUserId: number;
|
||||
isOnline: boolean;
|
||||
|
||||
protected sessionId: number;
|
||||
protected sessionId: string;
|
||||
protected onlineObserver: any;
|
||||
|
||||
constructor(navParams: NavParams, network: Network, zone: NgZone, private appProvider: CoreAppProvider,
|
||||
|
@ -77,7 +77,7 @@ export class AddonModChatUsersPage {
|
|||
*
|
||||
* @param user User object.
|
||||
*/
|
||||
talkTo(user: any): void {
|
||||
talkTo(user: AddonModChatUser): void {
|
||||
this.viewCtrl.dismiss({talkTo: user.fullname});
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ export class AddonModChatUsersPage {
|
|||
*
|
||||
* @param user User object.
|
||||
*/
|
||||
beepTo(user: any): void {
|
||||
beepTo(user: AddonModChatUser): void {
|
||||
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 { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for chats.
|
||||
|
@ -41,7 +42,7 @@ export class AddonModChatProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
|
@ -51,7 +52,9 @@ export class AddonModChatProvider {
|
|||
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) {
|
||||
const chat = response.chats.find((chat) => chat.coursemodule == cmId);
|
||||
if (chat) {
|
||||
|
@ -70,12 +73,14 @@ export class AddonModChatProvider {
|
|||
* @param chatId Chat instance ID.
|
||||
* @return Promise resolved when the WS is executed.
|
||||
*/
|
||||
loginUser(chatId: number): Promise<any> {
|
||||
loginUser(chatId: number): Promise<string> {
|
||||
const params = {
|
||||
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) {
|
||||
return response.chatsid;
|
||||
}
|
||||
|
@ -108,14 +113,16 @@ export class AddonModChatProvider {
|
|||
* @param beepUserId Beep user ID.
|
||||
* @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 = {
|
||||
chatsid: sessionId,
|
||||
messagetext: message,
|
||||
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) {
|
||||
return response.messageid;
|
||||
}
|
||||
|
@ -131,7 +138,7 @@ export class AddonModChatProvider {
|
|||
* @param lastTime Last time when messages were retrieved.
|
||||
* @return Promise resolved when the WS is executed.
|
||||
*/
|
||||
getLatestMessages(sessionId: number, lastTime: number): Promise<any> {
|
||||
getLatestMessages(sessionId: string, lastTime: number): Promise<AddonModChatGetChatLatestMessagesResult> {
|
||||
const params = {
|
||||
chatsid: sessionId,
|
||||
chatlasttime: lastTime
|
||||
|
@ -149,8 +156,10 @@ export class AddonModChatProvider {
|
|||
* @param courseId ID of the course the messages belong to.
|
||||
* @return Promise always resolved with the formatted messages.
|
||||
*/
|
||||
getMessagesUserData(messages: any[], courseId: number): Promise<any> {
|
||||
const promises = messages.map((message) => {
|
||||
getMessagesUserData(messages: (AddonModChatMessage | AddonModChatSessionMessage)[], courseId: number)
|
||||
: Promise<(AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData)[]> {
|
||||
|
||||
const promises = messages.map((message: AddonModChatMessageWithUserData | AddonModChatSessionMessageWithUserData) => {
|
||||
return this.userProvider.getProfile(message.userid, courseId, true).then((user) => {
|
||||
message.userfullname = user.fullname;
|
||||
message.userprofileimageurl = user.profileimageurl;
|
||||
|
@ -171,7 +180,7 @@ export class AddonModChatProvider {
|
|||
* @param sessionId Chat sessiond ID.
|
||||
* @return Promise resolved when the WS is executed.
|
||||
*/
|
||||
getChatUsers(sessionId: number): Promise<any> {
|
||||
getChatUsers(sessionId: string): Promise<AddonModChatGetChatUsersResult> {
|
||||
const params = {
|
||||
chatsid: sessionId
|
||||
};
|
||||
|
@ -206,7 +215,8 @@ export class AddonModChatProvider {
|
|||
* @since 3.5
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
chatid: chatId,
|
||||
|
@ -222,7 +232,7 @@ export class AddonModChatProvider {
|
|||
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) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
@ -245,7 +255,8 @@ export class AddonModChatProvider {
|
|||
* @since 3.5
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
chatid: chatId,
|
||||
|
@ -262,7 +273,9 @@ export class AddonModChatProvider {
|
|||
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) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
@ -390,3 +403,152 @@ export class AddonModChatProvider {
|
|||
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 { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonModChatProvider } from './chat';
|
||||
import { AddonModChatProvider, AddonModChatChat } from './chat';
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
// Prefetch chat and group info.
|
||||
const promises = [
|
||||
const promises: Promise<any>[] = [
|
||||
this.chatProvider.getChat(courseId, module.id, 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 = [];
|
||||
|
||||
let groupIds = [0];
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<!-- Activity availability messages -->
|
||||
<ion-card class="core-info-card" icon-start *ngIf="choiceNotOpenYet">
|
||||
<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.notopenyet' | 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: openTimeReadable} }}</p>
|
||||
</ion-card>
|
||||
<ion-card class="core-info-card" icon-start *ngIf="choiceClosed">
|
||||
<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>{{ 'addon.mod_choice.expired' | translate:{$a: choice.closeTimeReadable} }}</p>
|
||||
<p>{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}</p>
|
||||
</ion-card>
|
||||
|
||||
<!-- Choice done in offline but not synchronized -->
|
||||
|
@ -80,7 +80,7 @@
|
|||
<ion-item-group *ngFor="let result of results">
|
||||
<ion-item-divider text-wrap>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Optional, Injector } from '@angular/core';
|
|||
import { Content } from 'ionic-angular';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
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 { AddonModChoiceSyncProvider } from '../../providers/sync';
|
||||
|
||||
|
@ -31,9 +31,9 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
component = AddonModChoiceProvider.COMPONENT;
|
||||
moduleName = 'choice';
|
||||
|
||||
choice: any;
|
||||
options = [];
|
||||
selectedOption: any;
|
||||
choice: AddonModChoiceChoice;
|
||||
options: AddonModChoiceOption[] = [];
|
||||
selectedOption: {id: number};
|
||||
choiceNotOpenYet = false;
|
||||
choiceClosed = false;
|
||||
canEdit = false;
|
||||
|
@ -43,6 +43,8 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
labels = [];
|
||||
results = [];
|
||||
publishInfo: string; // Message explaining the user what will happen with his choices.
|
||||
openTimeReadable: string;
|
||||
closeTimeReadable: string;
|
||||
|
||||
protected userId: number;
|
||||
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) => {
|
||||
this.choice = choice;
|
||||
this.choice.timeopen = parseInt(choice.timeopen) * 1000;
|
||||
this.choice.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
|
||||
this.choice.timeclose = parseInt(choice.timeclose) * 1000;
|
||||
this.choice.closeTimeReadable = this.timeUtils.userDate(choice.timeclose);
|
||||
this.choice.timeopen = choice.timeopen * 1000;
|
||||
this.choice.timeclose = choice.timeclose * 1000;
|
||||
this.openTimeReadable = this.timeUtils.userDate(choice.timeopen);
|
||||
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.choiceClosed = choice.timeclose && choice.timeclose <= this.now;
|
||||
|
||||
|
@ -175,7 +177,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
|
||||
if (hasOffline) {
|
||||
promise = this.choiceOffline.getResponse(this.choice.id).then((response) => {
|
||||
const optionsKeys = {};
|
||||
const optionsKeys: {[id: number]: AddonModChoiceOption} = {};
|
||||
options.forEach((option) => {
|
||||
optionsKeys[option.id] = option;
|
||||
});
|
||||
|
@ -223,7 +225,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
promise = Promise.resolve(options);
|
||||
}
|
||||
|
||||
promise.then((options) => {
|
||||
promise.then((options: AddonModChoiceOption[]) => {
|
||||
const isOpen = this.isChoiceOpen();
|
||||
|
||||
let hasAnswered = false;
|
||||
|
@ -291,11 +293,11 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
let hasVotes = false;
|
||||
this.data = [];
|
||||
this.labels = [];
|
||||
results.forEach((result) => {
|
||||
results.forEach((result: AddonModChoiceResultFormatted) => {
|
||||
if (result.numberofuser > 0) {
|
||||
hasVotes = true;
|
||||
}
|
||||
result.percentageamount = parseFloat(result.percentageamount).toFixed(1);
|
||||
result.percentageamountfixed = result.percentageamount.toFixed(1);
|
||||
this.data.push(result.numberofuser);
|
||||
this.labels.push(result.text);
|
||||
});
|
||||
|
@ -429,3 +431,10 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
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 { AddonModChoiceOfflineProvider } from './offline';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for choices.
|
||||
|
@ -118,7 +119,9 @@ export class AddonModChoiceProvider {
|
|||
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.
|
||||
if (!response || response.status === false) {
|
||||
return Promise.reject(this.utils.createFakeWSError(''));
|
||||
|
@ -179,7 +182,7 @@ export class AddonModChoiceProvider {
|
|||
* @return Promise resolved when the choice is retrieved.
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
|
@ -198,7 +201,9 @@ export class AddonModChoiceProvider {
|
|||
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) {
|
||||
const currentChoice = response.choices.find((choice) => choice[key] == value);
|
||||
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).
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -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).
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -247,7 +254,7 @@ export class AddonModChoiceProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
choiceid: choiceId
|
||||
|
@ -262,7 +269,9 @@ export class AddonModChoiceProvider {
|
|||
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) {
|
||||
return response.options;
|
||||
}
|
||||
|
@ -280,7 +289,7 @@ export class AddonModChoiceProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
choiceid: choiceId
|
||||
|
@ -294,7 +303,9 @@ export class AddonModChoiceProvider {
|
|||
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) {
|
||||
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;
|
||||
canGetFolder: boolean;
|
||||
contents: any;
|
||||
moduleContents: any;
|
||||
|
||||
constructor(injector: Injector, private folderProvider: AddonModFolderProvider, private courseProvider: CoreCourseProvider,
|
||||
private appProvider: CoreAppProvider, private folderHelper: AddonModFolderHelperProvider) {
|
||||
|
@ -87,9 +88,9 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
|
||||
if (this.path) {
|
||||
// Subfolder.
|
||||
this.contents = module.contents;
|
||||
this.contents = this.moduleContents;
|
||||
} 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) {
|
||||
promise = this.folderProvider.getFolder(this.courseId, this.module.id).then((folder) => {
|
||||
return this.courseProvider.loadModuleContents(this.module, this.courseId).then(() => {
|
||||
folder.contents = this.module.contents;
|
||||
this.moduleContents = this.module.contents;
|
||||
|
||||
return folder;
|
||||
});
|
||||
|
@ -117,17 +118,13 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
folder.contents = this.module.contents;
|
||||
}
|
||||
this.module = folder;
|
||||
this.moduleContents = folder.contents;
|
||||
|
||||
return folder;
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then((folder) => {
|
||||
if (folder) {
|
||||
this.description = folder.intro || folder.description;
|
||||
this.dataRetrieved.emit(folder);
|
||||
}
|
||||
|
||||
this.showModuleData(folder);
|
||||
|
||||
// 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 { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for folder.
|
||||
|
@ -43,7 +44,7 @@ export class AddonModFolderProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -56,7 +57,7 @@ export class AddonModFolderProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
|
@ -66,7 +67,9 @@ export class AddonModFolderProvider {
|
|||
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) {
|
||||
const currentFolder = response.folders.find((folder) => {
|
||||
return folder[key] == value;
|
||||
|
@ -147,3 +150,33 @@ export class AddonModFolderProvider {
|
|||
{}, 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 = [];
|
||||
|
||||
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);
|
||||
}));
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
|||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for IMSCP.
|
||||
|
@ -157,7 +158,7 @@ export class AddonModImscpProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
|
@ -167,7 +168,9 @@ export class AddonModImscpProvider {
|
|||
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) {
|
||||
const currentImscp = response.imscps.find((imscp) => imscp[key] == value);
|
||||
if (currentImscp) {
|
||||
|
@ -188,7 +191,7 @@ export class AddonModImscpProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -324,3 +327,32 @@ export class AddonModImscpProvider {
|
|||
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 { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for labels.
|
||||
|
@ -52,7 +53,7 @@ export class AddonModLabelProvider {
|
|||
* @return Promise resolved when the label is retrieved.
|
||||
*/
|
||||
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) => {
|
||||
const params = {
|
||||
|
@ -70,7 +71,9 @@ export class AddonModLabelProvider {
|
|||
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) {
|
||||
const currentLabel = response.labels.find((label) => label[key] == value);
|
||||
if (currentLabel) {
|
||||
|
@ -93,7 +96,8 @@ export class AddonModLabelProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -107,7 +111,8 @@ export class AddonModLabelProvider {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -170,3 +175,29 @@ export class AddonModLabelProvider {
|
|||
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 { Content } from 'ionic-angular';
|
||||
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.
|
||||
|
@ -28,7 +28,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo
|
|||
component = AddonModLtiProvider.COMPONENT;
|
||||
moduleName = 'lti';
|
||||
|
||||
lti: any; // The LTI object.
|
||||
lti: AddonModLtiLti; // The LTI object.
|
||||
|
||||
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> {
|
||||
return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => {
|
||||
this.lti = ltiData;
|
||||
this.description = this.lti.intro || this.description;
|
||||
this.description = this.lti.intro;
|
||||
this.dataRetrieved.emit(this.lti);
|
||||
}).then(() => {
|
||||
// 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 { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||
import { CoreSite } from '@classes/site';
|
||||
|
||||
export interface AddonModLtiParam {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws';
|
||||
|
||||
/**
|
||||
* Service that provides some features for LTI.
|
||||
|
@ -104,7 +100,7 @@ export class AddonModLtiProvider {
|
|||
* @param cmId Course module ID.
|
||||
* @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 = {
|
||||
courseids: [courseId]
|
||||
};
|
||||
|
@ -113,7 +109,9 @@ export class AddonModLtiProvider {
|
|||
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) {
|
||||
const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId);
|
||||
if (currentLti) {
|
||||
|
@ -141,8 +139,8 @@ export class AddonModLtiProvider {
|
|||
* @param id LTI id.
|
||||
* @return Promise resolved when the launch data is retrieved.
|
||||
*/
|
||||
getLtiLaunchData(id: number): Promise<any> {
|
||||
const params: any = {
|
||||
getLtiLaunchData(id: number): Promise<AddonModLtiGetToolLaunchDataResult> {
|
||||
const params = {
|
||||
toolid: id
|
||||
};
|
||||
|
||||
|
@ -154,7 +152,9 @@ export class AddonModLtiProvider {
|
|||
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) {
|
||||
return response;
|
||||
}
|
||||
|
@ -227,3 +227,66 @@ export class AddonModLtiProvider {
|
|||
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