From 48d008fd0b94759b70e015d3574ebb879b74aa92 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 11 Feb 2021 16:08:48 -0500 Subject: [PATCH 01/27] xAPI: Add activity table to create when installing - refs BT#16742 --- plugin/xapi/src/XApiPlugin.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin/xapi/src/XApiPlugin.php b/plugin/xapi/src/XApiPlugin.php index 13dc2a1f933..268da068642 100644 --- a/plugin/xapi/src/XApiPlugin.php +++ b/plugin/xapi/src/XApiPlugin.php @@ -2,6 +2,7 @@ /* For licensing terms, see /license.txt */ +use Chamilo\PluginBundle\Entity\XApi\ActivityState; use Chamilo\PluginBundle\Entity\XApi\Cmi5Item; use Chamilo\PluginBundle\Entity\XApi\LrsAuth; use Chamilo\PluginBundle\Entity\XApi\SharedStatement; @@ -90,6 +91,7 @@ public function install() 'xapi_tool_launch', 'xapi_lrs_auth', 'xapi_cmi5_item', + 'xapi_activity_state', 'xapi_attachment', 'xapi_object', @@ -154,6 +156,7 @@ public function uninstallPluginDbTables() $schemaTool = new SchemaTool($em); $schemaTool->dropSchema( [ + $em->getClassMetadata(ActivityState::class), $em->getClassMetadata(SharedStatement::class), $em->getClassMetadata(ToolLaunch::class), $em->getClassMetadata(LrsAuth::class), @@ -495,6 +498,7 @@ private function installPluginDbTables() $em->getClassMetadata(ToolLaunch::class), $em->getClassMetadata(LrsAuth::class), $em->getClassMetadata(Cmi5Item::class), + $em->getClassMetadata(ActivityState::class), ] ); From 7fdbd24ed3384ba0f6f66f317f9f56e9f57371df Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 11 Feb 2021 19:10:32 -0500 Subject: [PATCH 02/27] xAPI: Fix State API resource - refs BT#16742 --- ...roller.php => ActivitiesStateController.php} | 17 ++++------------- plugin/xapi/src/Lrs/LrsRequest.php | 9 ++++++--- 2 files changed, 10 insertions(+), 16 deletions(-) rename plugin/xapi/src/Lrs/{ActivitiesController.php => ActivitiesStateController.php} (89%) diff --git a/plugin/xapi/src/Lrs/ActivitiesController.php b/plugin/xapi/src/Lrs/ActivitiesStateController.php similarity index 89% rename from plugin/xapi/src/Lrs/ActivitiesController.php rename to plugin/xapi/src/Lrs/ActivitiesStateController.php index 6e9ea84906e..7267559751a 100644 --- a/plugin/xapi/src/Lrs/ActivitiesController.php +++ b/plugin/xapi/src/Lrs/ActivitiesStateController.php @@ -12,16 +12,13 @@ use Xabbuh\XApi\Serializer\Symfony\Serializer; /** - * Class ActivitiesController. + * Class ActivitiesStateController. * * @package Chamilo\PluginBundle\XApi\Lrs */ -class ActivitiesController extends BaseController +class ActivitiesStateController extends BaseController { - /** - * @return \Symfony\Component\HttpFoundation\JsonResponse - */ - public function get() + public function get(): Response { $serializer = Serializer::createSerializer(); @@ -70,13 +67,7 @@ public function get() return JsonResponse::create($documentData); } - /** - * @throws \Doctrine\ORM\ORMException - * @throws \Doctrine\ORM\OptimisticLockException - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function put() + public function put(): Response { $activityId = $this->httpRequest->query->get('activityId'); $agent = $this->httpRequest->query->get('agent'); diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index 74ab2742760..c825f3b3295 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -65,15 +65,18 @@ public function send() /** * @return string|null */ - private function getControllerName() + private function getControllerName(): ?string { $segments = explode('/', $this->request->getPathInfo()); + $segments = array_filter($segments); + $segments = array_values($segments); - if (empty($segments[1])) { + if (empty($segments)) { return null; } - $controllerName = ucfirst($segments[1]).'Controller'; + $segments = array_map('ucfirst', $segments); + $controllerName = implode('', $segments).'Controller'; return "Chamilo\\PluginBundle\\XApi\Lrs\\$controllerName"; } From 434370fc97776b48005b39930c420e2c12a4e915 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 11 Feb 2021 19:12:29 -0500 Subject: [PATCH 03/27] Minor - Format code - refs BT#16742 --- plugin/xapi/src/Lrs/LrsRequest.php | 91 +++++++++++++----------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index c825f3b3295..31525be70af 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -5,6 +5,7 @@ namespace Chamilo\PluginBundle\XApi\Lrs; use Chamilo\PluginBundle\Entity\XApi\LrsAuth; +use Database; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\HttpFoundation\Response as HttpResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -62,57 +63,7 @@ public function send() $response->send(); } - /** - * @return string|null - */ - private function getControllerName(): ?string - { - $segments = explode('/', $this->request->getPathInfo()); - $segments = array_filter($segments); - $segments = array_values($segments); - - if (empty($segments)) { - return null; - } - - $segments = array_map('ucfirst', $segments); - $controllerName = implode('', $segments).'Controller'; - - return "Chamilo\\PluginBundle\\XApi\Lrs\\$controllerName"; - } - - /** - * @return string - */ - private function getMethodName() - { - $method = $this->request->getMethod(); - - return strtolower($method); - } - - /** - * @param string $version - * - * @return bool - */ - private function isValidVersion($version) - { - if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) { - if ('1.0' === $version) { - $this->request->headers->set('X-Experience-API-Version', '1.0.0'); - } - - return true; - } - - return false; - } - - /** - * @return bool - */ - private function validAuth() + private function validAuth(): bool { if (!$this->request->headers->has('Authorization')) { throw new AccessDeniedHttpException(); @@ -136,7 +87,7 @@ private function validAuth() list($username, $password) = $parts; - $auth = \Database::getManager() + $auth = Database::getManager() ->getRepository(LrsAuth::class) ->findOneBy( ['username' => $username, 'password' => $password, 'enabled' => true] @@ -148,4 +99,40 @@ private function validAuth() return true; } + + private function isValidVersion(string $version): string + { + if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) { + if ('1.0' === $version) { + $this->request->headers->set('X-Experience-API-Version', '1.0.0'); + } + + return true; + } + + return false; + } + + private function getControllerName(): ?string + { + $segments = explode('/', $this->request->getPathInfo()); + $segments = array_filter($segments); + $segments = array_values($segments); + + if (empty($segments)) { + return null; + } + + $segments = array_map('ucfirst', $segments); + $controllerName = implode('', $segments).'Controller'; + + return "Chamilo\\PluginBundle\\XApi\Lrs\\$controllerName"; + } + + private function getMethodName(): string + { + $method = $this->request->getMethod(); + + return strtolower($method); + } } From 1e4a0a5444f0f38bb22f6f98357b2b673994161d Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 12 Feb 2021 05:18:39 -0500 Subject: [PATCH 04/27] xAPI: Add activities profile API - refs BT#16742 --- plugin/xapi/README.md | 1 + plugin/xapi/src/Entity/ActivityProfile.php | 125 ++++++++++++++++++ .../src/Lrs/ActivitiesProfileController.php | 72 ++++++++++ plugin/xapi/src/Lrs/StatementsController.php | 25 ++++ plugin/xapi/src/XApiPlugin.php | 4 + 5 files changed, 227 insertions(+) create mode 100644 plugin/xapi/src/Entity/ActivityProfile.php create mode 100644 plugin/xapi/src/Lrs/ActivitiesProfileController.php diff --git a/plugin/xapi/README.md b/plugin/xapi/README.md index a59cc1a7c76..f9ba4890555 100644 --- a/plugin/xapi/README.md +++ b/plugin/xapi/README.md @@ -66,4 +66,5 @@ ALTER TABLE xapi_cmi5_item ADD CONSTRAINT FK_7CA116D8A977936C FOREIGN KEY (tree_ ALTER TABLE xapi_cmi5_item ADD CONSTRAINT FK_7CA116D8727ACA70 FOREIGN KEY (parent_id) REFERENCES xapi_cmi5_item (id) ON DELETE CASCADE; CREATE TABLE xapi_activity_state (id INT AUTO_INCREMENT NOT NULL, state_id VARCHAR(255) NOT NULL, activity_id VARCHAR(255) NOT NULL, agent LONGTEXT NOT NULL COMMENT '(DC2Type:json)', document_data LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_activity_profile (id INT AUTO_INCREMENT NOT NULL, profile_id VARCHAR(255) NOT NULL, activity_id VARCHAR(255) NOT NULL, document_data LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; ``` \ No newline at end of file diff --git a/plugin/xapi/src/Entity/ActivityProfile.php b/plugin/xapi/src/Entity/ActivityProfile.php new file mode 100644 index 00000000000..e1113ef3ef9 --- /dev/null +++ b/plugin/xapi/src/Entity/ActivityProfile.php @@ -0,0 +1,125 @@ +id; + } + + /** + * @param int $id + * + * @return ActivityProfile + */ + public function setId(int $id): ActivityProfile + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getProfileId(): string + { + return $this->profileId; + } + + /** + * @param string $profileId + * + * @return ActivityProfile + */ + public function setProfileId(string $profileId): ActivityProfile + { + $this->profileId = $profileId; + + return $this; + } + + /** + * @return string + */ + public function getActivityId(): string + { + return $this->activityId; + } + + /** + * @param string $activityId + * + * @return ActivityProfile + */ + public function setActivityId(string $activityId): ActivityProfile + { + $this->activityId = $activityId; + + return $this; + } + + /** + * @return array + */ + public function getDocumentData(): array + { + return $this->documentData; + } + + /** + * @param array $documentData + * + * @return ActivityProfile + */ + public function setDocumentData(array $documentData): ActivityProfile + { + $this->documentData = $documentData; + + return $this; + } +} diff --git a/plugin/xapi/src/Lrs/ActivitiesProfileController.php b/plugin/xapi/src/Lrs/ActivitiesProfileController.php new file mode 100644 index 00000000000..70c71e1c99b --- /dev/null +++ b/plugin/xapi/src/Lrs/ActivitiesProfileController.php @@ -0,0 +1,72 @@ +httpRequest->query->get('profileId'); + $activityId = $this->httpRequest->query->get('activityId'); + + $em = \Database::getManager(); + $profileRepo = $em->getRepository(ActivityProfile::class); + + /** @var ActivityProfile $activityProfile */ + $activityProfile = $profileRepo->findOneBy( + [ + 'profileId' => $profileId, + 'activityId' => $activityId, + ] + ); + + if (empty($activityProfile)) { + return Response::create(null, Response::HTTP_NO_CONTENT); + } + + return Response::create( + json_encode($activityProfile->getDocumentData()) + ); + } + public function put(): Response + { + $profileId = $this->httpRequest->query->get('profileId'); + $activityId = $this->httpRequest->query->get('activityId'); + $documentData = $this->httpRequest->getContent(); + + $em = \Database::getManager(); + $profileRepo = $em->getRepository(ActivityProfile::class); + + /** @var ActivityProfile $activityProfile */ + $activityProfile = $profileRepo->findOneBy( + [ + 'profileId' => $profileId, + 'activityId' => $activityId, + ] + ); + + if (empty($activityProfile)) { + $activityProfile = new ActivityProfile(); + $activityProfile + ->setProfileId($profileId) + ->setActivityId($activityId); + } + + $activityProfile->setDocumentData(json_decode($documentData, true)); + + $em->persist($activityProfile); + $em->flush(); + + return Response::create(null, Response::HTTP_NO_CONTENT); + } +} diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index dec9c97bd93..24a4a4307d6 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -5,8 +5,13 @@ namespace Chamilo\PluginBundle\XApi\Lrs; use Xabbuh\XApi\Model\Statement; +use Xabbuh\XApi\Serializer\Symfony\ActorSerializer; use Xabbuh\XApi\Serializer\Symfony\Serializer; +use Xabbuh\XApi\Serializer\Symfony\StatementResultSerializer; +use Xabbuh\XApi\Serializer\Symfony\StatementSerializer; +use XApi\LrsBundle\Controller\StatementGetController; use XApi\LrsBundle\Controller\StatementPutController; +use XApi\LrsBundle\Model\StatementsFilterFactory; use XApi\Repository\Doctrine\Mapping\Statement as StatementEntity; use XApi\Repository\Doctrine\Repository\StatementRepository; use XApiPlugin; @@ -18,6 +23,26 @@ */ class StatementsController extends BaseController { + public function get() + { + $pluginEm = XApiPlugin::getEntityManager(); + + $serializer = Serializer::createSerializer(); + + $getStatementController = new StatementGetController( + new StatementRepository( + $pluginEm->getRepository(StatementEntity::class) + ), + new StatementSerializer($serializer), + new StatementResultSerializer($serializer), + new StatementsFilterFactory( + new ActorSerializer($serializer) + ) + ); + + return $getStatementController->getStatement($this->httpRequest); + } + /** * @return \Symfony\Component\HttpFoundation\Response */ diff --git a/plugin/xapi/src/XApiPlugin.php b/plugin/xapi/src/XApiPlugin.php index 268da068642..0c2cd5ca862 100644 --- a/plugin/xapi/src/XApiPlugin.php +++ b/plugin/xapi/src/XApiPlugin.php @@ -2,6 +2,7 @@ /* For licensing terms, see /license.txt */ +use Chamilo\PluginBundle\Entity\XApi\ActivityProfile; use Chamilo\PluginBundle\Entity\XApi\ActivityState; use Chamilo\PluginBundle\Entity\XApi\Cmi5Item; use Chamilo\PluginBundle\Entity\XApi\LrsAuth; @@ -92,6 +93,7 @@ public function install() 'xapi_lrs_auth', 'xapi_cmi5_item', 'xapi_activity_state', + 'xapi_activity_profile', 'xapi_attachment', 'xapi_object', @@ -156,6 +158,7 @@ public function uninstallPluginDbTables() $schemaTool = new SchemaTool($em); $schemaTool->dropSchema( [ + $em->getClassMetadata(ActivityProfile::class), $em->getClassMetadata(ActivityState::class), $em->getClassMetadata(SharedStatement::class), $em->getClassMetadata(ToolLaunch::class), @@ -499,6 +502,7 @@ private function installPluginDbTables() $em->getClassMetadata(LrsAuth::class), $em->getClassMetadata(Cmi5Item::class), $em->getClassMetadata(ActivityState::class), + $em->getClassMetadata(ActivityProfile::class), ] ); From 012936f5c77fed90a0fb6047bf90d169046ff28b Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 12 Feb 2021 05:41:34 -0500 Subject: [PATCH 05/27] xAPI: Add post method in activities state api - refs BT#16742 --- .../Controller/StatementPostController.php | 39 +++++++++++++++++-- .../src/Lrs/ActivitiesStateController.php | 5 +++ plugin/xapi/src/Lrs/StatementsController.php | 27 +++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php index 5937caa7fb5..fd5ae0e386f 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php @@ -11,22 +11,53 @@ namespace XApi\LrsBundle\Controller; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Xabbuh\XApi\Common\Exception\NotFoundException; use Xabbuh\XApi\Model\Statement; +use XApi\Repository\Api\StatementRepositoryInterface; /** * @author Jérôme Parmentier */ final class StatementPostController { + /** + * @var \XApi\Repository\Api\StatementRepositoryInterface + */ + private $repository; + + public function __construct(StatementRepositoryInterface $repository) + { + $this->repository = $repository; + } + public function postStatement(Request $request, Statement $statement) { } - /** - * @param Statement[] $statements - */ - public function postStatements(Request $request, array $statements) + public function postStatements(Request $request, array $statements): JsonResponse { + $uuids = []; + + /** @var Statement $statement */ + foreach ($statements as $statement) { + try { + $existingStatement = $this->repository->findStatementById($statement->getId()); + + if (!$existingStatement->equals($statement)) { + throw new ConflictHttpException( + 'The new statement is not equal to an existing statement with the same id.' + ); + } + } catch (NotFoundException $e) { + $this->repository->storeStatement($statement, true); + } + + $uuids[] = $statement->getId()->getValue(); + } + + return new JsonResponse($uuids); } } diff --git a/plugin/xapi/src/Lrs/ActivitiesStateController.php b/plugin/xapi/src/Lrs/ActivitiesStateController.php index 7267559751a..790e19efc63 100644 --- a/plugin/xapi/src/Lrs/ActivitiesStateController.php +++ b/plugin/xapi/src/Lrs/ActivitiesStateController.php @@ -67,6 +67,11 @@ public function get(): Response return JsonResponse::create($documentData); } + public function post(): Response + { + return $this->put(); + } + public function put(): Response { $activityId = $this->httpRequest->query->get('activityId'); diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index 24a4a4307d6..601352dd66b 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -4,12 +4,15 @@ namespace Chamilo\PluginBundle\XApi\Lrs; +use Symfony\Component\HttpFoundation\Response; use Xabbuh\XApi\Model\Statement; +use Xabbuh\XApi\Model\StatementResult; use Xabbuh\XApi\Serializer\Symfony\ActorSerializer; use Xabbuh\XApi\Serializer\Symfony\Serializer; use Xabbuh\XApi\Serializer\Symfony\StatementResultSerializer; use Xabbuh\XApi\Serializer\Symfony\StatementSerializer; use XApi\LrsBundle\Controller\StatementGetController; +use XApi\LrsBundle\Controller\StatementPostController; use XApi\LrsBundle\Controller\StatementPutController; use XApi\LrsBundle\Model\StatementsFilterFactory; use XApi\Repository\Doctrine\Mapping\Statement as StatementEntity; @@ -63,6 +66,23 @@ public function put() return $putStatementController->putStatement($this->httpRequest, $statement); } + public function post(): Response + { + $pluginEm = XApiPlugin::getEntityManager(); + + $postStatementController = new StatementPostController( + new StatementRepository( + $pluginEm->getRepository(StatementEntity::class) + ) + ); + + $statements = $this->deserializeStatements( + $this->httpRequest->getContent() + ); + + return $postStatementController->postStatements($this->httpRequest, $statements); + } + /** * @param string $content * @@ -74,4 +94,11 @@ private function deserializeStatement($content) return $serializer->deserialize($content, Statement::class, 'json'); } + + private function deserializeStatements($content): array + { + $serializer = Serializer::createSerializer(); + + return $serializer->deserialize($content, Statement::class.'[]', 'json'); + } } From f1343038d7a7e6deba6f82e9d75f147e1a008fbe Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 17 Feb 2021 15:13:07 -0500 Subject: [PATCH 06/27] xAPI: Fix authorization requirementes - refs BT#16742 --- plugin/xapi/src/Lrs/LrsRequest.php | 49 ++++++++++++++++++------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index 31525be70af..d9e8741c4f0 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -10,6 +10,8 @@ use Symfony\Component\HttpFoundation\Response as HttpResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Xabbuh\XApi\Common\Exception\ConflictException; /** * Class LrsRequest. @@ -33,27 +35,28 @@ public function __construct() public function send() { - $this->validAuth(); - - $version = $this->request->headers->get('X-Experience-API-Version'); - - if (null === $version) { - throw new BadRequestHttpException('The "X-Experience-API-Version" header is required.'); - } - - if (!$this->isValidVersion($version)) { - throw new BadRequestHttpException("The xAPI version \"$version\" is not supported."); - } - $controllerName = $this->getControllerName(); $methodName = $this->getMethodName(); - if ($controllerName - && class_exists($controllerName) + if (class_exists($controllerName) && method_exists($controllerName, $methodName) ) { - /** @var HttpResponse $response */ - $response = call_user_func([new $controllerName(), $methodName]); + try { + $this->validateAuth(); + $this->validateVersion(); + + /** @var HttpResponse $response */ + $response = call_user_func([new $controllerName(), $methodName]); + } catch (AccessDeniedHttpException | \Exception $accessDeniedHttpException) { + $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); + } catch (HttpException $httpException) { + $response = HttpResponse::create( + $httpException->getMessage(), + $httpException->getStatusCode() + ); + } catch (ConflictException $conflictException) { + $response = HttpResponse::create('', HttpResponse::HTTP_CONFLICT); + } } else { $response = HttpResponse::create('Not Found', HttpResponse::HTTP_NOT_FOUND); } @@ -63,7 +66,7 @@ public function send() $response->send(); } - private function validAuth(): bool + private function validateAuth(): bool { if (!$this->request->headers->has('Authorization')) { throw new AccessDeniedHttpException(); @@ -100,17 +103,23 @@ private function validAuth(): bool return true; } - private function isValidVersion(string $version): string + private function validateVersion() { + $version = $this->request->headers->get('X-Experience-API-Version'); + + if (null === $version) { + throw new BadRequestHttpException('The "X-Experience-API-Version" header is required.'); + } + if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) { if ('1.0' === $version) { $this->request->headers->set('X-Experience-API-Version', '1.0.0'); } - return true; + return; } - return false; + throw new BadRequestHttpException("The xAPI version \"$version\" is not supported."); } private function getControllerName(): ?string From ce6f41f965e6deda4e408f758d7661b80c861889 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 17 Feb 2021 17:03:03 -0500 Subject: [PATCH 07/27] xAPI: Add about resource - refs BT#16742 --- plugin/xapi/src/Lrs/AboutController.php | 30 +++++++++++++++++++++++++ plugin/xapi/src/Lrs/LrsRequest.php | 6 +++-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 plugin/xapi/src/Lrs/AboutController.php diff --git a/plugin/xapi/src/Lrs/AboutController.php b/plugin/xapi/src/Lrs/AboutController.php new file mode 100644 index 00000000000..084f2ddae68 --- /dev/null +++ b/plugin/xapi/src/Lrs/AboutController.php @@ -0,0 +1,30 @@ + [ + '1.0.3', + '1.0.2', + '1.0.1', + '1.0.0', + ], + ]; + + return JsonResponse::create($json); + } +} diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index d9e8741c4f0..b8add7737d2 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -42,8 +42,10 @@ public function send() && method_exists($controllerName, $methodName) ) { try { - $this->validateAuth(); - $this->validateVersion(); + if ($controllerName !== AboutController::class) { + $this->validateAuth(); + $this->validateVersion(); + } /** @var HttpResponse $response */ $response = call_user_func([new $controllerName(), $methodName]); From b06023503ef472cefb9688a2a4bccced23599c0b Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 17 Feb 2021 18:27:09 -0500 Subject: [PATCH 08/27] xAPI: Fix POST request for /statements - refs BT#16742 --- plugin/xapi/src/Lrs/StatementsController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index 601352dd66b..b3c3ae7569d 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -76,9 +76,13 @@ public function post(): Response ) ); - $statements = $this->deserializeStatements( - $this->httpRequest->getContent() - ); + $content = $this->httpRequest->getContent(); + + if (substr($content, 0, 1) !== '[') { + $content = "[$content]"; + } + + $statements = $this->deserializeStatements($content); return $postStatementController->postStatements($this->httpRequest, $statements); } From ce20798ec3667d0b6e4202153e54c40d0b92d729 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 19 Feb 2021 02:58:08 -0500 Subject: [PATCH 09/27] Minor - Format code - refs BT#16742 --- plugin/xapi/src/Lrs/ActivitiesProfileController.php | 1 + plugin/xapi/src/Lrs/ActivitiesStateController.php | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/xapi/src/Lrs/ActivitiesProfileController.php b/plugin/xapi/src/Lrs/ActivitiesProfileController.php index 70c71e1c99b..67822caf7e5 100644 --- a/plugin/xapi/src/Lrs/ActivitiesProfileController.php +++ b/plugin/xapi/src/Lrs/ActivitiesProfileController.php @@ -38,6 +38,7 @@ public function get(): Response json_encode($activityProfile->getDocumentData()) ); } + public function put(): Response { $profileId = $this->httpRequest->query->get('profileId'); diff --git a/plugin/xapi/src/Lrs/ActivitiesStateController.php b/plugin/xapi/src/Lrs/ActivitiesStateController.php index 790e19efc63..dcebfe7ab26 100644 --- a/plugin/xapi/src/Lrs/ActivitiesStateController.php +++ b/plugin/xapi/src/Lrs/ActivitiesStateController.php @@ -1,9 +1,8 @@ Date: Fri, 19 Feb 2021 13:35:40 -0500 Subject: [PATCH 10/27] xAPI: Fix post request in Statement resource - refs BT#16742 --- .../Controller/StatementPostController.php | 20 ++++++++++++------- .../src/Resources/config/routing.xml | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php index fd5ae0e386f..fc91be68574 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php @@ -33,16 +33,18 @@ public function __construct(StatementRepositoryInterface $repository) $this->repository = $repository; } - public function postStatement(Request $request, Statement $statement) - { - } - public function postStatements(Request $request, array $statements): JsonResponse { - $uuids = []; + $statementsToStore = []; /** @var Statement $statement */ foreach ($statements as $statement) { + if (null === $statementId = $statement->getId()) { + $statementsToStore[] = $statement; + + continue; + } + try { $existingStatement = $this->repository->findStatementById($statement->getId()); @@ -52,10 +54,14 @@ public function postStatements(Request $request, array $statements): JsonRespons ); } } catch (NotFoundException $e) { - $this->repository->storeStatement($statement, true); + $statementsToStore[] = $statement; } + } + + $uuids = []; - $uuids[] = $statement->getId()->getValue(); + foreach ($statementsToStore as $statement) { + $uuids[] = $this->repository->storeStatement($statement, true)->getValue(); } return new JsonResponse($uuids); diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml index 27c4d2bc101..f0e75024fb3 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/routing.xml @@ -13,7 +13,7 @@ - xapi_lrs.controller.statement.post:postStatement + xapi_lrs.controller.statement.post:postStatements statement true From d355d0aae30ec11e243a17dae771192981b9bee1 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 19 Feb 2021 16:32:11 -0500 Subject: [PATCH 11/27] xAPI: Allow filter statements by verb - refs BT#16742 --- .../src/StatementRepository.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php index 9049a5e3b8c..438fe2308c2 100644 --- a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php @@ -13,6 +13,7 @@ use Doctrine\ORM\EntityRepository; use XApi\Repository\Doctrine\Mapping\Statement; +use XApi\Repository\Doctrine\Mapping\Verb; use XApi\Repository\Doctrine\Repository\Mapping\StatementRepository as BaseStatementRepository; /** @@ -33,7 +34,7 @@ public function findStatement(array $criteria) */ public function findStatements(array $criteria) { - return parent::findBy($criteria); + return $this->getQueryBuilder($criteria)->getQuery()->getResult(); } /** @@ -47,4 +48,17 @@ public function storeStatement(Statement $mappedStatement, $flush = true) $this->_em->flush(); } } + + private function getQueryBuilder(array $criteria): \Doctrine\ORM\QueryBuilder + { + $qb = $this->createQueryBuilder('statement'); + + if (!empty($criteria['verb'])) { + $qb->innerJoin('statement.verb', 'verb'); + $qb->andWhere($qb->expr()->eq('verb.id', ':c_verb')); + $qb->setParameter('c_verb', $criteria['verb']); + } + + return $qb; + } } From 12568f50e216b2281f4d24698d13c15bc4fef716 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 19 Feb 2021 19:14:36 -0500 Subject: [PATCH 12/27] xAPI: Fix GET request for statement resource - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementGetController.php | 5 +++-- plugin/xapi/src/Lrs/LrsRequest.php | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 17c3cc9bf8e..79f29c3f334 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -94,6 +94,7 @@ public function getStatement(Request $request) $now = new \DateTime(); $response->headers->set('X-Experience-API-Consistent-Through', $now->format(\DateTime::ATOM)); + $response->headers->set('Content-Type', 'application/json'); return $response; } @@ -107,7 +108,7 @@ private function buildSingleStatementResponse(Statement $statement, $includeAtta { $json = $this->statementSerializer->serializeStatement($statement); - $response = new JsonResponse($json, 200, [], true); + $response = new Response($json, 200); if ($includeAttachments) { $response = $this->buildMultipartResponse($response, [$statement]); @@ -128,7 +129,7 @@ private function buildMultiStatementsResponse(array $statements, $includeAttachm { $json = $this->statementResultSerializer->serializeStatementResult(new StatementResult($statements)); - $response = new JsonResponse($json, 200, [], true); + $response = new Response($json, 200); if ($includeAttachments) { $response = $this->buildMultipartResponse($response, $statements); diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index b8add7737d2..c63dec7f682 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -10,6 +10,7 @@ use Symfony\Component\HttpFoundation\Response as HttpResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Xabbuh\XApi\Common\Exception\ConflictException; @@ -49,15 +50,17 @@ public function send() /** @var HttpResponse $response */ $response = call_user_func([new $controllerName(), $methodName]); - } catch (AccessDeniedHttpException | \Exception $accessDeniedHttpException) { + } catch (AccessDeniedHttpException $accessDeniedHttpException) { $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); + } catch (ConflictException | ConflictHttpException $conflictException) { + $response = HttpResponse::create('', HttpResponse::HTTP_CONFLICT); } catch (HttpException $httpException) { $response = HttpResponse::create( $httpException->getMessage(), $httpException->getStatusCode() ); - } catch (ConflictException $conflictException) { - $response = HttpResponse::create('', HttpResponse::HTTP_CONFLICT); + } catch (\Exception $exception) { + $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); } } else { $response = HttpResponse::create('Not Found', HttpResponse::HTTP_NOT_FOUND); From 66341196a5ed50f264ef00aef3611050d3fabf92 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Sun, 21 Feb 2021 11:40:08 -0500 Subject: [PATCH 13/27] xAPI: Simplify catch exceptions in requests - refs BT#16742 --- plugin/xapi/src/Lrs/LrsRequest.php | 82 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index c63dec7f682..32262feb4e2 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -8,11 +8,11 @@ use Database; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\HttpFoundation\Response as HttpResponse; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; -use Xabbuh\XApi\Common\Exception\ConflictException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Xabbuh\XApi\Common\Exception\AccessDeniedException; +use Xabbuh\XApi\Common\Exception\XApiException; /** * Class LrsRequest. @@ -39,31 +39,17 @@ public function send() $controllerName = $this->getControllerName(); $methodName = $this->getMethodName(); - if (class_exists($controllerName) - && method_exists($controllerName, $methodName) - ) { - try { - if ($controllerName !== AboutController::class) { - $this->validateAuth(); - $this->validateVersion(); - } - - /** @var HttpResponse $response */ - $response = call_user_func([new $controllerName(), $methodName]); - } catch (AccessDeniedHttpException $accessDeniedHttpException) { - $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); - } catch (ConflictException | ConflictHttpException $conflictException) { - $response = HttpResponse::create('', HttpResponse::HTTP_CONFLICT); - } catch (HttpException $httpException) { - $response = HttpResponse::create( - $httpException->getMessage(), - $httpException->getStatusCode() - ); - } catch (\Exception $exception) { - $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); - } - } else { - $response = HttpResponse::create('Not Found', HttpResponse::HTTP_NOT_FOUND); + try { + $response = $this->generateResponse($controllerName, $methodName); + } catch (XApiException $xApiException) { + $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); + } catch (HttpException $httpException) { + $response = HttpResponse::create( + $httpException->getMessage(), + $httpException->getStatusCode() + ); + } catch (\Exception $exception) { + $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); } $response->headers->set('X-Experience-API-Version', '1.0.3'); @@ -71,10 +57,15 @@ public function send() $response->send(); } + /** + * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException + * + * @return bool + */ private function validateAuth(): bool { if (!$this->request->headers->has('Authorization')) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedException(); } $authHeader = $this->request->headers->get('Authorization'); @@ -82,7 +73,7 @@ private function validateAuth(): bool $parts = explode('Basic ', $authHeader, 2); if (empty($parts[1])) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedException(); } $authDecoded = base64_decode($parts[1]); @@ -90,7 +81,7 @@ private function validateAuth(): bool $parts = explode(':', $authDecoded, 2); if (empty($parts) || count($parts) !== 2) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedException(); } list($username, $password) = $parts; @@ -102,7 +93,7 @@ private function validateAuth(): bool ); if (null == $auth) { - throw new AccessDeniedHttpException(); + throw new AccessDeniedException(); } return true; @@ -149,4 +140,31 @@ private function getMethodName(): string return strtolower($method); } + + /** + * @param string $controllerName + * @param string $methodName + * + * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException + * + * @return \Symfony\Component\HttpFoundation\Response + */ + private function generateResponse(string $controllerName, string $methodName): HttpResponse + { + if (!class_exists($controllerName) + || !method_exists($controllerName, $methodName) + ) { + throw new NotFoundHttpException(); + } + + if ($controllerName !== AboutController::class) { + $this->validateAuth(); + $this->validateVersion(); + } + + /** @var HttpResponse $response */ + $response = call_user_func([new $controllerName(), $methodName]); + + return $response; + } } From ab2fe20db471a7374fd874eb8da6c76cd40b43de Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Sun, 21 Feb 2021 23:28:41 -0500 Subject: [PATCH 14/27] xAPI: Set alternate request syntax - refs BT#16742 --- plugin/xapi/src/Lrs/BaseController.php | 6 ++- plugin/xapi/src/Lrs/LrsRequest.php | 70 +++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/plugin/xapi/src/Lrs/BaseController.php b/plugin/xapi/src/Lrs/BaseController.php index ac85ccdc770..b5166f59ebe 100644 --- a/plugin/xapi/src/Lrs/BaseController.php +++ b/plugin/xapi/src/Lrs/BaseController.php @@ -20,9 +20,11 @@ abstract class BaseController /** * BaseController constructor. + * + * @param \Symfony\Component\HttpFoundation\Request $httpRequest */ - public function __construct() + public function __construct(Request $httpRequest) { - $this->httpRequest = Request::createFromGlobals(); + $this->httpRequest = $httpRequest; } } diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index 32262feb4e2..6a3bace62e4 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -36,10 +36,12 @@ public function __construct() public function send() { - $controllerName = $this->getControllerName(); - $methodName = $this->getMethodName(); - try { + $this->alternateRequestSyntax(); + + $controllerName = $this->getControllerName(); + $methodName = $this->getMethodName(); + $response = $this->generateResponse($controllerName, $methodName); } catch (XApiException $xApiException) { $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); @@ -49,7 +51,7 @@ public function send() $httpException->getStatusCode() ); } catch (\Exception $exception) { - $response = HttpResponse::create('', HttpResponse::HTTP_BAD_REQUEST); + $response = HttpResponse::create($exception->getMessage(), HttpResponse::HTTP_BAD_REQUEST); } $response->headers->set('X-Experience-API-Version', '1.0.3'); @@ -125,7 +127,7 @@ private function getControllerName(): ?string $segments = array_values($segments); if (empty($segments)) { - return null; + throw new BadRequestHttpException('Bad request'); } $segments = array_map('ucfirst', $segments); @@ -163,8 +165,64 @@ private function generateResponse(string $controllerName, string $methodName): H } /** @var HttpResponse $response */ - $response = call_user_func([new $controllerName(), $methodName]); + $response = call_user_func( + [ + new $controllerName($this->request), + $methodName, + ] + ); return $response; } + + private function alternateRequestSyntax() + { + if ('POST' !== $this->request->getMethod()) { + return; + } + + if (null === $method = $this->request->query->get('method')) { + return; + } + + if ($this->request->query->count() > 1) { + throw new BadRequestHttpException('Including other query parameters than "method" is not allowed. You have to send them as POST parameters inside the request body.'); + } + + $this->request->setMethod($method); + $this->request->query->remove('method'); + + if (null !== $content = $this->request->request->get('content')) { + $this->request->request->remove('content'); + + $this->request->initialize( + $this->request->query->all(), + $this->request->request->all(), + $this->request->attributes->all(), + $this->request->cookies->all(), + $this->request->files->all(), + $this->request->server->all(), + $content + ); + } + + $headerNames = [ + 'Authorization', + 'X-Experience-API-Version', + 'Content-Type', + 'Content-Length', + 'If-Match', + 'If-None-Match', + ]; + + foreach ($this->request->request as $key => $value) { + if (in_array($key, $headerNames, true)) { + $this->request->headers->set($key, $value); + } else { + $this->request->query->set($key, $value); + } + + $this->request->request->remove($key); + } + } } From 2f0ef7d8ce173104953efed89e772775b858b961 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Mon, 22 Feb 2021 18:17:48 -0500 Subject: [PATCH 15/27] xAPI: Add "more" property in result for statement resource - refs BT#16742 --- .../src/Controller/StatementGetController.php | 29 +++++++++++++++---- .../src/Model/StatementsFilterFactory.php | 1 + .../src/StatementRepository.php | 6 ++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 79f29c3f334..097835db610 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Xabbuh\XApi\Common\Exception\NotFoundException; +use Xabbuh\XApi\Model\IRL; use Xabbuh\XApi\Model\Statement; use Xabbuh\XApi\Model\StatementId; use Xabbuh\XApi\Model\StatementResult; @@ -47,6 +48,7 @@ final class StatementGetController 'format' => true, 'attachments' => true, 'ascending' => true, + 'cursor' => true, ]; private $repository; @@ -86,7 +88,7 @@ public function getStatement(Request $request) } else { $statements = $this->repository->findStatementsBy($this->statementsFilterFactory->createFromParameterBag($query)); - $response = $this->buildMultiStatementsResponse($statements, $includeAttachments); + $response = $this->buildMultiStatementsResponse($statements, $query, $includeAttachments); } } catch (NotFoundException $e) { $response = $this->buildMultiStatementsResponse([]); @@ -120,14 +122,21 @@ private function buildSingleStatementResponse(Statement $statement, $includeAtta } /** - * @param Statement[] $statements - * @param bool $includeAttachments true to include the attachments in the response, false otherwise + * @param Statement[] $statements + * @param \Symfony\Component\HttpFoundation\ParameterBag $query + * @param bool $includeAttachments true to include the attachments in the response, false otherwise * * @return JsonResponse|MultipartResponse */ - private function buildMultiStatementsResponse(array $statements, $includeAttachments = false) + private function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false) { - $json = $this->statementResultSerializer->serializeStatementResult(new StatementResult($statements)); + $moreUrlPath = null; + + $moreUrlPath = $this->generateMoreIrl($query); + + $json = $this->statementResultSerializer->serializeStatementResult( + new StatementResult($statements, $moreUrlPath) + ); $response = new Response($json, 200); @@ -186,4 +195,14 @@ private function validate(ParameterBag $query) throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".'); } } + + private function generateMoreIrl(ParameterBag $query): IRL + { + $params = $query->all(); + $params['cursor'] = empty($params['cursor']) ? 1 : $params['cursor'] + 1; + + return IRL::fromString( + '/plugin/xapi/lrs.php/statements?'.http_build_query($params) + ); + } } diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php b/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php index 7e1ba67f46c..6040f480f20 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Model/StatementsFilterFactory.php @@ -79,6 +79,7 @@ public function createFromParameterBag(ParameterBag $parameters) $filter->descending(); } + $filter->cursor($parameters->getInt('cursor')); $filter->limit($parameters->getInt('limit')); return $filter; diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php index 438fe2308c2..4dbe4946fc0 100644 --- a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php @@ -59,6 +59,12 @@ private function getQueryBuilder(array $criteria): \Doctrine\ORM\QueryBuilder $qb->setParameter('c_verb', $criteria['verb']); } + $qb->setFirstResult($criteria['cursor']); + + if (isset($criteria['limit'])) { + $qb->setMaxResults($criteria['limit']); + } + return $qb; } } From 5442375d333daca74ca927c0c2e264d4194bdd7d Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 23 Feb 2021 03:15:58 -0500 Subject: [PATCH 16/27] xAPI: Set id to statment in PUT statement resource - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementPutController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php index 4a01645b599..e7471787ad6 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPutController.php @@ -55,6 +55,8 @@ public function putStatement(Request $request, Statement $statement) throw new ConflictHttpException('The new statement is not equal to an existing statement with the same id.'); } } catch (NotFoundException $e) { + $statement = $statement->withId($id); + $this->repository->storeStatement($statement, true); } From daf5c3682f047b5b974a5dfc19e85026a37f2518 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Tue, 23 Feb 2021 11:03:17 -0500 Subject: [PATCH 17/27] xAPI: Don't show "more" when statement result is empty - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementGetController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 097835db610..0638f1d1d64 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -130,9 +130,7 @@ private function buildSingleStatementResponse(Statement $statement, $includeAtta */ private function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false) { - $moreUrlPath = null; - - $moreUrlPath = $this->generateMoreIrl($query); + $moreUrlPath = $statements ? $this->generateMoreIrl($query) : null; $json = $this->statementResultSerializer->serializeStatementResult( new StatementResult($statements, $moreUrlPath) From 7e097cd1cb9f36b3ef3270440caabbc2aa2a80f8 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 24 Feb 2021 17:22:51 -0500 Subject: [PATCH 18/27] xAPI: Use serialize factory for statements - refs BT#16742 --- plugin/xapi/src/Lrs/StatementsController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index b3c3ae7569d..8585ed99b27 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -6,9 +6,9 @@ use Symfony\Component\HttpFoundation\Response; use Xabbuh\XApi\Model\Statement; -use Xabbuh\XApi\Model\StatementResult; use Xabbuh\XApi\Serializer\Symfony\ActorSerializer; use Xabbuh\XApi\Serializer\Symfony\Serializer; +use Xabbuh\XApi\Serializer\Symfony\SerializerFactory; use Xabbuh\XApi\Serializer\Symfony\StatementResultSerializer; use Xabbuh\XApi\Serializer\Symfony\StatementSerializer; use XApi\LrsBundle\Controller\StatementGetController; @@ -92,17 +92,17 @@ public function post(): Response * * @return \Xabbuh\XApi\Model\Statement */ - private function deserializeStatement($content) + private function deserializeStatement(string $content = ''): Statement { - $serializer = Serializer::createSerializer(); + $factory = new SerializerFactory(Serializer::createSerializer()); - return $serializer->deserialize($content, Statement::class, 'json'); + return $factory->createStatementSerializer()->deserializeStatement($content); } - private function deserializeStatements($content): array + private function deserializeStatements(string $content = ''): array { - $serializer = Serializer::createSerializer(); + $factory = new SerializerFactory(Serializer::createSerializer()); - return $serializer->deserialize($content, Statement::class.'[]', 'json'); + return $factory->createStatementSerializer()->deserializeStatements($content); } } From b86fe5f2a31963acc9755fc3c2c0fff7f10afbd6 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 25 Feb 2021 19:24:27 -0500 Subject: [PATCH 19/27] xAPI: Accept HEAD requests in statements resource - refs BT#16742 --- .../src/Controller/StatementGetController.php | 24 +++++++------- .../Controller/StatementHeadController.php | 28 +++++++++++++++++ plugin/xapi/src/Lrs/StatementsController.php | 31 ++++++++++++++++--- 3 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 0638f1d1d64..05bafd41031 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -31,9 +31,9 @@ /** * @author Jérôme Parmentier */ -final class StatementGetController +class StatementGetController { - private static $getParameters = [ + protected static $getParameters = [ 'statementId' => true, 'voidedStatementId' => true, 'agent' => true, @@ -51,10 +51,10 @@ final class StatementGetController 'cursor' => true, ]; - private $repository; - private $statementSerializer; - private $statementResultSerializer; - private $statementsFilterFactory; + protected $repository; + protected $statementSerializer; + protected $statementResultSerializer; + protected $statementsFilterFactory; public function __construct(StatementRepositoryInterface $repository, StatementSerializerInterface $statementSerializer, StatementResultSerializerInterface $statementResultSerializer, StatementsFilterFactory $statementsFilterFactory) { @@ -92,6 +92,8 @@ public function getStatement(Request $request) } } catch (NotFoundException $e) { $response = $this->buildMultiStatementsResponse([]); + } catch (\Exception $exception) { + $response = Response::create('', Response::HTTP_BAD_REQUEST); } $now = new \DateTime(); @@ -106,7 +108,7 @@ public function getStatement(Request $request) * * @return JsonResponse|MultipartResponse */ - private function buildSingleStatementResponse(Statement $statement, $includeAttachments = false) + protected function buildSingleStatementResponse(Statement $statement, $includeAttachments = false) { $json = $this->statementSerializer->serializeStatement($statement); @@ -128,7 +130,7 @@ private function buildSingleStatementResponse(Statement $statement, $includeAtta * * @return JsonResponse|MultipartResponse */ - private function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false) + protected function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false) { $moreUrlPath = $statements ? $this->generateMoreIrl($query) : null; @@ -150,7 +152,7 @@ private function buildMultiStatementsResponse(array $statements, ParameterBag $q * * @return MultipartResponse */ - private function buildMultipartResponse(JsonResponse $statementResponse, array $statements) + protected function buildMultipartResponse(JsonResponse $statementResponse, array $statements) { $attachmentsParts = []; @@ -168,7 +170,7 @@ private function buildMultipartResponse(JsonResponse $statementResponse, array $ * * @throws BadRequestHttpException if the parameters does not comply with the xAPI specification */ - private function validate(ParameterBag $query) + protected function validate(ParameterBag $query) { $hasStatementId = $query->has('statementId'); $hasVoidedStatementId = $query->has('voidedStatementId'); @@ -194,7 +196,7 @@ private function validate(ParameterBag $query) } } - private function generateMoreIrl(ParameterBag $query): IRL + protected function generateMoreIrl(ParameterBag $query): IRL { $params = $query->all(); $params['cursor'] = empty($params['cursor']) ? 1 : $params['cursor'] + 1; diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php new file mode 100644 index 00000000000..d2f42267c0e --- /dev/null +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php @@ -0,0 +1,28 @@ +setContent(''); + } +} diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index 8585ed99b27..b9e9841d962 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -9,9 +9,8 @@ use Xabbuh\XApi\Serializer\Symfony\ActorSerializer; use Xabbuh\XApi\Serializer\Symfony\Serializer; use Xabbuh\XApi\Serializer\Symfony\SerializerFactory; -use Xabbuh\XApi\Serializer\Symfony\StatementResultSerializer; -use Xabbuh\XApi\Serializer\Symfony\StatementSerializer; use XApi\LrsBundle\Controller\StatementGetController; +use XApi\LrsBundle\Controller\StatementHeadController; use XApi\LrsBundle\Controller\StatementPostController; use XApi\LrsBundle\Controller\StatementPutController; use XApi\LrsBundle\Model\StatementsFilterFactory; @@ -26,18 +25,19 @@ */ class StatementsController extends BaseController { - public function get() + public function get(): Response { $pluginEm = XApiPlugin::getEntityManager(); $serializer = Serializer::createSerializer(); + $factory = new SerializerFactory($serializer); $getStatementController = new StatementGetController( new StatementRepository( $pluginEm->getRepository(StatementEntity::class) ), - new StatementSerializer($serializer), - new StatementResultSerializer($serializer), + $factory->createStatementSerializer(), + $factory->createStatementResultSerializer(), new StatementsFilterFactory( new ActorSerializer($serializer) ) @@ -46,6 +46,27 @@ public function get() return $getStatementController->getStatement($this->httpRequest); } + public function head(): Response + { + $pluginEm = XApiPlugin::getEntityManager(); + + $serializer = Serializer::createSerializer(); + $factory = new SerializerFactory($serializer); + + $headStatementController = new StatementHeadController( + new StatementRepository( + $pluginEm->getRepository(StatementEntity::class) + ), + $factory->createStatementSerializer(), + $factory->createStatementResultSerializer(), + new StatementsFilterFactory( + new ActorSerializer($serializer) + ) + ); + + return $headStatementController->getStatement($this->httpRequest); + } + /** * @return \Symfony\Component\HttpFoundation\Response */ From 765ea69f6af0a0f8be734fdaa4e8f29336d67e40 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Fri, 26 Feb 2021 17:15:49 -0500 Subject: [PATCH 20/27] xAPI: Allow verb without display --- plugin/xapi/README.md | 2 +- .../xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/xapi/README.md b/plugin/xapi/README.md index f9ba4890555..c23d0aba296 100644 --- a/plugin/xapi/README.md +++ b/plugin/xapi/README.md @@ -27,7 +27,7 @@ The endpoint for the statements API is "https://CHAMILO_DOMAIN/plugin/xapi/lrs.p CREATE TABLE xapi_attachment (identifier INT AUTO_INCREMENT NOT NULL, statement_id VARCHAR(255) DEFAULT NULL, usageType VARCHAR(255) NOT NULL, contentType VARCHAR(255) NOT NULL, length INT NOT NULL, sha2 VARCHAR(255) NOT NULL, display LONGTEXT NOT NULL COMMENT '(DC2Type:json)', hasDescription TINYINT(1) NOT NULL, description LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', fileUrl VARCHAR(255) DEFAULT NULL, content LONGTEXT DEFAULT NULL, INDEX IDX_7148C9A1849CB65B (statement_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; CREATE TABLE xapi_object (identifier INT AUTO_INCREMENT NOT NULL, group_id INT DEFAULT NULL, actor_id INT DEFAULT NULL, verb_id INT DEFAULT NULL, object_id INT DEFAULT NULL, type VARCHAR(255) DEFAULT NULL, activityId VARCHAR(255) DEFAULT NULL, hasActivityDefinition TINYINT(1) DEFAULT NULL, hasActivityName TINYINT(1) DEFAULT NULL, activityName LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', hasActivityDescription TINYINT(1) DEFAULT NULL, activityDescription LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json)', activityType VARCHAR(255) DEFAULT NULL, activityMoreInfo VARCHAR(255) DEFAULT NULL, mbox VARCHAR(255) DEFAULT NULL, mboxSha1Sum VARCHAR(255) DEFAULT NULL, openId VARCHAR(255) DEFAULT NULL, accountName VARCHAR(255) DEFAULT NULL, accountHomePage VARCHAR(255) DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, referenced_statement_id VARCHAR(255) DEFAULT NULL, activityExtensions_id INT DEFAULT NULL, parentContext_id INT DEFAULT NULL, groupingContext_id INT DEFAULT NULL, categoryContext_id INT DEFAULT NULL, otherContext_id INT DEFAULT NULL, UNIQUE INDEX UNIQ_E2B68640303C7F1D (activityExtensions_id), INDEX IDX_E2B68640FE54D947 (group_id), UNIQUE INDEX UNIQ_E2B6864010DAF24A (actor_id), UNIQUE INDEX UNIQ_E2B68640C1D03483 (verb_id), UNIQUE INDEX UNIQ_E2B68640232D562B (object_id), INDEX IDX_E2B68640988A4CEC (parentContext_id), INDEX IDX_E2B686404F542860 (groupingContext_id), INDEX IDX_E2B68640AEA1B132 (categoryContext_id), INDEX IDX_E2B68640B73EEAB7 (otherContext_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; CREATE TABLE xapi_result (identifier INT AUTO_INCREMENT NOT NULL, extensions_id INT DEFAULT NULL, hasScore TINYINT(1) NOT NULL, scaled DOUBLE PRECISION DEFAULT NULL, raw DOUBLE PRECISION DEFAULT NULL, min DOUBLE PRECISION DEFAULT NULL, max DOUBLE PRECISION DEFAULT NULL, success TINYINT(1) DEFAULT NULL, completion TINYINT(1) DEFAULT NULL, response VARCHAR(255) DEFAULT NULL, duration VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_5971ECBFD0A19400 (extensions_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; -CREATE TABLE xapi_verb (identifier INT AUTO_INCREMENT NOT NULL, id VARCHAR(255) NOT NULL, display LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; +CREATE TABLE xapi_verb (identifier INT AUTO_INCREMENT NOT NULL, id VARCHAR(255) NOT NULL, display LONGTEXT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; CREATE TABLE xapi_extensions (identifier INT AUTO_INCREMENT NOT NULL, extensions LONGTEXT NOT NULL COMMENT '(DC2Type:json)', PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; CREATE TABLE xapi_context (identifier INT AUTO_INCREMENT NOT NULL, instructor_id INT DEFAULT NULL, team_id INT DEFAULT NULL, extensions_id INT DEFAULT NULL, registration VARCHAR(255) DEFAULT NULL, hasContextActivities TINYINT(1) DEFAULT NULL, revision VARCHAR(255) DEFAULT NULL, platform VARCHAR(255) DEFAULT NULL, language VARCHAR(255) DEFAULT NULL, statement VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_3D7771908C4FC193 (instructor_id), UNIQUE INDEX UNIQ_3D777190296CD8AE (team_id), UNIQUE INDEX UNIQ_3D777190D0A19400 (extensions_id), PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; CREATE TABLE xapi_actor (identifier INT AUTO_INCREMENT NOT NULL, type VARCHAR(255) DEFAULT NULL, mbox VARCHAR(255) DEFAULT NULL, mboxSha1Sum VARCHAR(255) DEFAULT NULL, openId VARCHAR(255) DEFAULT NULL, accountName VARCHAR(255) DEFAULT NULL, accountHomePage VARCHAR(255) DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, members VARCHAR(255) NOT NULL, PRIMARY KEY(identifier)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB; diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml index 2e2277c6d7e..e65a41c8bf7 100644 --- a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/Verb.orm.xml @@ -9,6 +9,6 @@ - + From 01f354a959c8ff3d4a74912e99ec98c3ec48e677 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Mon, 1 Mar 2021 00:53:03 -0500 Subject: [PATCH 21/27] xAPI: Fi head request in statements resource - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementHeadController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php index d2f42267c0e..5a45b0be186 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php @@ -23,6 +23,6 @@ class StatementHeadController extends StatementGetController */ public function getStatement(Request $request) { - parent::getStatement($request)->setContent(''); + return parent::getStatement($request)->setContent(''); } } From 9315b6d2637836bc5e25025f43640735de2b2bf1 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Mon, 1 Mar 2021 02:49:39 -0500 Subject: [PATCH 22/27] xAPI: HEAD request to activities profile and state - refs BT#16742 --- plugin/xapi/src/Lrs/ActivitiesProfileController.php | 5 +++++ plugin/xapi/src/Lrs/ActivitiesStateController.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/plugin/xapi/src/Lrs/ActivitiesProfileController.php b/plugin/xapi/src/Lrs/ActivitiesProfileController.php index 67822caf7e5..bf12e82f0ff 100644 --- a/plugin/xapi/src/Lrs/ActivitiesProfileController.php +++ b/plugin/xapi/src/Lrs/ActivitiesProfileController.php @@ -39,6 +39,11 @@ public function get(): Response ); } + public function head(): Response + { + return $this->get()->setContent(''); + } + public function put(): Response { $profileId = $this->httpRequest->query->get('profileId'); diff --git a/plugin/xapi/src/Lrs/ActivitiesStateController.php b/plugin/xapi/src/Lrs/ActivitiesStateController.php index dcebfe7ab26..6d05fc7b865 100644 --- a/plugin/xapi/src/Lrs/ActivitiesStateController.php +++ b/plugin/xapi/src/Lrs/ActivitiesStateController.php @@ -66,6 +66,11 @@ public function get(): Response return JsonResponse::create($documentData); } + public function head(): Response + { + return $this->get()->setContent(''); + } + public function post(): Response { return $this->put(); From 9e66ff5e8604b8aee0cc347bb83fd9d7bbef3c0a Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 31 Mar 2021 16:01:09 -0500 Subject: [PATCH 23/27] xAPI: Fix response when statement is not found - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementGetController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 05bafd41031..605240c17d1 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -91,7 +91,7 @@ public function getStatement(Request $request) $response = $this->buildMultiStatementsResponse($statements, $query, $includeAttachments); } } catch (NotFoundException $e) { - $response = $this->buildMultiStatementsResponse([]); + $response = $this->buildMultiStatementsResponse([], $query); } catch (\Exception $exception) { $response = Response::create('', Response::HTTP_BAD_REQUEST); } From 82a71bf3a1e52006ec7ca96d669a08212714e82d Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 31 Mar 2021 21:18:21 -0500 Subject: [PATCH 24/27] xAPI: Fix members relationship with statement object for groups - refs BT#16742 --- .../metadata/StatementObject.orm.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml index 22464ce3faa..8253f07e2b5 100644 --- a/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/metadata/StatementObject.orm.xml @@ -61,7 +61,11 @@ - + + + + + From d434a4ac10963a07c2427aad1cb7d77f8f6ad027 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Mon, 5 Apr 2021 10:48:27 -0500 Subject: [PATCH 25/27] xAPI: Return 204 when statement is not found - refs BT#16742 --- .../lrs-bundle/src/Controller/StatementGetController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 605240c17d1..36eedbbabc2 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -91,7 +91,9 @@ public function getStatement(Request $request) $response = $this->buildMultiStatementsResponse($statements, $query, $includeAttachments); } } catch (NotFoundException $e) { - $response = $this->buildMultiStatementsResponse([], $query); + $response = $this->buildMultiStatementsResponse([], $query) + ->setStatusCode(Response::HTTP_NOT_FOUND) + ->setContent(''); } catch (\Exception $exception) { $response = Response::create('', Response::HTTP_BAD_REQUEST); } From 5bcab08e3037100d1b806a05459e8065660b4e5c Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Wed, 11 Aug 2021 23:13:51 -0500 Subject: [PATCH 26/27] Minor - Flint fixes --- .../src/Controller/StatementGetController.php | 5 ++- .../Controller/StatementHeadController.php | 1 - .../Controller/StatementPostController.php | 4 +-- .../src/StatementRepository.php | 1 - plugin/xapi/src/Entity/ActivityProfile.php | 32 ------------------- plugin/xapi/src/Lrs/BaseController.php | 2 -- plugin/xapi/src/Lrs/LrsRequest.php | 7 ---- plugin/xapi/src/Lrs/StatementsController.php | 5 --- 8 files changed, 3 insertions(+), 54 deletions(-) diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php index 36eedbbabc2..4bb99561dec 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementGetController.php @@ -126,9 +126,8 @@ protected function buildSingleStatementResponse(Statement $statement, $includeAt } /** - * @param Statement[] $statements - * @param \Symfony\Component\HttpFoundation\ParameterBag $query - * @param bool $includeAttachments true to include the attachments in the response, false otherwise + * @param Statement[] $statements + * @param bool $includeAttachments true to include the attachments in the response, false otherwise * * @return JsonResponse|MultipartResponse */ diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php index 5a45b0be186..224a441850a 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementHeadController.php @@ -15,7 +15,6 @@ class StatementHeadController extends StatementGetController { - /** * @throws BadRequestHttpException if the query parameters does not comply with xAPI specification * diff --git a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php index fc91be68574..44e8d739b55 100644 --- a/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php +++ b/plugin/xapi/php-xapi/lrs-bundle/src/Controller/StatementPostController.php @@ -49,9 +49,7 @@ public function postStatements(Request $request, array $statements): JsonRespons $existingStatement = $this->repository->findStatementById($statement->getId()); if (!$existingStatement->equals($statement)) { - throw new ConflictHttpException( - 'The new statement is not equal to an existing statement with the same id.' - ); + throw new ConflictHttpException('The new statement is not equal to an existing statement with the same id.'); } } catch (NotFoundException $e) { $statementsToStore[] = $statement; diff --git a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php index 4dbe4946fc0..47397ddb21d 100644 --- a/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php +++ b/plugin/xapi/php-xapi/repository-doctrine-orm/src/StatementRepository.php @@ -13,7 +13,6 @@ use Doctrine\ORM\EntityRepository; use XApi\Repository\Doctrine\Mapping\Statement; -use XApi\Repository\Doctrine\Mapping\Verb; use XApi\Repository\Doctrine\Repository\Mapping\StatementRepository as BaseStatementRepository; /** diff --git a/plugin/xapi/src/Entity/ActivityProfile.php b/plugin/xapi/src/Entity/ActivityProfile.php index e1113ef3ef9..82f07523cc9 100644 --- a/plugin/xapi/src/Entity/ActivityProfile.php +++ b/plugin/xapi/src/Entity/ActivityProfile.php @@ -43,19 +43,11 @@ class ActivityProfile */ private $documentData; - /** - * @return int - */ public function getId(): int { return $this->id; } - /** - * @param int $id - * - * @return ActivityProfile - */ public function setId(int $id): ActivityProfile { $this->id = $id; @@ -63,19 +55,11 @@ public function setId(int $id): ActivityProfile return $this; } - /** - * @return string - */ public function getProfileId(): string { return $this->profileId; } - /** - * @param string $profileId - * - * @return ActivityProfile - */ public function setProfileId(string $profileId): ActivityProfile { $this->profileId = $profileId; @@ -83,19 +67,11 @@ public function setProfileId(string $profileId): ActivityProfile return $this; } - /** - * @return string - */ public function getActivityId(): string { return $this->activityId; } - /** - * @param string $activityId - * - * @return ActivityProfile - */ public function setActivityId(string $activityId): ActivityProfile { $this->activityId = $activityId; @@ -103,19 +79,11 @@ public function setActivityId(string $activityId): ActivityProfile return $this; } - /** - * @return array - */ public function getDocumentData(): array { return $this->documentData; } - /** - * @param array $documentData - * - * @return ActivityProfile - */ public function setDocumentData(array $documentData): ActivityProfile { $this->documentData = $documentData; diff --git a/plugin/xapi/src/Lrs/BaseController.php b/plugin/xapi/src/Lrs/BaseController.php index b5166f59ebe..865b2b71b68 100644 --- a/plugin/xapi/src/Lrs/BaseController.php +++ b/plugin/xapi/src/Lrs/BaseController.php @@ -20,8 +20,6 @@ abstract class BaseController /** * BaseController constructor. - * - * @param \Symfony\Component\HttpFoundation\Request $httpRequest */ public function __construct(Request $httpRequest) { diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index 6a3bace62e4..f409f61943b 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -61,8 +61,6 @@ public function send() /** * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException - * - * @return bool */ private function validateAuth(): bool { @@ -144,12 +142,7 @@ private function getMethodName(): string } /** - * @param string $controllerName - * @param string $methodName - * * @throws \Xabbuh\XApi\Common\Exception\AccessDeniedException - * - * @return \Symfony\Component\HttpFoundation\Response */ private function generateResponse(string $controllerName, string $methodName): HttpResponse { diff --git a/plugin/xapi/src/Lrs/StatementsController.php b/plugin/xapi/src/Lrs/StatementsController.php index b9e9841d962..517a51a545d 100644 --- a/plugin/xapi/src/Lrs/StatementsController.php +++ b/plugin/xapi/src/Lrs/StatementsController.php @@ -108,11 +108,6 @@ public function post(): Response return $postStatementController->postStatements($this->httpRequest, $statements); } - /** - * @param string $content - * - * @return \Xabbuh\XApi\Model\Statement - */ private function deserializeStatement(string $content = ''): Statement { $factory = new SerializerFactory(Serializer::createSerializer()); From 08003272cad00ede282126cdb3e2ff079703b04d Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos Date: Thu, 12 Aug 2021 00:46:51 -0500 Subject: [PATCH 27/27] Vendor: Update XAPI dependencies - refs BT#16742 --- composer.json | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d61d48fc3dc..d1cef6b791d 100755 --- a/composer.json +++ b/composer.json @@ -86,10 +86,11 @@ "patchwork/utf8": "~1.2", "php-ffmpeg/php-ffmpeg": "0.5.1", "php-http/guzzle6-adapter": "^2.0", - "php-xapi/client": "0.7.x-dev", + "php-xapi/client": "dev-master", + "php-xapi/model": "dev-master as 1.2", "php-xapi/repository-api": "dev-master as 0.3.1", "php-xapi/repository-doctrine": "dev-master", - "php-xapi/symfony-serializer": "2.1.0 as 2.0", + "php-xapi/symfony-serializer": "dev-master", "phpmailer/phpmailer": "~6.1", "phpoffice/phpexcel": "~1.8", "phpoffice/phpword": "~0.14", @@ -167,6 +168,21 @@ "type": "github", "url": "https://github.com/AngelFQC/vimeo.php.git", "no-api": true + }, + { + "type": "github", + "url": "https://github.com/AngelFQC/xapi-symfony-serializer.git", + "no-api": true + }, + { + "type": "github", + "url": "https://github.com/AngelFQC/xapi-repository-doctrine.git", + "no-api": true + }, + { + "type": "github", + "url": "https://github.com/AngelFQC/xapi-model.git", + "no-api": true } ], "config": {