Merge branch '1.11.x' of github.com:chamilo/chamilo-lms into 1.11.x

pull/2731/head
Julio Montoya 7 years ago
commit 2c40d4b9aa
  1. 11
      main/gradebook/index.php
  2. 24
      main/inc/lib/template.lib.php
  3. 2
      main/lang/english/trad4all.inc.php
  4. 20
      plugin/ims_lti/Entity/ImsLtiTool.php
  5. 39
      plugin/ims_lti/ImsLtiPlugin.php
  6. 4
      plugin/ims_lti/configure.php
  7. 12
      plugin/ims_lti/form.php
  8. 5
      plugin/ims_lti/src/Form/FrmAdd.php
  9. 7
      plugin/ims_lti/src/Form/FrmEdit.php
  10. 13
      plugin/ims_lti/src/ImsLtiServiceReadRequest.php
  11. 14
      plugin/ims_lti/src/ImsLtiServiceReplaceRequest.php
  12. 8
      plugin/ims_lti/src/ImsLtiServiceRequestFactory.php
  13. 2
      plugin/ims_lti/src/ImsLtiServiceResponseFactory.php
  14. 2
      plugin/ims_lti/src/ImsLtiServiceResponseStatus.php
  15. 31
      plugin/ims_lti/src/ImsLtiServiceUnsupportedRequest.php
  16. 28
      plugin/ims_lti/src/ImsLtiServiceUnsupportedResponse.php
  17. 2
      plugin/ims_lti/view/start.tpl
  18. 10
      tests/behat/features/bootstrap/FeatureContext.php
  19. 4
      tests/behat/features/toolExercise.feature

@ -772,6 +772,12 @@ if (!api_is_allowed_to_edit(null, true)) {
if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)) { if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)) {
echo '<meta http-equiv="refresh" content="0;url='.api_get_self().'?'.api_get_cidreq().'" />'; echo '<meta http-equiv="refresh" content="0;url='.api_get_self().'?'.api_get_cidreq().'" />';
} else { } else {
// Tool introduction
Display::display_introduction_section(
TOOL_GRADEBOOK,
['ToolbarSet' => 'AssessmentsIntroduction']
);
if (!empty($actionsLeft)) { if (!empty($actionsLeft)) {
echo $toolbar = Display::toolbarAction( echo $toolbar = Display::toolbarAction(
'gradebook-student-actions', 'gradebook-student-actions',
@ -780,11 +786,6 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
} }
if (api_is_allowed_to_edit(null, true)) { if (api_is_allowed_to_edit(null, true)) {
// Tool introduction
Display::display_introduction_section(
TOOL_GRADEBOOK,
['ToolbarSet' => 'AssessmentsIntroduction']
);
if (((empty($selectCat)) || (isset($_GET['cidReq']) && $_GET['cidReq'] !== '')) || if (((empty($selectCat)) || (isset($_GET['cidReq']) && $_GET['cidReq'] !== '')) ||
(isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'false') (isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'false')

@ -1852,20 +1852,25 @@ class Template
$course = api_get_course_entity($courseId); $course = api_get_course_entity($courseId);
// @TODO: support right-to-left in title // @TODO: support right-to-left in title
$socialMeta .= '<meta property="og:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n"; $socialMeta .= '<meta property="og:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="twitter:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="og:url" content="'.api_get_course_url($course->getCode()).'" />'."\n"; $socialMeta .= '<meta property="og:url" content="'.api_get_course_url($course->getCode()).'" />'."\n";
$metaDescription = api_get_setting('meta_description'); $metaDescription = api_get_setting('meta_description');
if (!empty($course->getDescription())) { if (!empty($course->getDescription())) {
$socialMeta .= '<meta property="og:description" content="'.strip_tags($course->getDescription()).'" />'."\n"; $socialMeta .= '<meta property="og:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
$socialMeta .= '<meta property="twitter:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
} elseif (!empty($metaDescription)) { } elseif (!empty($metaDescription)) {
$socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n"; $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
$socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
} }
$picture = CourseManager::getPicturePath($course, true); $picture = CourseManager::getPicturePath($course, true);
if (!empty($picture)) { if (!empty($picture)) {
$socialMeta .= '<meta property="og:image" content="'.$picture.'" />'."\n"; $socialMeta .= '<meta property="og:image" content="'.$picture.'" />'."\n";
$socialMeta .= '<meta property="twitter:image" content="'.$picture.'" />'."\n";
$socialMeta .= '<meta property="twitter:image:alt" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
} else { } else {
$socialMeta .= $this->getMetaPortalImagePath(); $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
} }
} elseif ($sessionId !== 0) { } elseif ($sessionId !== 0) {
// If we are on a session "about" screen, publish info about the session // If we are on a session "about" screen, publish info about the session
@ -1873,6 +1878,7 @@ class Template
$session = $em->find('ChamiloCoreBundle:Session', $sessionId); $session = $em->find('ChamiloCoreBundle:Session', $sessionId);
$socialMeta .= '<meta property="og:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n"; $socialMeta .= '<meta property="og:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="twitter:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH)."session/{$session->getId()}/about/".'" />'."\n"; $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH)."session/{$session->getId()}/about/".'" />'."\n";
$sessionValues = new ExtraFieldValue('session'); $sessionValues = new ExtraFieldValue('session');
@ -1882,19 +1888,23 @@ class Template
if (!empty($sessionImage) && is_file($sessionImageSysPath)) { if (!empty($sessionImage) && is_file($sessionImageSysPath)) {
$sessionImagePath = api_get_path(WEB_UPLOAD_PATH).$sessionImage; $sessionImagePath = api_get_path(WEB_UPLOAD_PATH).$sessionImage;
$socialMeta .= '<meta property="og:image" content="'.$sessionImagePath.'" />'."\n"; $socialMeta .= '<meta property="og:image" content="'.$sessionImagePath.'" />'."\n";
$socialMeta .= '<meta property="twitter:image" content="'.$sessionImagePath.'" />'."\n";
$socialMeta .= '<meta property="twitter:image:alt" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
} else { } else {
$socialMeta .= $this->getMetaPortalImagePath(); $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
} }
} else { } else {
// Otherwise (not a course nor a session, nor a user, nor a badge), publish portal info // Otherwise (not a course nor a session, nor a user, nor a badge), publish portal info
$socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n"; $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="twitter:title" content="'.$metaTitle.'" />'."\n";
$socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n"; $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
$metaDescription = api_get_setting('meta_description'); $metaDescription = api_get_setting('meta_description');
if (!empty($metaDescription)) { if (!empty($metaDescription)) {
$socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n"; $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
$socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
} }
$socialMeta .= $this->getMetaPortalImagePath(); $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
} }
} }
} }
@ -1906,10 +1916,10 @@ class Template
/** /**
* Get platform meta image tag (check meta_image_path setting, then use the logo). * Get platform meta image tag (check meta_image_path setting, then use the logo).
* * @param string $imageAlt The alt attribute for the image
* @return string The meta image HTML tag, or empty * @return string The meta image HTML tag, or empty
*/ */
private function getMetaPortalImagePath() private function getMetaPortalImagePath($imageAlt = '')
{ {
// Load portal meta image if defined // Load portal meta image if defined
$metaImage = api_get_setting('meta_image_path'); $metaImage = api_get_setting('meta_image_path');
@ -1919,11 +1929,15 @@ class Template
if (!empty($metaImage)) { if (!empty($metaImage)) {
if (is_file($metaImageSysPath)) { if (is_file($metaImageSysPath)) {
$portalImageMeta = '<meta property="og:image" content="'.$metaImageWebPath.'" />'."\n"; $portalImageMeta = '<meta property="og:image" content="'.$metaImageWebPath.'" />'."\n";
$portalImageMeta .= '<meta property="twitter:image" content="'.$metaImageWebPath.'" />'."\n";
$portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
} }
} else { } else {
$logo = ChamiloApi::getPlatformLogoPath($this->theme); $logo = ChamiloApi::getPlatformLogoPath($this->theme);
if (!empty($logo)) { if (!empty($logo)) {
$portalImageMeta = '<meta property="og:image" content="'.$logo.'" />'."\n"; $portalImageMeta = '<meta property="og:image" content="'.$logo.'" />'."\n";
$portalImageMeta .= '<meta property="twitter:image" content="'.$logo.'" />'."\n";
$portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
} }
} }

@ -7582,7 +7582,7 @@ $SkillProfile = "Skill profile";
$AchievedSkills = "Achieved skills"; $AchievedSkills = "Achieved skills";
$BusinessCard = "Business card"; $BusinessCard = "Business card";
$BadgeDetails = "Badge details"; $BadgeDetails = "Badge details";
$TheUserXNotYetAchievedTheSkillX = "The user %s not yet achieved the skill \"%s\""; $TheUserXNotYetAchievedTheSkillX = "The user %s has not achieved the skill \"%s\" yet";
$IssuedBadgeInformation = "Issued badge information"; $IssuedBadgeInformation = "Issued badge information";
$RecipientDetails = "Recipient details"; $RecipientDetails = "Recipient details";
$SkillAcquiredAt = "Skill acquired at"; $SkillAcquiredAt = "Skill acquired at";

@ -249,6 +249,26 @@ class ImsLtiTool
return $this->course === null; return $this->course === null;
} }
/**
* @param array $params
*
* @return null|string
*/
public function encodeCustomParams(array $params)
{
if (empty($params)) {
return null;
}
$pairs = [];
foreach ($params as $key => $value) {
$pairs[] = "$key=$value";
}
return implode("\n", $pairs);
}
/** /**
* @return array * @return array
*/ */

@ -298,18 +298,22 @@ class ImsLtiPlugin extends Plugin
*/ */
public static function getUserRoles(User $user) public static function getUserRoles(User $user)
{ {
if (DRH === $user->getStatus()) {
return 'urn:lti:role:ims/lis/Mentor';
}
if ($user->getStatus() === INVITEE) { if ($user->getStatus() === INVITEE) {
return 'Learner/GuestLearner,Learner'; return 'Learner,urn:lti:role:ims/lis/Learner/GuestLearner';
} }
if (!api_is_allowed_to_edit(false, true)) { if (!api_is_allowed_to_edit(false, true)) {
return 'Learner,Learner/Learner'; return 'Learner';
} }
$roles = ['Instructor']; $roles = ['Instructor'];
if (api_is_platform_admin_by_id($user->getId())) { if (api_is_platform_admin_by_id($user->getId())) {
$roles[] = 'Administrator/SystemAdministrator'; $roles[] = 'urn:lti:role:ims/lis/Administrator';
} }
return implode(',', $roles); return implode(',', $roles);
@ -331,24 +335,22 @@ class ImsLtiPlugin extends Plugin
} }
/** /**
* @param Course $course * @param User $currentUser
* @param Session|null $session
* *
* @return string * @return string
*/ */
public static function getRoleScopeMentor(Course $course, Session $session = null) public static function getRoleScopeMentor(User $currentUser)
{ {
$scope = []; if (DRH !== $currentUser->getStatus()) {
return '';
if ($session) {
$students = $session->getUserCourseSubscriptionsByStatus($course, Session::STUDENT);
} else {
$students = $course->getStudents();
} }
/** @var SessionRelCourseRelUser|CourseRelUser $subscription */ $followedUsers = UserManager::get_users_followed_by_drh($currentUser->getId());
foreach ($students as $subscription) {
$scope[] = self::generateToolUserId($subscription->getUser()->getId()); $scope = [];
foreach ($followedUsers as $userInfo) {
$scope[] = self::generateToolUserId($userInfo['user_id']);
} }
return implode(',', $scope); return implode(',', $scope);
@ -394,6 +396,13 @@ class ImsLtiPlugin extends Plugin
!empty($contentItem['text']) ? $contentItem['text'] : null !empty($contentItem['text']) ? $contentItem['text'] : null
); );
if (!empty($contentItem['custom'])) {
$newLtiTool
->setCustomParams(
$newLtiTool->encodeCustomParams($contentItem['custom'])
);
}
$em->persist($newLtiTool); $em->persist($newLtiTool);
$em->flush(); $em->flush();

@ -126,7 +126,9 @@ switch ($action) {
$tool $tool
->setName($formValues['name']) ->setName($formValues['name'])
->setDescription($formValues['description']) ->setDescription(
empty($formValues['description']) ? null : $formValues['description']
)
->setActiveDeepLinking( ->setActiveDeepLinking(
!empty($formValues['deep_linking']) !empty($formValues['deep_linking'])
) )

@ -82,15 +82,19 @@ $params['roles'] = ImsLtiPlugin::getUserRoles($user);
if ($tool->isSharingName()) { if ($tool->isSharingName()) {
$params['lis_person_name_given'] = $user->getFirstname(); $params['lis_person_name_given'] = $user->getFirstname();
$params['lis_person_name_family'] = $user->getLastname(); $params['lis_person_name_family'] = $user->getLastname();
$params['lis_person_name_full'] = $user->getCompleteName(); $params['lis_person_name_full'] = $user->getFirstname().' '.$user->getLastname();
} }
if ($tool->isSharingEmail()) { if ($tool->isSharingEmail()) {
$params['lis_person_contact_email_primary'] = $user->getEmail(); $params['lis_person_contact_email_primary'] = $user->getEmail();
} }
if (api_is_allowed_to_edit(false, true)) { if (DRH === $user->getStatus()) {
$params['role_scope_mentor'] = ImsLtiPlugin::getRoleScopeMentor($course, $session); $scopeMentor = ImsLtiPlugin::getRoleScopeMentor($user);
if (!empty($scopeMentor)) {
$params['role_scope_mentor'] = $scopeMentor;
}
} }
$params['context_id'] = $course->getId(); $params['context_id'] = $course->getId();
@ -132,7 +136,7 @@ $result = $oauth->sign(array(
encType="application/x-www-form-urlencoded"> encType="application/x-www-form-urlencoded">
<?php <?php
foreach ($result["parameters"] as $key => $values) { //Dump parameters foreach ($result["parameters"] as $key => $values) { //Dump parameters
echo '<input type="hidden" name="'.$key.'" value="'.$values.'" />'; echo '<input type="hidden" name="'.$key.'" value="'.htmlspecialchars($values).'" />'.PHP_EOL;
} }
?> ?>
<button type="submit"> <button type="submit">

@ -42,8 +42,7 @@ class FrmAdd extends FormValidator
$this->addTextarea('description', get_lang('Description')); $this->addTextarea('description', get_lang('Description'));
if (null === $this->baseTool) { if (null === $this->baseTool) {
$this->addElement('url', 'launch_url', $plugin->get_lang('LaunchUrl')); $this->addUrl('launch_url', $plugin->get_lang('LaunchUrl'), true);
$this->addRule('launch_url', get_lang('Required'), 'required');
$this->addText('consumer_key', $plugin->get_lang('ConsumerKey')); $this->addText('consumer_key', $plugin->get_lang('ConsumerKey'));
$this->addText('shared_secret', $plugin->get_lang('SharedSecret')); $this->addText('shared_secret', $plugin->get_lang('SharedSecret'));
} }
@ -74,6 +73,8 @@ class FrmAdd extends FormValidator
$this->addHtml('</div>'); $this->addHtml('</div>');
$this->addButtonCreate($plugin->get_lang('AddExternalTool')); $this->addButtonCreate($plugin->get_lang('AddExternalTool'));
$this->applyFilter('__ALL__', 'Security::remove_XSS'); $this->applyFilter('__ALL__', 'Security::remove_XSS');
$this->applyFilter('__ALL__', 'htmlspecialchars_decode');
$this->applyFilter('__ALL__', 'trim');
} }
public function setDefaultValues() public function setDefaultValues()

@ -59,8 +59,7 @@ class FrmEdit extends FormValidator
$this->addTextarea('description', get_lang('Description')); $this->addTextarea('description', get_lang('Description'));
if (null === $parent) { if (null === $parent) {
$this->addElement('url', 'launch_url', $plugin->get_lang('LaunchUrl')); $this->addUrl('launch_url', $plugin->get_lang('LaunchUrl'), true);
$this->addRule('launch_url', get_lang('Required'), 'required');
$this->addText('consumer_key', $plugin->get_lang('ConsumerKey')); $this->addText('consumer_key', $plugin->get_lang('ConsumerKey'));
$this->addText('shared_secret', $plugin->get_lang('SharedSecret')); $this->addText('shared_secret', $plugin->get_lang('SharedSecret'));
} }
@ -89,10 +88,12 @@ class FrmEdit extends FormValidator
$this->addCheckBox('share_email', null, $plugin->get_lang('ShareLauncherEmail')); $this->addCheckBox('share_email', null, $plugin->get_lang('ShareLauncherEmail'));
$this->addCheckBox('share_picture', null, $plugin->get_lang('ShareLauncherPicture')); $this->addCheckBox('share_picture', null, $plugin->get_lang('ShareLauncherPicture'));
$this->addHtml('</div>'); $this->addHtml('</div>');
$this->addButtonCreate($plugin->get_lang('EditExternalTool')); $this->addButtonUpdate($plugin->get_lang('EditExternalTool'));
$this->addHidden('id', $this->tool->getId()); $this->addHidden('id', $this->tool->getId());
$this->addHidden('action', 'edit'); $this->addHidden('action', 'edit');
$this->applyFilter('__ALL__', 'Security::remove_XSS'); $this->applyFilter('__ALL__', 'Security::remove_XSS');
$this->applyFilter('__ALL__', 'htmlspecialchars_decode');
$this->applyFilter('__ALL__', 'trim');
} }
public function setDefaultValues() public function setDefaultValues()

@ -51,16 +51,17 @@ class ImsLtiServiceReadRequest extends ImsLtiServiceRequest
if (!empty($results)) { if (!empty($results)) {
/** @var Result $result */ /** @var Result $result */
$result = $results[0]; $result = $results[0];
$ltiScore = 0;
if (!empty($result->get_score())) { if (!empty($result->get_score())) {
$ltiScore = $result->get_score() / $evaluation->getMax(); $ltiScore = $result->get_score() / $evaluation->getMax();
$responseDescription = sprintf(
get_plugin_lang('ScoreForXUserIsYScore', 'ImsLtiPlugin'),
$user->getId(),
$ltiScore
);
} }
$responseDescription = sprintf(
get_plugin_lang('ScoreForXUserIsYScore', 'ImsLtiPlugin'),
$user->getId(),
$ltiScore
);
} }
$this->statusInfo $this->statusInfo

@ -18,7 +18,7 @@ class ImsLtiServiceReplaceRequest extends ImsLtiServiceRequest
{ {
parent::__construct($xml); parent::__construct($xml);
$this->responseType = ImsLtiServiceResponse::TYPE_DELETE; $this->responseType = ImsLtiServiceResponse::TYPE_REPLACE;
$this->xmlRequest = $this->xmlRequest->replaceResultRequest; $this->xmlRequest = $this->xmlRequest->replaceResultRequest;
} }
@ -26,7 +26,17 @@ class ImsLtiServiceReplaceRequest extends ImsLtiServiceRequest
{ {
$resultRecord = $this->xmlRequest->resultRecord; $resultRecord = $this->xmlRequest->resultRecord;
$sourcedId = (string) $resultRecord->sourcedGUID->sourcedId; $sourcedId = (string) $resultRecord->sourcedGUID->sourcedId;
$resultScore = (float) $resultRecord->result->resultScore->textString; $resultScore = (string) $resultRecord->result->resultScore->textString;
if (!is_numeric($resultScore)) {
$this->statusInfo
->setSeverity(ImsLtiServiceResponseStatus::SEVERITY_ERROR)
->setCodeMajor(ImsLtiServiceResponseStatus::CODEMAJOR_FAILURE);
return;
}
$resultScore = (float) $resultScore;
if (0 > $resultScore || 1 < $resultScore) { if (0 > $resultScore || 1 < $resultScore) {
$this->statusInfo $this->statusInfo

@ -16,13 +16,19 @@ class ImsLtiServiceRequestFactory
$bodyChildren = $xml->imsx_POXBody->children(); $bodyChildren = $xml->imsx_POXBody->children();
if (!empty($bodyChildren)) { if (!empty($bodyChildren)) {
switch ($bodyChildren->getName()) { $name = $bodyChildren->getName();
switch ($name) {
case 'replaceResultRequest': case 'replaceResultRequest':
return new ImsLtiServiceReplaceRequest($xml); return new ImsLtiServiceReplaceRequest($xml);
case 'readResultRequest': case 'readResultRequest':
return new ImsLtiServiceReadRequest($xml); return new ImsLtiServiceReadRequest($xml);
case 'deleteResultRequest': case 'deleteResultRequest':
return new ImsLtiServiceDeleteRequest($xml); return new ImsLtiServiceDeleteRequest($xml);
default:
$name = str_replace(['ResultRequest', 'Request'], '', $name);
return new ImsLtiServiceUnsupportedRequest($xml, $name);
} }
} }

@ -22,6 +22,8 @@ class ImsLtiServiceResponseFactory
return new ImsLtiServiceReadResponse($statusInfo, $bodyParam); return new ImsLtiServiceReadResponse($statusInfo, $bodyParam);
case ImsLtiServiceResponse::TYPE_DELETE: case ImsLtiServiceResponse::TYPE_DELETE:
return new ImsLtiServiceDeleteResponse($statusInfo, $bodyParam); return new ImsLtiServiceDeleteResponse($statusInfo, $bodyParam);
default:
return new ImsLtiServiceUnsupportedResponse($statusInfo, $type);
} }
return null; return null;

@ -13,7 +13,7 @@ class ImsLtiServiceResponseStatus
const CODEMAJOR_SUCCESS = 'success'; const CODEMAJOR_SUCCESS = 'success';
const CODEMAJOR_PROCESSING = 'processing'; const CODEMAJOR_PROCESSING = 'processing';
const CODEMAJOR_FAILURE = 'failure'; const CODEMAJOR_FAILURE = 'failure';
const CODEMAJOR_UNSUPPORTED = 'supported'; const CODEMAJOR_UNSUPPORTED = 'unsupported';
/** /**
* @var string * @var string

@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class ImsLtiServiceUnsupportedRequest.
*/
class ImsLtiServiceUnsupportedRequest extends ImsLtiServiceRequest
{
/**
* ImsLtiDeleteServiceRequest constructor.
*
* @param SimpleXMLElement $xml
* @param string $name
*/
public function __construct(SimpleXMLElement $xml, $name)
{
parent::__construct($xml);
$this->responseType = $name;
}
protected function processBody()
{
$this->statusInfo
->setSeverity(ImsLtiServiceResponseStatus::SEVERITY_STATUS)
->setCodeMajor(ImsLtiServiceResponseStatus::CODEMAJOR_UNSUPPORTED)
->setDescription(
$this->responseType.' is not supported'
);
}
}

@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class ImsLtiServiceUnsupportedResponse.
*/
class ImsLtiServiceUnsupportedResponse extends ImsLtiServiceResponse
{
/**
* ImsLtiServiceUnsupportedResponse constructor.
*
* @param ImsLtiServiceResponseStatus $statusInfo
* @param string $type
*/
public function __construct(ImsLtiServiceResponseStatus $statusInfo, $type)
{
$statusInfo->setOperationRefIdentifier($type);
parent::__construct($statusInfo);
}
/**
* @param SimpleXMLElement $xmlBody
*/
protected function generateBody(SimpleXMLElement $xmlBody)
{
}
}

@ -1,5 +1,5 @@
{% if tool.description %} {% if tool.description %}
<p class="lead">{{ tool.description }}</p> <p class="lead">{{ tool.description|nl2br }}</p>
{% endif %} {% endif %}
<div class="embed-responsive embed-responsive-4by3"> <div class="embed-responsive embed-responsive-4by3">
<iframe src="{{ launch_url }}" class="plugin-ims-lti-iframe"></iframe> <iframe src="{{ launch_url }}" class="plugin-ims-lti-iframe"></iframe>

@ -384,10 +384,18 @@ class FeatureContext extends MinkContext
*/ */
public function waitForThePageToBeLoaded() public function waitForThePageToBeLoaded()
{ {
//$this->getSession()->wait(10000, "document.readyState === 'complete'");
$this->getSession()->wait(3000); $this->getSession()->wait(3000);
} }
/**
* @When /^wait very long for the page to be loaded$/
*/
public function waitVeryLongForThePageToBeLoaded()
{
//$this->getSession()->wait(10000, "document.readyState === 'complete'");
$this->getSession()->wait(6000);
}
/** /**
* @When /^I check the "([^"]*)" radio button$/ * @When /^I check the "([^"]*)" radio button$/
*/ */

@ -384,7 +384,7 @@ Feature: Exercise tool
| none | 80 / 60 | 133.33% | | none | 80 / 60 | 133.33% |
| Total | 190 / 190 | 100% | | Total | 190 / 190 | 100% |
Scenario: Teacher see exercise results by categories Scenario: Teacher looks at exercise results by categories
Given I am on "/user_portal.php" Given I am on "/user_portal.php"
And I am on course "TEMP" homepage in session "Session Exercise" And I am on course "TEMP" homepage in session "Session Exercise"
Then I should see "TEMP (Session Exercise)" Then I should see "TEMP (Session Exercise)"
@ -392,7 +392,7 @@ Feature: Exercise tool
And I follow "Exercise for Behat test" And I follow "Exercise for Behat test"
And I follow "Results and feedback" And I follow "Results and feedback"
Then I should see "Learner score" Then I should see "Learner score"
And wait for the page to be loaded And wait very long for the page to be loaded
And I follow "Grade activity" And I follow "Grade activity"
Then I should see "Score for the test: 190 / 190" Then I should see "Score for the test: 190 / 190"
And I should see the table "#category_results": And I should see the table "#category_results":

Loading…
Cancel
Save