upgrade
This commit is contained in:
19
plugin/xapi/php-xapi/lrs-bundle/LICENSE
Normal file
19
plugin/xapi/php-xapi/lrs-bundle/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-2017 Christian Flothmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
0
plugin/xapi/php-xapi/lrs-bundle/README.md
Normal file
0
plugin/xapi/php-xapi/lrs-bundle/README.md
Normal file
51
plugin/xapi/php-xapi/lrs-bundle/composer.json
Normal file
51
plugin/xapi/php-xapi/lrs-bundle/composer.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "php-xapi/lrs-bundle",
|
||||
"type": "symfony-bundle",
|
||||
"description": "Experience API (xAPI) Learning Record Store (LRS) based on the Symfony Framework",
|
||||
"keywords": ["xAPI", "Experience API", "Tin Can API", "LRS", "Learning Record Store", "Symfony", "bundle"],
|
||||
"homepage": "https://github.com/php-xapi/lrs-bundle",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Flothmann",
|
||||
"homepage": "https://github.com/xabbuh"
|
||||
},
|
||||
{
|
||||
"name": "Jérôme Parmentier",
|
||||
"homepage": "https://github.com/Lctrs"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"php-xapi/exception": "^0.1 || ^0.2",
|
||||
"php-xapi/model": "^1.1 || ^2.0 || ^3.0",
|
||||
"php-xapi/repository-api": "^0.3@dev || ^0.4@dev",
|
||||
"php-xapi/serializer": "^1.0 || ^2.0",
|
||||
"php-xapi/symfony-serializer": "^1.0 || ^2.0",
|
||||
"symfony/config": "^3.4 || ^4.3",
|
||||
"symfony/dependency-injection": "^3.4 || ^4.3",
|
||||
"symfony/http-foundation": "^3.4 || ^4.3",
|
||||
"symfony/http-kernel": "^3.4 || ^4.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "~2.3",
|
||||
"php-xapi/json-test-fixtures": "^1.0 || ^2.0",
|
||||
"php-xapi/test-fixtures": "^1.0.1",
|
||||
"ramsey/uuid": "^2.9 || ^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"XApi\\LrsBundle\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"spec\\XApi\\LrsBundle\\": "spec/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
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;
|
||||
use Xabbuh\XApi\Serializer\StatementResultSerializerInterface;
|
||||
use Xabbuh\XApi\Serializer\StatementSerializerInterface;
|
||||
use XApi\LrsBundle\Model\StatementsFilterFactory;
|
||||
use XApi\LrsBundle\Response\AttachmentResponse;
|
||||
use XApi\LrsBundle\Response\MultipartResponse;
|
||||
use XApi\Repository\Api\StatementRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class StatementGetController
|
||||
{
|
||||
protected static $getParameters = [
|
||||
'statementId' => true,
|
||||
'voidedStatementId' => true,
|
||||
'agent' => true,
|
||||
'verb' => true,
|
||||
'activity' => true,
|
||||
'registration' => true,
|
||||
'related_activities' => true,
|
||||
'related_agents' => true,
|
||||
'since' => true,
|
||||
'until' => true,
|
||||
'limit' => true,
|
||||
'format' => true,
|
||||
'attachments' => true,
|
||||
'ascending' => true,
|
||||
'cursor' => true,
|
||||
];
|
||||
|
||||
protected $repository;
|
||||
protected $statementSerializer;
|
||||
protected $statementResultSerializer;
|
||||
protected $statementsFilterFactory;
|
||||
|
||||
public function __construct(StatementRepositoryInterface $repository, StatementSerializerInterface $statementSerializer, StatementResultSerializerInterface $statementResultSerializer, StatementsFilterFactory $statementsFilterFactory)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->statementSerializer = $statementSerializer;
|
||||
$this->statementResultSerializer = $statementResultSerializer;
|
||||
$this->statementsFilterFactory = $statementsFilterFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequestHttpException if the query parameters does not comply with xAPI specification
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getStatement(Request $request)
|
||||
{
|
||||
$query = new ParameterBag(\array_intersect_key($request->query->all(), self::$getParameters));
|
||||
|
||||
$this->validate($query);
|
||||
|
||||
$includeAttachments = $query->filter('attachments', false, FILTER_VALIDATE_BOOLEAN);
|
||||
try {
|
||||
if (($statementId = $query->get('statementId')) !== null) {
|
||||
$statement = $this->repository->findStatementById(StatementId::fromString($statementId));
|
||||
|
||||
$response = $this->buildSingleStatementResponse($statement, $includeAttachments);
|
||||
} elseif (($voidedStatementId = $query->get('voidedStatementId')) !== null) {
|
||||
$statement = $this->repository->findVoidedStatementById(StatementId::fromString($voidedStatementId));
|
||||
|
||||
$response = $this->buildSingleStatementResponse($statement, $includeAttachments);
|
||||
} else {
|
||||
$statements = $this->repository->findStatementsBy($this->statementsFilterFactory->createFromParameterBag($query));
|
||||
|
||||
$response = $this->buildMultiStatementsResponse($statements, $query, $includeAttachments);
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
$response = $this->buildMultiStatementsResponse([], $query)
|
||||
->setStatusCode(Response::HTTP_NOT_FOUND)
|
||||
->setContent('');
|
||||
} catch (\Exception $exception) {
|
||||
$response = Response::create('', Response::HTTP_BAD_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeAttachments true to include the attachments in the response, false otherwise
|
||||
*
|
||||
* @return JsonResponse|MultipartResponse
|
||||
*/
|
||||
protected function buildSingleStatementResponse(Statement $statement, $includeAttachments = false)
|
||||
{
|
||||
$json = $this->statementSerializer->serializeStatement($statement);
|
||||
|
||||
$response = new Response($json, 200);
|
||||
|
||||
if ($includeAttachments) {
|
||||
$response = $this->buildMultipartResponse($response, [$statement]);
|
||||
}
|
||||
|
||||
$response->setLastModified($statement->getStored());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Statement[] $statements
|
||||
* @param bool $includeAttachments true to include the attachments in the response, false otherwise
|
||||
*
|
||||
* @return JsonResponse|MultipartResponse
|
||||
*/
|
||||
protected function buildMultiStatementsResponse(array $statements, ParameterBag $query, $includeAttachments = false)
|
||||
{
|
||||
$moreUrlPath = $statements ? $this->generateMoreIrl($query) : null;
|
||||
|
||||
$json = $this->statementResultSerializer->serializeStatementResult(
|
||||
new StatementResult($statements, $moreUrlPath)
|
||||
);
|
||||
|
||||
$response = new Response($json, 200);
|
||||
|
||||
if ($includeAttachments) {
|
||||
$response = $this->buildMultipartResponse($response, $statements);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Statement[] $statements
|
||||
*
|
||||
* @return MultipartResponse
|
||||
*/
|
||||
protected function buildMultipartResponse(JsonResponse $statementResponse, array $statements)
|
||||
{
|
||||
$attachmentsParts = [];
|
||||
|
||||
foreach ($statements as $statement) {
|
||||
foreach ((array) $statement->getAttachments() as $attachment) {
|
||||
$attachmentsParts[] = new AttachmentResponse($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
return new MultipartResponse($statementResponse, $attachmentsParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the parameters.
|
||||
*
|
||||
* @throws BadRequestHttpException if the parameters does not comply with the xAPI specification
|
||||
*/
|
||||
protected function validate(ParameterBag $query)
|
||||
{
|
||||
$hasStatementId = $query->has('statementId');
|
||||
$hasVoidedStatementId = $query->has('voidedStatementId');
|
||||
|
||||
if ($hasStatementId && $hasVoidedStatementId) {
|
||||
throw new BadRequestHttpException('Request must not have both statementId and voidedStatementId parameters at the same time.');
|
||||
}
|
||||
|
||||
$hasAttachments = $query->has('attachments');
|
||||
$hasFormat = $query->has('format');
|
||||
$queryCount = $query->count();
|
||||
|
||||
if (($hasStatementId || $hasVoidedStatementId) && $hasAttachments && $hasFormat && $queryCount > 3) {
|
||||
throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".');
|
||||
}
|
||||
|
||||
if (($hasStatementId || $hasVoidedStatementId) && ($hasAttachments || $hasFormat) && $queryCount > 2) {
|
||||
throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".');
|
||||
}
|
||||
|
||||
if (($hasStatementId || $hasVoidedStatementId) && $queryCount > 1) {
|
||||
throw new BadRequestHttpException('Request must not contain statementId or voidedStatementId parameters, and also any other parameter besides "attachments" or "format".');
|
||||
}
|
||||
}
|
||||
|
||||
protected 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class StatementHeadController extends StatementGetController
|
||||
{
|
||||
/**
|
||||
* @throws BadRequestHttpException if the query parameters does not comply with xAPI specification
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getStatement(Request $request)
|
||||
{
|
||||
return parent::getStatement($request)->setContent('');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
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 <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
final class StatementPostController
|
||||
{
|
||||
/**
|
||||
* @var StatementRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
public function __construct(StatementRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function postStatements(Request $request, array $statements): JsonResponse
|
||||
{
|
||||
$statementsToStore = [];
|
||||
|
||||
/** @var Statement $statement */
|
||||
foreach ($statements as $statement) {
|
||||
if (null === $statementId = $statement->getId()) {
|
||||
$statementsToStore[] = $statement;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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) {
|
||||
$statementsToStore[] = $statement;
|
||||
}
|
||||
}
|
||||
|
||||
$uuids = [];
|
||||
|
||||
foreach ($statementsToStore as $statement) {
|
||||
$uuids[] = $this->repository->storeStatement($statement, true)->getValue();
|
||||
}
|
||||
|
||||
return new JsonResponse($uuids);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
||||
use Xabbuh\XApi\Common\Exception\NotFoundException;
|
||||
use Xabbuh\XApi\Model\Statement;
|
||||
use Xabbuh\XApi\Model\StatementId;
|
||||
use XApi\Repository\Api\StatementRepositoryInterface;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
final class StatementPutController
|
||||
{
|
||||
private $repository;
|
||||
|
||||
public function __construct(StatementRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function putStatement(Request $request, Statement $statement): Response
|
||||
{
|
||||
if (null === $statementId = $request->query->get('statementId')) {
|
||||
throw new BadRequestHttpException('Required statementId parameter is missing.');
|
||||
}
|
||||
|
||||
try {
|
||||
$id = StatementId::fromString($statementId);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new BadRequestHttpException(sprintf('Parameter statementId ("%s") is not a valid UUID.', $statementId), $e);
|
||||
}
|
||||
|
||||
if (null !== $statement->getId() && !$id->equals($statement->getId())) {
|
||||
throw new ConflictHttpException(sprintf('Id parameter ("%s") and statement id ("%s") do not match.', $id->getValue(), $statement->getId()->getValue()));
|
||||
}
|
||||
|
||||
try {
|
||||
$existingStatement = $this->repository->findStatementById($id);
|
||||
|
||||
if (!$existingStatement->equals($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);
|
||||
}
|
||||
|
||||
return new Response('', 204);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace XApi\LrsBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
final class Configuration implements ConfigurationInterface
|
||||
{
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder();
|
||||
|
||||
$treeBuilder
|
||||
->root('xapi_lrs')
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) { return isset($v['type']) && in_array($v['type'], ['mongodb', 'orm']) && !isset($v['object_manager_service']); })
|
||||
->thenInvalid('You need to configure the object manager service when the repository type is "mongodb" or orm".')
|
||||
->end()
|
||||
->children()
|
||||
->enumNode('type')
|
||||
->isRequired()
|
||||
->values(['in_memory', 'mongodb', 'orm'])
|
||||
->end()
|
||||
->scalarNode('object_manager_service')->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
final class XApiLrsExtension extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
|
||||
$loader->load('controller.xml');
|
||||
$loader->load('event_listener.xml');
|
||||
$loader->load('factory.xml');
|
||||
$loader->load('serializer.xml');
|
||||
|
||||
switch ($config['type']) {
|
||||
case 'in_memory':
|
||||
break;
|
||||
case 'mongodb':
|
||||
$loader->load('doctrine.xml');
|
||||
$loader->load('mongodb.xml');
|
||||
|
||||
$container->setAlias('xapi_lrs.doctrine.object_manager', $config['object_manager_service']);
|
||||
$container->setAlias('xapi_lrs.repository.statement', 'xapi_lrs.repository.statement.doctrine');
|
||||
break;
|
||||
case 'orm':
|
||||
$loader->load('doctrine.xml');
|
||||
$loader->load('orm.xml');
|
||||
|
||||
$container->setAlias('xapi_lrs.doctrine.object_manager', $config['object_manager_service']);
|
||||
$container->setAlias('xapi_lrs.repository.statement', 'xapi_lrs.repository.statement.doctrine');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function getAlias()
|
||||
{
|
||||
return 'xapi_lrs';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class AlternateRequestSyntaxListener
|
||||
{
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$request->attributes->has('xapi_lrs.route')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('POST' !== $request->getMethod()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $method = $request->query->get('method')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($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.');
|
||||
}
|
||||
|
||||
$request->setMethod($method);
|
||||
$request->query->remove('method');
|
||||
|
||||
if (null !== $content = $request->request->get('content')) {
|
||||
$request->request->remove('content');
|
||||
|
||||
$request->initialize(
|
||||
$request->query->all(),
|
||||
$request->request->all(),
|
||||
$request->attributes->all(),
|
||||
$request->cookies->all(),
|
||||
$request->files->all(),
|
||||
$request->server->all(),
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($request->request as $key => $value) {
|
||||
if (in_array($key, ['Authorization', 'X-Experience-API-Version', 'Content-Type', 'Content-Length', 'If-Match', 'If-None-Match'], true)) {
|
||||
$request->headers->set($key, $value);
|
||||
} else {
|
||||
$request->query->set($key, $value);
|
||||
}
|
||||
|
||||
$request->request->remove($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace XApi\LrsBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
|
||||
/**
|
||||
* Converts Experience API specific domain exceptions into proper HTTP responses.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class ExceptionListener
|
||||
{
|
||||
public function onKernelException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace XApi\LrsBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface as BaseSerializerException;
|
||||
use Xabbuh\XApi\Serializer\StatementSerializerInterface;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class SerializerListener
|
||||
{
|
||||
private $statementSerializer;
|
||||
|
||||
public function __construct(StatementSerializerInterface $statementSerializer)
|
||||
{
|
||||
$this->statementSerializer = $statementSerializer;
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$request->attributes->has('xapi_lrs.route')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($request->attributes->get('xapi_serializer')) {
|
||||
case 'statement':
|
||||
$request->attributes->set('statement', $this->statementSerializer->deserializeStatement($request->getContent()));
|
||||
break;
|
||||
}
|
||||
} catch (BaseSerializerException $e) {
|
||||
throw new BadRequestHttpException(sprintf('The content of the request cannot be deserialized into a valid xAPI %s.', $request->attributes->get('xapi_serializer')), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class VersionListener
|
||||
{
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$request->attributes->has('xapi_lrs.route')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $version = $request->headers->get('X-Experience-API-Version')) {
|
||||
throw new BadRequestHttpException('Missing required "X-Experience-API-Version" header.');
|
||||
}
|
||||
|
||||
if (preg_match('/^1\.0(?:\.\d+)?$/', $version)) {
|
||||
if ('1.0' === $version) {
|
||||
$request->headers->set('X-Experience-API-Version', '1.0.0');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new BadRequestHttpException(sprintf('xAPI version "%s" is not supported.', $version));
|
||||
}
|
||||
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (!$event->isMasterRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$event->getRequest()->attributes->has('xapi_lrs.route')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$headers = $event->getResponse()->headers;
|
||||
|
||||
if (!$headers->has('X-Experience-API-Version')) {
|
||||
$headers->set('X-Experience-API-Version', '1.0.3');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Model;
|
||||
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Xabbuh\XApi\Model\Activity;
|
||||
use Xabbuh\XApi\Model\IRI;
|
||||
use Xabbuh\XApi\Model\StatementsFilter;
|
||||
use Xabbuh\XApi\Model\Verb;
|
||||
use Xabbuh\XApi\Serializer\ActorSerializerInterface;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class StatementsFilterFactory
|
||||
{
|
||||
private $actorSerializer;
|
||||
|
||||
public function __construct(ActorSerializerInterface $actorSerializer)
|
||||
{
|
||||
$this->actorSerializer = $actorSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StatementsFilter
|
||||
*/
|
||||
public function createFromParameterBag(ParameterBag $parameters)
|
||||
{
|
||||
$filter = new StatementsFilter();
|
||||
|
||||
if (($actor = $parameters->get('agent')) !== null) {
|
||||
$filter->byActor($this->actorSerializer->deserializeActor($actor));
|
||||
}
|
||||
|
||||
if (($verbId = $parameters->get('verb')) !== null) {
|
||||
$filter->byVerb(new Verb(IRI::fromString($verbId)));
|
||||
}
|
||||
|
||||
if (($activityId = $parameters->get('activity')) !== null) {
|
||||
$filter->byActivity(new Activity(IRI::fromString($activityId)));
|
||||
}
|
||||
|
||||
if (($registration = $parameters->get('registration')) !== null) {
|
||||
$filter->byRegistration($registration);
|
||||
}
|
||||
|
||||
if ($parameters->filter('related_activities', false, FILTER_VALIDATE_BOOLEAN)) {
|
||||
$filter->enableRelatedActivityFilter();
|
||||
} else {
|
||||
$filter->disableRelatedActivityFilter();
|
||||
}
|
||||
|
||||
if ($parameters->filter('related_agents', false, FILTER_VALIDATE_BOOLEAN)) {
|
||||
$filter->enableRelatedAgentFilter();
|
||||
} else {
|
||||
$filter->disableRelatedAgentFilter();
|
||||
}
|
||||
|
||||
if (($since = $parameters->get('since')) !== null) {
|
||||
$filter->since(\DateTime::createFromFormat(\DateTime::ATOM, $since));
|
||||
}
|
||||
|
||||
if (($until = $parameters->get('until')) !== null) {
|
||||
$filter->until(\DateTime::createFromFormat(\DateTime::ATOM, $until));
|
||||
}
|
||||
|
||||
if ($parameters->filter('ascending', false, FILTER_VALIDATE_BOOLEAN)) {
|
||||
$filter->ascending();
|
||||
} else {
|
||||
$filter->descending();
|
||||
}
|
||||
|
||||
$filter->limit($parameters->getInt('limit'));
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.controller.statement.get" class="XApi\LrsBundle\Controller\StatementGetController">
|
||||
<argument type="service" id="xapi_lrs.repository.statement"/>
|
||||
<argument type="service" id="xapi_lrs.statement.serializer"/>
|
||||
<argument type="service" id="xapi_lrs.statement_result.serializer"/>
|
||||
<argument type="service" id="xapi_lrs.factory.statements_filter"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.controller.statement.post" class="XApi\LrsBundle\Controller\StatementPostController"/>
|
||||
|
||||
<service id="xapi_lrs.controller.statement.put" class="XApi\LrsBundle\Controller\StatementPutController">
|
||||
<argument type="service" id="xapi_lrs.repository.statement"/>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.repository.statement.doctrine" class="XApi\Repository\Doctrine\Repository\StatementRepository" public="false">
|
||||
<argument type="service" id="xapi_lrs.repository.mapped_statement" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.event_listener.alternate_request_syntax" class="XApi\LrsBundle\EventListener\AlternateRequestSyntaxListener">
|
||||
<tag name="kernel.event_listener" event="kernel.request" />
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.event_listener.exception" class="XApi\LrsBundle\EventListener\ExceptionListener">
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.event_listener.serializer" class="XApi\LrsBundle\EventListener\SerializerListener">
|
||||
<argument type="service" id="xapi_lrs.statement.serializer" />
|
||||
<tag name="kernel.event_listener" event="kernel.request" />
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.event_listener.version" class="XApi\LrsBundle\EventListener\VersionListener">
|
||||
<tag name="kernel.event_listener" event="kernel.request" />
|
||||
<tag name="kernel.event_listener" event="kernel.response" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.factory.statements_filter" class="XApi\LrsBundle\Model\StatementsFilterFactory">
|
||||
<argument type="service" id="xapi_lrs.actor.serializer"/>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
18
plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml
Normal file
18
plugin/xapi/php-xapi/lrs-bundle/src/Resources/config/orm.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.doctrine.class_metadata" class="Doctrine\ORM\Mapping\ClassMetadata" public="false">
|
||||
<argument>XApi\Repository\Api\Mapping\MappedStatement</argument>
|
||||
<factory service="xapi_lrs.doctrine.object_manager" method="getClassMetadata" />
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.repository.mapped_statement" class="XApi\Repository\ORM\MappedStatementRepository" public="false">
|
||||
<argument type="service" id="xapi_lrs.doctrine.object_manager" />
|
||||
<argument type="service" id="xapi_lrs.doctrine.class_metadata" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<routes xmlns="http://symfony.com/schema/routing"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/routing
|
||||
http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||
|
||||
<route id="xapi_lrs.statement.put" path="/statements" methods="PUT">
|
||||
<default key="_controller">xapi_lrs.controller.statement.put:putStatement</default>
|
||||
<default key="xapi_serializer">statement</default>
|
||||
<default key="xapi_lrs.route">
|
||||
<bool>true</bool>
|
||||
</default>
|
||||
</route>
|
||||
|
||||
<route id="xapi_lrs.statement.post" path="/statements" methods="POST">
|
||||
<default key="_controller">xapi_lrs.controller.statement.post:postStatement</default>
|
||||
<default key="xapi_serializer">statement</default>
|
||||
<default key="xapi_lrs.route">
|
||||
<bool>true</bool>
|
||||
</default>
|
||||
</route>
|
||||
|
||||
<route id="xapi_lrs.statement.get" path="/statements" methods="GET">
|
||||
<default key="_controller">xapi_lrs.controller.statement.get:getStatement</default>
|
||||
<default key="xapi_lrs.route">
|
||||
<bool>true</bool>
|
||||
</default>
|
||||
</route>
|
||||
</routes>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services
|
||||
http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="xapi_lrs.statement.serializer" class="Xabbuh\XApi\Serializer\StatementSerializerInterface" public="false">
|
||||
<factory service="xapi_lrs.serializer.factory" method="createStatementSerializer"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.statement_result.serializer" class="Xabbuh\XApi\Serializer\StatementResultSerializerInterface" public="false">
|
||||
<factory service="xapi_lrs.serializer.factory" method="createStatementResultSerializer"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.actor.serializer" class="Xabbuh\XApi\Serializer\ActorSerializerInterface" public="false">
|
||||
<factory service="xapi_lrs.serializer.factory" method="createActorSerializer"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.document_data.serializer" class="Xabbuh\XApi\Serializer\DocumentDataSerializerInterface" public="false">
|
||||
<factory service="xapi_lrs.serializer.factory" method="createDocumentDataSerializer"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.serializer_factory" class="Xabbuh\XApi\Serializer\Symfony\SerializerFactory" public="false">
|
||||
<argument type="service" id="xapi_lrs.serializer"/>
|
||||
</service>
|
||||
|
||||
<service id="xapi_lrs.serializer" class="Symfony\Component\Serializer\SerializerInterface" public="false">
|
||||
<factory class="Xabbuh\XApi\Serializer\Symfony\Serializer" method="createSerializer"/>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Response;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Xabbuh\XApi\Model\Attachment;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class AttachmentResponse extends Response
|
||||
{
|
||||
protected $attachment;
|
||||
|
||||
public function __construct(Attachment $attachment)
|
||||
{
|
||||
parent::__construct(null);
|
||||
|
||||
$this->attachment = $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare(Request $request)
|
||||
{
|
||||
if (!$this->headers->has('Content-Type')) {
|
||||
$this->headers->set('Content-Type', $this->attachment->getContentType());
|
||||
}
|
||||
|
||||
$this->headers->set('Content-Transfer-Encoding', 'binary');
|
||||
$this->headers->set('X-Experience-API-Hash', $this->attachment->getSha2());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function sendContent()
|
||||
{
|
||||
throw new \LogicException('An AttachmentResponse is only meant to be part of a multipart Response.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException when the content is not null
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on an AttachmentResponse instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->attachment->getContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle\Response;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @author Jérôme Parmentier <jerome.parmentier@acensi.fr>
|
||||
*/
|
||||
class MultipartResponse extends Response
|
||||
{
|
||||
protected $subtype;
|
||||
protected $boundary;
|
||||
protected $statementPart;
|
||||
/**
|
||||
* @var Response[]
|
||||
*/
|
||||
protected $parts;
|
||||
|
||||
/**
|
||||
* @param AttachmentResponse[] $attachmentsParts
|
||||
* @param int $status
|
||||
* @param string|null $subtype
|
||||
*/
|
||||
public function __construct(JsonResponse $statementPart, array $attachmentsParts = [], $status = 200, array $headers = [], $subtype = null)
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
|
||||
if (null === $subtype) {
|
||||
$subtype = 'mixed';
|
||||
}
|
||||
|
||||
$this->subtype = $subtype;
|
||||
$this->boundary = uniqid('', true);
|
||||
$this->statementPart = $statementPart;
|
||||
|
||||
$this->setAttachmentsParts($attachmentsParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function addAttachmentPart(AttachmentResponse $part)
|
||||
{
|
||||
if ($part->getContent() !== null) {
|
||||
$this->parts[] = $part;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttachmentResponse[] $attachmentsParts
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttachmentsParts(array $attachmentsParts)
|
||||
{
|
||||
$this->parts = [$this->statementPart];
|
||||
|
||||
foreach ($attachmentsParts as $part) {
|
||||
$this->addAttachmentPart($part);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare(Request $request)
|
||||
{
|
||||
foreach ($this->parts as $part) {
|
||||
$part->prepare($request);
|
||||
}
|
||||
|
||||
$this->headers->set('Content-Type', sprintf('multipart/%s; boundary="%s"', $this->subtype, $this->boundary));
|
||||
$this->headers->set('Transfer-Encoding', 'chunked');
|
||||
|
||||
return parent::prepare($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendContent()
|
||||
{
|
||||
$content = '';
|
||||
foreach ($this->parts as $part) {
|
||||
$content .= sprintf('--%s', $this->boundary)."\r\n";
|
||||
$content .= $part->headers."\r\n";
|
||||
$content .= $part->getContent();
|
||||
$content .= "\r\n";
|
||||
}
|
||||
|
||||
$content .= sprintf('--%s--', $this->boundary)."\r\n";
|
||||
|
||||
echo $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \LogicException when the content is not null
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on a MultipartResponse instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
26
plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php
Normal file
26
plugin/xapi/php-xapi/lrs-bundle/src/XApiLrsBundle.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\LrsBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use XApi\LrsBundle\DependencyInjection\XApiLrsExtension;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class XApiLrsBundle extends Bundle
|
||||
{
|
||||
public function getContainerExtension()
|
||||
{
|
||||
return new XApiLrsExtension();
|
||||
}
|
||||
}
|
||||
19
plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE
Normal file
19
plugin/xapi/php-xapi/repository-doctrine-orm/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-2017 Christian Flothmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
41
plugin/xapi/php-xapi/repository-doctrine-orm/composer.json
Normal file
41
plugin/xapi/php-xapi/repository-doctrine-orm/composer.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "php-xapi/repository-doctrine-orm",
|
||||
"description": "Doctrine based ORM implementations of an Experience API (xAPI) repository",
|
||||
"keywords": ["xAPI", "Tin Can API", "Experience API", "storage", "database", "repository", "entity", "Doctrine", "ORM"],
|
||||
"homepage": "https://github.com/php-xapi/repository-orm/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Flothmann",
|
||||
"homepage": "https://github.com/xabbuh"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"doctrine/orm": "^2.3",
|
||||
"php-xapi/repository-api": "^0.4",
|
||||
"php-xapi/repository-doctrine": "^0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3.4 || ^4.0"
|
||||
},
|
||||
"provide": {
|
||||
"php-xapi/repository-implementation": "0.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"XApi\\Repository\\ORM\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"XApi\\Repository\\ORM\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Actor" table="xapi_actor">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="type" type="string" nullable="true" />
|
||||
<field name="mbox" type="string" nullable="true" />
|
||||
<field name="mboxSha1Sum" type="string" nullable="true" />
|
||||
<field name="openId" type="string" nullable="true" />
|
||||
<field name="accountName" type="string" nullable="true" />
|
||||
<field name="accountHomePage" type="string" nullable="true" />
|
||||
<field name="name" type="string" nullable="true" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,23 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Attachment" table="xapi_attachment">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="usageType" type="string" />
|
||||
<field name="contentType" type="string" />
|
||||
<field name="length" type="integer" />
|
||||
<field name="sha2" type="string" />
|
||||
<field name="display" type="json_array" />
|
||||
<field name="hasDescription" type="boolean" />
|
||||
<field name="description" type="json_array" nullable="true" />
|
||||
<field name="fileUrl" type="string" nullable="true" />
|
||||
<field name="content" type="text" nullable="true" />
|
||||
|
||||
<many-to-one field="statement" target-entity="XApi\Repository\Doctrine\Mapping\Statement" inversed-by="attachments" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,59 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Context" table="xapi_context">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="registration" type="string" nullable="true" />
|
||||
<field name="hasContextActivities" type="boolean" nullable="true" />
|
||||
<field name="revision" type="string" nullable="true" />
|
||||
<field name="platform" type="string" nullable="true" />
|
||||
<field name="language" type="string" nullable="true" />
|
||||
<field name="statement" type="string" nullable="true" />
|
||||
|
||||
<one-to-one field="instructor" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="team" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="extensions" target-entity="XApi\Repository\Doctrine\Mapping\Extensions">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
|
||||
<!-- context activities -->
|
||||
<one-to-many field="parentActivities" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" mapped-by="parentContext">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
</one-to-many>
|
||||
<one-to-many field="groupingActivities" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" mapped-by="groupingContext">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
</one-to-many>
|
||||
<one-to-many field="categoryActivities" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" mapped-by="categoryContext">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
</one-to-many>
|
||||
<one-to-many field="otherActivities" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" mapped-by="otherContext">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
</one-to-many>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,13 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Extensions" table="xapi_extensions">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="extensions" type="json_array" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,28 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Result" table="xapi_result">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="hasScore" type="boolean" />
|
||||
<field name="scaled" type="float" nullable="true" />
|
||||
<field name="raw" type="float" nullable="true" />
|
||||
<field name="min" type="float" nullable="true" />
|
||||
<field name="max" type="float" nullable="true" />
|
||||
<field name="success" type="boolean" nullable="true" />
|
||||
<field name="completion" type="boolean" nullable="true" />
|
||||
<field name="response" type="string" nullable="true" />
|
||||
<field name="duration" type="string" nullable="true" />
|
||||
|
||||
<one-to-one field="extensions" target-entity="XApi\Repository\Doctrine\Mapping\Extensions">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,62 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Statement"
|
||||
repository-class="XApi\Repository\ORM\StatementRepository"
|
||||
table="xapi_statement">
|
||||
|
||||
<id name="id" type="string">
|
||||
<generator strategy="NONE" />
|
||||
</id>
|
||||
|
||||
<field name="created" type="bigint" nullable="true" />
|
||||
<field name="stored" type="bigint" nullable="true" />
|
||||
<field name="hasAttachments" type="boolean" />
|
||||
|
||||
<one-to-one field="actor" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="verb" target-entity="XApi\Repository\Doctrine\Mapping\Verb">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="object" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="result" target-entity="XApi\Repository\Doctrine\Mapping\Result">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="authority" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="context" target-entity="XApi\Repository\Doctrine\Mapping\Context">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
|
||||
<!-- attachments -->
|
||||
<one-to-many field="attachments" target-entity="XApi\Repository\Doctrine\Mapping\Attachment" mapped-by="statement">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
</one-to-many>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,83 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\StatementObject" table="xapi_object">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<!-- discriminator column -->
|
||||
<field name="type" type="string" nullable="true" />
|
||||
|
||||
<!-- activity -->
|
||||
<field name="activityId" type="string" nullable="true" />
|
||||
<field name="hasActivityDefinition" type="boolean" nullable="true" />
|
||||
<field name="hasActivityName" type="boolean" nullable="true" />
|
||||
<field name="activityName" type="json_array" nullable="true" />
|
||||
<field name="hasActivityDescription" type="boolean" nullable="true" />
|
||||
<field name="activityDescription" type="json_array" nullable="true" />
|
||||
<field name="activityType" type="string" nullable="true" />
|
||||
<field name="activityMoreInfo" type="string" nullable="true" />
|
||||
|
||||
<!-- actor -->
|
||||
<field name="mbox" type="string" nullable="true" />
|
||||
<field name="mboxSha1Sum" type="string" nullable="true" />
|
||||
<field name="openId" type="string" nullable="true" />
|
||||
<field name="accountName" type="string" nullable="true" />
|
||||
<field name="accountHomePage" type="string" nullable="true" />
|
||||
<field name="name" type="string" nullable="true" />
|
||||
|
||||
<!-- statement reference -->
|
||||
<field name="referencedStatementId" type="string" nullable="true" />
|
||||
|
||||
<!-- sub statement -->
|
||||
<one-to-one field="actor" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="verb" target-entity="XApi\Repository\Doctrine\Mapping\Verb">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
<one-to-one field="object" target-entity="XApi\Repository\Doctrine\Mapping\StatementObject">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
|
||||
<!-- activity extensions -->
|
||||
<one-to-one field="activityExtensions" target-entity="XApi\Repository\Doctrine\Mapping\Extensions">
|
||||
<cascade>
|
||||
<cascade-all />
|
||||
</cascade>
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</one-to-one>
|
||||
|
||||
<!-- group members -->
|
||||
<one-to-many target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" mapped-by="group" field="members" />
|
||||
<many-to-one target-entity="XApi\Repository\Doctrine\Mapping\StatementObject" field="group" inversed-by="members">
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</many-to-one>
|
||||
|
||||
<!-- context activities -->
|
||||
<many-to-one target-entity="XApi\Repository\Doctrine\Mapping\Context" field="parentContext" inversed-by="parentActivities">
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</many-to-one>
|
||||
<many-to-one target-entity="XApi\Repository\Doctrine\Mapping\Context" field="groupingContext" inversed-by="groupingActivities">
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</many-to-one>
|
||||
<many-to-one target-entity="XApi\Repository\Doctrine\Mapping\Context" field="categoryContext" inversed-by="categoryActivities">
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</many-to-one>
|
||||
<many-to-one target-entity="XApi\Repository\Doctrine\Mapping\Context" field="otherContext" inversed-by="otherActivities">
|
||||
<join-column referenced-column-name="identifier" />
|
||||
</many-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,14 @@
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="XApi\Repository\Doctrine\Mapping\Verb" table="xapi_verb">
|
||||
<id name="identifier" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="id" type="string" />
|
||||
<field name="display" type="json_array" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the xAPI package.
|
||||
*
|
||||
* (c) Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace XApi\Repository\ORM;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use XApi\Repository\Doctrine\Mapping\Context;
|
||||
use XApi\Repository\Doctrine\Mapping\Statement;
|
||||
use XApi\Repository\Doctrine\Repository\Mapping\StatementRepository as BaseStatementRepository;
|
||||
|
||||
/**
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
final class StatementRepository extends EntityRepository implements BaseStatementRepository
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findStatement(array $criteria)
|
||||
{
|
||||
return parent::findOneBy($criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findStatements(array $criteria)
|
||||
{
|
||||
if (!empty($criteria['registration'])) {
|
||||
$contexts = $this->_em->getRepository(Context::class)->findBy([
|
||||
'registration' => $criteria['registration'],
|
||||
]);
|
||||
|
||||
$criteria['context'] = $contexts;
|
||||
}
|
||||
|
||||
unset(
|
||||
$criteria['registration'],
|
||||
$criteria['related_activities'],
|
||||
$criteria['related_agents'],
|
||||
$criteria['ascending'],
|
||||
$criteria['limit']
|
||||
);
|
||||
|
||||
return parent::findBy($criteria, ['created' => 'ASC']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storeStatement(Statement $mappedStatement, $flush = true)
|
||||
{
|
||||
$this->_em->persist($mappedStatement);
|
||||
|
||||
if ($flush) {
|
||||
$this->_em->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user