XAPI: Single import process - refs BT#16742

* XAPI: Single import process - refs BT#16742

* XAPI: Set cascade operation for tool entity - refs BT#16742

* XAPI: Rename files - refs BT#16742

* XAPI: Fix LRS config when import/edit package - refs BT#16742

* XAPI: Fix breadcrumb and back button - refs BT#16742

* XAPI: Fix width in table column - refs BT#16742
pull/3732/head
Angel Fernando Quiroz Campos 5 years ago committed by GitHub
parent 8c95efcb3e
commit 6ffe842b2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 119
      plugin/xapi/cmi5/add.php
  2. 0
      plugin/xapi/cmi5/launch.php
  3. 2
      plugin/xapi/cmi5/view.php
  4. 2
      plugin/xapi/src/Entity/Cmi5Item.php
  5. 31
      plugin/xapi/src/Entity/ToolLaunch.php
  6. 129
      plugin/xapi/src/Importer/AbstractImporter.php
  7. 45
      plugin/xapi/src/Importer/Cmi5Importer.php
  8. 76
      plugin/xapi/src/Importer/PackageImporter.php
  9. 46
      plugin/xapi/src/Importer/TinCanImporter.php
  10. 29
      plugin/xapi/src/Importer/XmlPackageImporter.php
  11. 95
      plugin/xapi/src/Importer/ZipPackageImporter.php
  12. 53
      plugin/xapi/src/Parser/AbstractParser.php
  13. 40
      plugin/xapi/src/Parser/Cmi5Parser.php
  14. 70
      plugin/xapi/src/Parser/PackageParser.php
  15. 19
      plugin/xapi/src/Parser/TinCanParser.php
  16. 8
      plugin/xapi/src/XApiPlugin.php
  17. 25
      plugin/xapi/start.php
  18. 4
      plugin/xapi/tincan/stats.php
  19. 4
      plugin/xapi/tincan/view.php
  20. 2
      plugin/xapi/tool_delete.php
  21. 50
      plugin/xapi/tool_edit.php
  22. 47
      plugin/xapi/tool_import.php

@ -1,119 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\PluginBundle\XApi\Importer\Cmi5Importer;
use Chamilo\PluginBundle\XApi\Parser\Cmi5Parser;
require_once __DIR__.'/../../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();
$course = api_get_course_entity();
$session = api_get_session_entity();
$plugin = XApiPlugin::create();
$frmActivity = new FormValidator('frm_cmi5', 'post', api_get_self().'?'.api_get_cidreq());
$frmActivity->addFile('file', $plugin->get_lang('Cmi5Package'));
$frmActivity->addButtonAdvancedSettings('lrs_params', $plugin->get_lang('LrsConfiguration'));
$frmActivity->addHtml('<div id="lrs_params_options" style="display:none">');
$frmActivity->addText(
'lrs_url',
[
$plugin->get_lang('lrs_url'),
$plugin->get_lang('lrs_url_help'),
],
false
);
$frmActivity->addText(
'lrs_auth',
[
$plugin->get_lang('lrs_auth_username'),
$plugin->get_lang('lrs_auth_username_help'),
],
false
);
$frmActivity->addText(
'lrs_auth',
[
$plugin->get_lang('lrs_auth_password'),
$plugin->get_lang('lrs_auth_password_help'),
],
false
);
$frmActivity->addHtml('</div>');
$frmActivity->addButtonImport(get_lang('Import'));
$frmActivity->addRule('file', get_lang('ThisFileIsRequired'), 'required');
$frmActivity->addRule(
'file',
$plugin->get_lang('OnlyZipAllowed'),
'filetype',
['zip']
);
$frmActivity->applyFilter('title', 'trim');
$frmActivity->applyFilter('description', 'trim');
$frmActivity->applyFilter('lrs_url', 'trim');
$frmActivity->applyFilter('lrs_auth', 'trim');
if ($frmActivity->validate()) {
$values = $frmActivity->exportValues();
$zipFileInfo = $_FILES['file'];
try {
$packageFile = Cmi5Importer::create($zipFileInfo, $course)->import();
$parser = Cmi5Parser::create($packageFile, $course, $session);
$toolLaunch = $parser->parse();
} catch (Exception $e) {
Display::addFlash(
Display::return_message($e->getMessage(), 'error')
);
exit;
}
if (!empty($values['lrs_url'])
&& !empty($values['lrs_auth_username'])
&& !empty($values['lrs_auth_password'])
) {
$toolLaunch
->setLrsUrl($values['lrs_url'])
->setLrsAuthUsername($values['lrs_auth_username'])
->setLrsAuthUsername($values['lrs_auth_password']);
}
$em = Database::getManager();
$em->persist($toolLaunch);
foreach ($parser->getToc() as $cmi5Item) {
$cmi5Item->setTool($toolLaunch);
$em->persist($cmi5Item);
}
$em->flush();
Display::addFlash(
Display::return_message($plugin->get_lang('ActivityImported'), 'success')
);
header('Location: '.api_get_course_url());
exit;
}
$frmActivity->setDefaults(['allow_multiple_attempts' => true]);
$pageTitle = $plugin->get_title();
$pageContent = $frmActivity->returnForm();
$interbreadcrumb[] = ['url' => '../tincan/index.php', 'name' => $plugin->get_lang('ToolTinCan')];
$langAddActivity = $plugin->get_lang('AddActivity');
$view = new Template($langAddActivity);
$view->assign('header', $langAddActivity);
$view->assign('content', $pageContent);
$view->display_one_col_template();

@ -62,7 +62,7 @@ $tocHtml = $itemsRepo->buildTree(
return Display::url(
$title,
"launch_item.php?tool={$toolLaunch->getId()}&id={$node['id']}&$cidReq",
"launch.php?tool={$toolLaunch->getId()}&id={$node['id']}&$cidReq",
[
'target' => 'ifr_content',
'class' => 'text-left btn-link',

@ -100,7 +100,7 @@ class Cmi5Item
/**
* @var \Chamilo\PluginBundle\Entity\XApi\ToolLaunch
*
* @ORM\ManyToOne(targetEntity="Chamilo\PluginBundle\Entity\XApi\ToolLaunch")
* @ORM\ManyToOne(targetEntity="Chamilo\PluginBundle\Entity\XApi\ToolLaunch", inversedBy="items")
* @ORM\JoinColumn(name="tool_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $tool;

@ -6,6 +6,7 @@ namespace Chamilo\PluginBundle\Entity\XApi;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
@ -100,6 +101,13 @@ class ToolLaunch
* @ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* @var \Doctrine\Common\Collections\ArrayCollection
*
* @ORM\OneToMany(targetEntity="Chamilo\PluginBundle\Entity\XApi\Cmi5Item", mappedBy="tool", orphanRemoval=true,
* cascade="ALL")
*/
private $items;
/**
* ToolLaunch constructor.
@ -107,6 +115,7 @@ class ToolLaunch
public function __construct()
{
$this->allowMultipleAttempts = true;
$this->items = new ArrayCollection();
}
public function getId(): int
@ -264,4 +273,26 @@ class ToolLaunch
return $this;
}
/**
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function getItems(): ArrayCollection
{
return $this->items;
}
/**
* @param \Chamilo\PluginBundle\Entity\XApi\Cmi5Item $cmi5Item
*
* @return $this
*/
public function addItem(Cmi5Item $cmi5Item)
{
$cmi5Item->setTool($this);
$this->items->add($cmi5Item);
return $this;
}
}

@ -1,129 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use Chamilo\CoreBundle\Entity\Course;
use DocumentManager;
use Exception;
use PclZip;
use Symfony\Component\Filesystem\Filesystem;
/**
* Class AbstractImporter.
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
abstract class AbstractImporter
{
/**
* @var \Chamilo\CoreBundle\Entity\Course
*/
protected $course;
/**
* @var string
*/
protected $courseDirectoryPath;
/**
* @var string
*/
protected $toolDirectory;
/**
* @var string
*/
protected $packageDirectoryPath;
/**
* @var \PclZip
*/
protected $zipFile;
/**
* AbstractImporter constructor.
*/
protected function __construct(array $fileInfo, string $toolDirectory, Course $course)
{
$this->course = $course;
$pathInfo = pathinfo($fileInfo['name']);
$this->courseDirectoryPath = api_get_path(SYS_COURSE_PATH).$this->course->getDirectory();
$this->toolDirectory = $toolDirectory;
$this->packageDirectoryPath = implode(
'/',
[
$this->courseDirectoryPath,
$this->toolDirectory,
api_replace_dangerous_char($pathInfo['filename']),
]
);
$this->zipFile = new PclZip($fileInfo['tmp_name']);
}
/**
* @return \Chamilo\PluginBundle\XApi\Importer\AbstractImporter
*/
abstract public static function create(array $fileInfo, Course $course);
/**
* @throws \Exception
*/
public function import()
{
$this->validPackage();
if (!$this->isEnoughSpace()) {
throw new Exception('Not enough space to storage package.');
}
$fs = new Filesystem();
$fs->mkdir(
$this->packageDirectoryPath,
api_get_permissions_for_new_directories()
);
$this->zipFile->extract($this->packageDirectoryPath);
return "{$this->packageDirectoryPath}/{$this->toolDirectory}.xml";
}
/**
* @throws \Exception
*/
protected function validPackage()
{
$zipContent = $this->zipFile->listContent();
if (empty($zipContent)) {
throw new Exception('Package file is empty');
}
foreach ($zipContent as $zipEntry) {
if (preg_match('~.(php.*|phtml)$~i', $zipEntry['filename'])) {
throw new Exception("File \"{$zipEntry['filename']}\" contains a PHP script");
}
}
}
/**
* @return bool
*/
private function isEnoughSpace()
{
$zipRealSize = array_reduce(
$this->zipFile->listContent(),
function ($accumulator, $zipEntry) {
return $accumulator + $zipEntry['size'];
}
);
$courseSpaceQuota = DocumentManager::get_course_quota($this->course->getCode());
if (!enough_size($zipRealSize, $this->courseDirectoryPath, $courseSpaceQuota)) {
return false;
}
return true;
}
}

@ -1,45 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use Chamilo\CoreBundle\Entity\Course;
/**
* Class Cmi5Importer.
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
class Cmi5Importer extends AbstractImporter
{
/**
* {@inheritdoc}
*/
public static function create(array $fileInfo, Course $course)
{
return new self($fileInfo, 'cmi5', $course);
}
/**
* {@inheritdoc}
*/
protected function validPackage()
{
parent::validPackage();
$zipContent = $this->zipFile->listContent();
$isValid = false;
foreach ($zipContent as $zipEntry) {
if ('cmi5.xml' === $zipEntry['filename']) {
$isValid = true;
}
}
if (!$isValid) {
throw new \Exception('Incorrect package. Missing "cmi5.xml" file');
}
}
}

@ -0,0 +1,76 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use Chamilo\CoreBundle\Entity\Course;
/**
* Class AbstractImporter.
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
abstract class PackageImporter
{
/**
* @var \Chamilo\CoreBundle\Entity\Course
*/
protected $course;
/**
* @var string
*/
protected $courseDirectoryPath;
/**
* @var array
*/
protected $packageFileInfo;
/**
* @var string
*/
protected $packageType;
/**
* AbstractImporter constructor.
*
* @param array $fileInfo
* @param \Chamilo\CoreBundle\Entity\Course $course
*/
protected function __construct(array $fileInfo, Course $course)
{
$this->packageFileInfo = $fileInfo;
$this->course = $course;
$this->courseDirectoryPath = api_get_path(SYS_COURSE_PATH).$this->course->getDirectory();
}
/**
* @param array $fileInfo
* @param \Chamilo\CoreBundle\Entity\Course $course
*
* @return \Chamilo\PluginBundle\XApi\Importer\XmlPackageImporter|\Chamilo\PluginBundle\XApi\Importer\ZipPackageImporter
*/
public static function create(array $fileInfo, Course $course)
{
if ('text/xml' === $fileInfo['type']) {
return new XmlPackageImporter($fileInfo, $course);
}
return new ZipPackageImporter($fileInfo, $course);
}
/**
* @throws \Exception
*
* @return mixed
*/
abstract public function import(): string;
/**
* @return string
*/
public function getPackageType(): string
{
return $this->packageType;
}
}

@ -1,46 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use Chamilo\CoreBundle\Entity\Course;
use Exception;
/**
* Class TinCanImporter.
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
class TinCanImporter extends AbstractImporter
{
/**
* {@inheritdoc}
*/
public static function create(array $fileInfo, Course $course)
{
return new self($fileInfo, 'tincan', $course);
}
/**
* {@inheritdoc}
*/
protected function validPackage()
{
parent::validPackage();
$zipContent = $this->zipFile->listContent();
$isValid = false;
foreach ($zipContent as $zipEntry) {
if ('tincan.xml' === $zipEntry['filename']) {
$isValid = true;
}
}
if (!$isValid) {
throw new Exception('Incorrect package. Missing "tincan.xml" file');
}
}
}

@ -0,0 +1,29 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use Exception;
/**
* Class XmlImporter.
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
class XmlPackageImporter extends PackageImporter
{
/**
* @inheritDoc
*/
public function import(): string
{
if (!in_array($this->packageFileInfo['name'], ['tincan.xml', 'cmi5.xml'])) {
throw new Exception('Invalid package');
}
$this->packageType = explode('.', $this->packageFileInfo['name'], 2)[0];
return $this->packageFileInfo['tmp_name'];
}
}

@ -0,0 +1,95 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Importer;
use DocumentManager;
use Exception;
use PclZip;
use Symfony\Component\Filesystem\Filesystem;
/**
* Class ZipImporter
*
* @package Chamilo\PluginBundle\XApi\Importer
*/
class ZipPackageImporter extends PackageImporter
{
/**
* @inheritDoc
*/
public function import(): string
{
$zipFile = new PclZip($this->packageFileInfo['tmp_name']);
$zipContent = $zipFile->listContent();
$packageSize = array_reduce(
$zipContent,
function ($accumulator, $zipEntry) {
if (preg_match('~.(php.*|phtml)$~i', $zipEntry['filename'])) {
throw new Exception("File \"{$zipEntry['filename']}\" contains a PHP script");
}
if (in_array($zipEntry['filename'], ['tincan.xml', 'cmi5.xml'])) {
$this->packageType = explode('.', $zipEntry['filename'], 2)[0];
}
return $accumulator + $zipEntry['size'];
}
);
if (empty($this->packageType)) {
throw new Exception('Invalid package');
}
$this->validateEnoughSpace($packageSize);
$pathInfo = pathinfo($this->packageFileInfo['name']);
$packageDirectoryPath = $this->generatePackageDirectory($pathInfo['filename']);
$zipFile->extract($packageDirectoryPath);
return "$packageDirectoryPath/{$this->packageType}.xml";
}
/**
* @param int $packageSize
*
* @throws \Exception
*/
protected function validateEnoughSpace(int $packageSize)
{
$courseSpaceQuota = DocumentManager::get_course_quota($this->course->getCode());
if (!enough_size($packageSize, $this->courseDirectoryPath, $courseSpaceQuota)) {
throw new Exception('Not enough space to storage package.');
}
}
/**
* @param string $name
*
* @return string
*/
private function generatePackageDirectory(string $name): string
{
$directoryPath = implode(
'/',
[
$this->courseDirectoryPath,
$this->packageType,
api_replace_dangerous_char($name),
]
);
$fs = new Filesystem();
$fs->mkdir(
$directoryPath,
api_get_permissions_for_new_directories()
);
return $directoryPath;
}
}

@ -1,53 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Parser;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
/**
* Class AbstractParser.
*
* @package Chamilo\PluginBundle\XApi\Parser
*/
abstract class AbstractParser
{
/**
* @var string
*/
protected $filePath;
/**
* @var Course
*/
protected $course;
/**
* @var Session|null
*/
protected $session;
/**
* AbstractParser constructor.
*
* @param $filePath
*/
protected function __construct($filePath, Course $course, Session $session = null)
{
$this->filePath = $filePath;
$this->course = $course;
$this->session = $session;
}
/**
* @param string $filePath
*
* @return mixed
*/
abstract public static function create($filePath, Course $course, Session $session = null);
/**
* @return \Chamilo\PluginBundle\Entity\XApi\ToolLaunch
*/
abstract public function parse();
}

@ -4,8 +4,6 @@
namespace Chamilo\PluginBundle\XApi\Parser;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\PluginBundle\Entity\XApi\Cmi5Item;
use Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
use Symfony\Component\DomCrawler\Crawler;
@ -15,25 +13,12 @@ use Symfony\Component\DomCrawler\Crawler;
*
* @package Chamilo\PluginBundle\XApi\Parser
*/
class Cmi5Parser extends AbstractParser
class Cmi5Parser extends PackageParser
{
/**
* @var array|\Chamilo\PluginBundle\Entity\XApi\Cmi5Item[]
* @inheritDoc
*/
private $toc;
/**
* {@inheritdoc}
*/
public static function create($filePath, Course $course, Session $session = null)
{
return new self($filePath, $course, $session);
}
/**
* {@inheritdoc}
*/
public function parse()
public function parse(): ToolLaunch
{
$content = file_get_contents($this->filePath);
$xml = new Crawler($content);
@ -64,17 +49,13 @@ class Cmi5Parser extends AbstractParser
->setCourse($this->course)
->setSession($this->session);
$this->toc = $this->generateToC($xml);
$toc = $this->generateToC($xml);
return $toolLaunch;
}
foreach ($toc as $cmi5Item) {
$toolLaunch->addItem($cmi5Item);
}
/**
* @return array|\Chamilo\PluginBundle\Entity\XApi\Cmi5Item[]
*/
public function getToc()
{
return $this->toc;
return $toolLaunch;
}
/**
@ -133,9 +114,8 @@ class Cmi5Parser extends AbstractParser
if ('au' === $node->nodeName()) {
$launchParametersNode = $node->filterXPath('//launchParameters');
$entitlementKeyNode = $node->filterXPath('//entitlementKey');
$url =
$item
$url
= $item
->setUrl(
$this->parseLaunchUrl(
trim($node->filterXPath('//url')->text())

@ -0,0 +1,70 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\XApi\Parser;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
/**
* Class PackageParser.
*
* @package Chamilo\PluginBundle\XApi\Parser
*/
abstract class PackageParser
{
/**
* @var string
*/
protected $filePath;
/**
* @var Course
*/
protected $course;
/**
* @var Session|null
*/
protected $session;
/**
* AbstractParser constructor.
*
* @param $filePath
* @param \Chamilo\CoreBundle\Entity\Course $course
* @param \Chamilo\CoreBundle\Entity\Session|null $session
*/
protected function __construct($filePath, Course $course, Session $session = null)
{
$this->filePath = $filePath;
$this->course = $course;
$this->session = $session;
}
/**
* @param string $packageType
* @param string $filePath
* @param \Chamilo\CoreBundle\Entity\Course $course
* @param \Chamilo\CoreBundle\Entity\Session|null $session
*
* @throws \Exception
*
* @return mixed
*/
public static function create(string $packageType, string $filePath, Course $course, Session $session = null)
{
switch ($packageType) {
case 'tincan':
return new TinCanParser($filePath, $course, $session);
case 'cmi5':
return new Cmi5Parser($filePath, $course, $session);
default:
throw new \Exception('Invalid package.');
}
}
/**
* @return \Chamilo\PluginBundle\Entity\XApi\ToolLaunch
*/
abstract public function parse(): \Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
}

@ -4,8 +4,6 @@
namespace Chamilo\PluginBundle\XApi\Parser;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
use Symfony\Component\DomCrawler\Crawler;
@ -14,20 +12,12 @@ use Symfony\Component\DomCrawler\Crawler;
*
* @package Chamilo\PluginBundle\XApi\Parser
*/
class TinCanParser extends AbstractParser
class TinCanParser extends PackageParser
{
/**
* {@inheritdoc}
* @inheritDoc
*/
public static function create($filePath, Course $course, Session $session = null)
{
return new self($filePath, $course, $session);
}
/**
* {@inheritdoc}
*/
public function parse()
public function parse(): ToolLaunch
{
$content = file_get_contents($this->filePath);
@ -61,7 +51,7 @@ class TinCanParser extends AbstractParser
/**
* @return string
*/
private function parseLaunchUrl(Crawler $launchNode)
private function parseLaunchUrl(Crawler $launchNode): string
{
$launchUrl = $launchNode->text();
@ -79,4 +69,5 @@ class TinCanParser extends AbstractParser
return $launchUrl;
}
}

@ -317,7 +317,7 @@ class XApiPlugin extends Plugin implements HookPluginInterface
$this->get_lang('ToolTinCan'),
$courseId,
null,
'xapi/tincan/index.php'
'xapi/start.php'
);
}
@ -525,10 +525,6 @@ class XApiPlugin extends Plugin implements HookPluginInterface
{
Database::getManager()
->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link')
->execute(['category' => 'plugin', 'link' => 'xapi/tincan/index.php%']);
Database::getManager()
->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link')
->execute(['category' => 'plugin', 'link' => 'xapi/cmi5/index.php%']);
->execute(['category' => 'plugin', 'link' => 'xapi/start.php%']);
}
}

@ -4,7 +4,7 @@
use Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
require_once __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_block_anonymous_users();
@ -68,7 +68,7 @@ $table->set_column_filter(
$data = Display::url(
$title,
('cmi5' === $ativityType ? '../cmi5/tool.php' : 'tool.php')."?id=$id&$cidReq",
('cmi5' === $ativityType ? 'cmi5/view.php' : 'tincan/view.php')."?id=$id&$cidReq",
['class' => 'show']
);
@ -81,7 +81,9 @@ $table->set_column_filter(
);
if ($isAllowedToEdit) {
$table->set_header(1, get_lang('Actions'), false, ['class' => 'text-right'], ['class' => 'text-right']);
$thAttributes = ['class' => 'text-right', 'style' => 'width: 100px;'];
$table->set_header(1, get_lang('Actions'), false, $thAttributes, $thAttributes);
$table->set_column_filter(
1,
function ($id) use ($cidReq, $isAllowedToEdit) {
@ -90,15 +92,15 @@ if ($isAllowedToEdit) {
if ($isAllowedToEdit) {
$actions[] = Display::url(
Display::return_icon('statistics.png', get_lang('Reporting')),
"stats.php?$cidReq&id=$id"
"tincan/stats.php?$cidReq&id=$id"
);
$actions[] = Display::url(
Display::return_icon('edit.png', get_lang('Edit')),
"edit.php?$cidReq&edit=$id"
"tool_edit.php?$cidReq&edit=$id"
);
$actions[] = Display::url(
Display::return_icon('delete.png', get_lang('Delete')),
"delete.php?$cidReq&delete=$id"
"tool_delete.php?$cidReq&delete=$id"
);
}
@ -119,14 +121,9 @@ $view->assign('header', $pageTitle);
if ($isAllowedToEdit) {
$actions = Display::url(
Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM),
"add.php?$cidReq"
)
.PHP_EOL
.Display::url(
Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM),
"../cmi5/add.php?$cidReq"
);
Display::return_icon('import_scorm.png', get_lang('Import'), [], ICON_SIZE_MEDIUM),
"tool_import.php?$cidReq"
);
$view->assign(
'actions',

@ -109,7 +109,7 @@ $content .= $pagination;
// View
$interbreadcrumb[] = [
'name' => $plugin->get_title(),
'url' => 'index.php',
'url' => '../start.php',
];
$htmlHeadXtra[] = "<script>
@ -150,7 +150,7 @@ $htmlHeadXtra[] = "<script>
$actions = Display::url(
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
"index.php?$cidReq"
"../start.php?$cidReq"
);
$view = new Template($toolLaunch->getTitle());

@ -140,7 +140,7 @@ if ($stateDocument) {
$table->setColAttributes(2, ['class' => 'text-center']);
}
$interbreadcrumb[] = ['url' => 'index.php', 'name' => $plugin->get_lang('ToolTinCan')];
$interbreadcrumb[] = ['url' => '../start.php', 'name' => $plugin->get_lang('ToolTinCan')];
$pageTitle = $toolLaunch->getTitle();
$pageContent = '';
@ -165,7 +165,7 @@ if ($stateDocument) {
$actions = Display::url(
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
'index.php?'.api_get_cidreq()
'../start.php?'.api_get_cidreq()
);
$view = new Template($pageTitle);

@ -5,7 +5,7 @@
use Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
require_once __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();

@ -5,7 +5,7 @@
use Chamilo\PluginBundle\Entity\XApi\ToolLaunch;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
require_once __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();
@ -31,18 +31,22 @@ $cidReq = api_get_cidreq();
$plugin = XApiPlugin::create();
$toolIsCmi5 = 'cmi5' === $toolLaunch->getActivityType();
$toolIsTinCan = !$toolIsCmi5;
$langEditActivity = $plugin->get_lang('EditActivity');
$frmActivity = new FormValidator('frm_activity', 'post', api_get_self()."?$cidReq&edit={$toolLaunch->getId()}");
$frmActivity->addText('title', get_lang('Title'));
$frmActivity->addTextarea('description', get_lang('Description'));
$frmActivity->addCheckBox('allow_multiple_attempts', '', get_lang('AllowMultipleAttempts'));
$frmActivity->addButtonAdvancedSettings('advanced_params');
$frmActivity->addHtml('<div id="advanced_params_options" style="display:none">');
$frmActivity->addUrl('launch_url', $plugin->get_lang('ActivityLaunchUrl'), true);
$frmActivity->addUrl('activity_id', $plugin->get_lang('ActivityId'), true);
$frmActivity->addUrl('activity_type', $plugin->get_lang('ActivityType'), true);
$frmActivity->addHtml('</div>');
if ($toolIsTinCan) {
$frmActivity->addButtonAdvancedSettings('advanced_params');
$frmActivity->addHtml('<div id="advanced_params_options" style="display:none">');
$frmActivity->addCheckBox('allow_multiple_attempts', '', get_lang('AllowMultipleAttempts'));
$frmActivity->addHtml('</div>');
}
$frmActivity->addButtonAdvancedSettings('lrs_params', $plugin->get_lang('LrsConfiguration'));
$frmActivity->addHtml('<div id="lrs_params_options" style="display:none">');
$frmActivity->addText(
@ -54,7 +58,7 @@ $frmActivity->addText(
false
);
$frmActivity->addText(
'lrs_auth',
'lrs_auth_username',
[
$plugin->get_lang('lrs_auth_username'),
$plugin->get_lang('lrs_auth_username_help'),
@ -62,7 +66,7 @@ $frmActivity->addText(
false
);
$frmActivity->addText(
'lrs_auth',
'lrs_auth_password',
[
$plugin->get_lang('lrs_auth_password'),
$plugin->get_lang('lrs_auth_password_help'),
@ -81,15 +85,16 @@ if ($frmActivity->validate()) {
$toolLaunch
->setTitle($values['title'])
->setDescription(empty($values['description']) ? null : $values['description'])
->setLaunchUrl($values['launch_url'])
->setActivityId($values['activity_id'])
->setActivityType($values['activity_type'])
->setAllowMultipleAttempts(
isset($values['allow_multiple_attempts'])
);
if (!empty($values['lrs_url']) && !empty($values['lrs_auth'])) {
->setDescription(empty($values['description']) ? null : $values['description']);
if ($toolIsTinCan && isset($values['allow_multiple_attempts'])) {
$toolLaunch->setAllowMultipleAttempts(true);
}
if (!empty($values['lrs_url'])
&& !empty($values['lrs_auth_username'])
&& !empty($values['lrs_auth_password'])
) {
$toolLaunch
->setLrsUrl($values['lrs_url'])
->setLrsAuthUsername($values['lrs_auth_username'])
@ -111,9 +116,6 @@ $frmActivity->setDefaults(
[
'title' => $toolLaunch->getTitle(),
'description' => $toolLaunch->getDescription(),
'activity_id' => $toolLaunch->getActivityId(),
'activity_type' => $toolLaunch->getActivityType(),
'launch_url' => $toolLaunch->getLaunchUrl(),
'allow_multiple_attempts' => $toolLaunch->isAllowMultipleAttempts(),
'lrs_url' => $toolLaunch->getLrsUrl(),
'lrs_auth_username' => $toolLaunch->getLrsAuthUsername(),
@ -123,12 +125,12 @@ $frmActivity->setDefaults(
$actions = Display::url(
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
'index.php?'.api_get_cidreq()
'start.php?'.api_get_cidreq()
);
$pageContent = $frmActivity->returnForm();
$interbreadcrumb[] = ['url' => 'index.php', 'name' => $plugin->get_lang('ToolTinCan')];
$interbreadcrumb[] = ['url' => 'start.php', 'name' => $plugin->get_lang('ToolTinCan')];
$view = new Template($langEditActivity);
$view->assign('header', $langEditActivity);

@ -2,27 +2,32 @@
/* For licensing terms, see /license.txt */
use Chamilo\PluginBundle\XApi\Importer\TinCanImporter;
use Chamilo\PluginBundle\XApi\Importer\PackageImporter;
use Chamilo\PluginBundle\XApi\Importer\XmlPackageImporter;
use Chamilo\PluginBundle\XApi\Parser\PackageParser;
use Chamilo\PluginBundle\XApi\Parser\TinCanParser;
require_once __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();
$course = api_get_course_entity();
$session = api_get_session_entity();
$cidReq = api_get_cidreq();
$plugin = XApiPlugin::create();
$pluginIndex = "./start.php?$cidReq";
$langAddActivity = $plugin->get_lang('AddActivity');
$frmActivity = new FormValidator('frm_activity', 'post', api_get_self().'?'.api_get_cidreq());
$frmActivity->addFile('file', $plugin->get_lang('TinCanPackage'));
$frmActivity->addCheckBox('allow_multiple_attempts', '', get_lang('AllowMultipleAttempts'));
$frmActivity->addFile('file', $plugin->get_lang('XApiPackage'));
$frmActivity->addButtonAdvancedSettings('advanced_params');
$frmActivity->addHtml('<div id="advanced_params_options" style="display:none">');
$frmActivity->addText('title', get_lang('Title'), false);
$frmActivity->addTextarea('description', get_lang('Description'));
$frmActivity->addCheckBox('allow_multiple_attempts', '', get_lang('TinCanAllowMultipleAttempts'));
$frmActivity->addHtml('</div>');
$frmActivity->addButtonAdvancedSettings('lrs_params', $plugin->get_lang('LrsConfiguration'));
$frmActivity->addHtml('<div id="lrs_params_options" style="display:none">');
@ -35,7 +40,7 @@ $frmActivity->addText(
false
);
$frmActivity->addText(
'lrs_auth',
'lrs_auth_username',
[
$plugin->get_lang('lrs_auth_username'),
$plugin->get_lang('lrs_auth_username_help'),
@ -43,7 +48,7 @@ $frmActivity->addText(
false
);
$frmActivity->addText(
'lrs_auth',
'lrs_auth_password',
[
$plugin->get_lang('lrs_auth_password'),
$plugin->get_lang('lrs_auth_password_help'),
@ -55,9 +60,9 @@ $frmActivity->addButtonImport(get_lang('Import'));
$frmActivity->addRule('file', get_lang('ThisFileIsRequired'), 'required');
$frmActivity->addRule(
'file',
$plugin->get_lang('OnlyZipAllowed'),
$plugin->get_lang('OnlyZipOrXmlAllowed'),
'filetype',
['zip']
['zip', 'xml']
);
$frmActivity->applyFilter('title', 'trim');
$frmActivity->applyFilter('description', 'trim');
@ -69,20 +74,28 @@ if ($frmActivity->validate()) {
$zipFileInfo = $_FILES['file'];
try {
$tinCanFile = TinCanImporter::create($zipFileInfo, $course)->import();
$toolLaunch = TinCanParser::create($tinCanFile, $course, $session)->parse();
$importer = PackageImporter::create($zipFileInfo, $course);
$packageFile = $importer->import();
$parser = PackageParser::create(
$importer->getPackageType(),
$packageFile,
$course,
$session
);
$toolLaunch = $parser->parse();
} catch (Exception $e) {
Display::addFlash(
Display::return_message($e->getMessage(), 'error')
);
header("Location: $pluginIndex");
exit;
}
$toolLaunch->setAllowMultipleAttempts(
isset($values['allow_multiple_attempts'])
);
if ('tincan' === $importer->getPackageType() && isset($values['allow_multiple_attempts'])) {
$toolLaunch->setAllowMultipleAttempts(true);
}
if (!empty($values['title'])) {
$toolLaunch->setTitle($values['title']);
@ -110,7 +123,7 @@ if ($frmActivity->validate()) {
Display::return_message($plugin->get_lang('ActivityImported'), 'success')
);
header('Location: '.api_get_course_url());
header("Location: $pluginIndex");
exit;
}
@ -118,12 +131,12 @@ $frmActivity->setDefaults(['allow_multiple_attempts' => true]);
$actions = Display::url(
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
'index.php?'.api_get_cidreq()
'start.php?'.api_get_cidreq()
);
$pageContent = $frmActivity->returnForm();
$interbreadcrumb[] = ['url' => 'index.php', 'name' => $plugin->get_lang('ToolTinCan')];
$interbreadcrumb[] = ['url' => 'start.php', 'name' => $plugin->get_lang('ToolTinCan')];
$view = new Template($langAddActivity);
$view->assign('header', $langAddActivity);
Loading…
Cancel
Save