Actualización
This commit is contained in:
255
plugin/h5pimport/Entity/H5pImport.php
Normal file
255
plugin/h5pimport/Entity/H5pImport.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\H5pImport;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
use Chamilo\CoreBundle\Entity\Session;
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* Class H5pImport.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
*
|
||||
* @ORM\Table(name="plugin_h5p_import")
|
||||
*/
|
||||
class H5pImport
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="path", type="text", nullable=false)
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="relative_path", type="text", nullable=false)
|
||||
*/
|
||||
protected $relativePath;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
*
|
||||
* @ORM\Column(name="created_at", type="datetime", nullable=false)
|
||||
*/
|
||||
protected $createdAt;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
*
|
||||
* @ORM\Column(name="modified_at", type="datetime", nullable=false)
|
||||
*/
|
||||
protected $modifiedAt;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="iid", type="integer")
|
||||
*
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $iid;
|
||||
|
||||
/**
|
||||
* @var Course
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course")
|
||||
*
|
||||
* @ORM\JoinColumn(name="c_id", referencedColumnName="id", nullable=false)
|
||||
*/
|
||||
private $course;
|
||||
|
||||
/**
|
||||
* @var Session|null
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Session")
|
||||
*
|
||||
* @ORM\JoinColumn(name="session_id", referencedColumnName="id")
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*
|
||||
* @ORM\Column(name="name", type="text", nullable=true)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*
|
||||
* @ORM\Column(name="description", type="text", nullable=true)
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @var Collection<int, H5pImportLibrary>
|
||||
*
|
||||
* @ORM\ManyToMany(targetEntity="H5pImportLibrary", mappedBy="h5pImports", cascade={"persist"})
|
||||
*/
|
||||
private $libraries;
|
||||
|
||||
/**
|
||||
* @var H5pImportLibrary
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="H5pImportLibrary")
|
||||
*
|
||||
* @ORM\JoinColumn(name="main_library_id", referencedColumnName="iid", onDelete="SET NULL")
|
||||
*/
|
||||
private $mainLibrary;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->libraries = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getIid(): int
|
||||
{
|
||||
return $this->iid;
|
||||
}
|
||||
|
||||
public function setIid(int $iid): void
|
||||
{
|
||||
$this->iid = $iid;
|
||||
}
|
||||
|
||||
public function getCourse(): Course
|
||||
{
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
public function setCourse(Course $course): H5pImport
|
||||
{
|
||||
$this->course = $course;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSession(): ?Session
|
||||
{
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
public function setSession(?Session $session): H5pImport
|
||||
{
|
||||
$this->session = $session;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): H5pImport
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setPath(string $path): H5pImport
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): H5pImport
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRelativePath(): string
|
||||
{
|
||||
return $this->relativePath;
|
||||
}
|
||||
|
||||
public function setRelativePath(string $relativePath): H5pImport
|
||||
{
|
||||
$this->relativePath = $relativePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTime $createdAt): H5pImport
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getModifiedAt(): DateTime
|
||||
{
|
||||
return $this->modifiedAt;
|
||||
}
|
||||
|
||||
public function setModifiedAt(DateTime $modifiedAt): void
|
||||
{
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
}
|
||||
|
||||
public function addLibraries(H5pImportLibrary $library): self
|
||||
{
|
||||
$library->addH5pImport($this);
|
||||
$this->libraries[] = $library;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeLibraries(H5pImportLibrary $library): self
|
||||
{
|
||||
$this->libraries->removeElement($library);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLibraries(): Collection
|
||||
{
|
||||
return $this->libraries;
|
||||
}
|
||||
|
||||
public function setMainLibrary(H5pImportLibrary $library): self
|
||||
{
|
||||
$this->mainLibrary = $library;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMainLibrary(): ?H5pImportLibrary
|
||||
{
|
||||
return $this->mainLibrary;
|
||||
}
|
||||
}
|
||||
383
plugin/h5pimport/Entity/H5pImportLibrary.php
Normal file
383
plugin/h5pimport/Entity/H5pImportLibrary.php
Normal file
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\H5pImport;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* Class H5pImportLibrary.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
*
|
||||
* @ORM\Table(name="plugin_h5p_import_library")
|
||||
*/
|
||||
class H5pImportLibrary extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
*
|
||||
* @ORM\Column(name="created_at", type="datetime", nullable=false)
|
||||
*/
|
||||
protected $createdAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
*
|
||||
* @ORM\Column(name="modified_at", type="datetime", nullable=false)
|
||||
*/
|
||||
protected $modifiedAt;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="iid", type="integer")
|
||||
*
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $iid;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="title", type="string", nullable=true)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="machine_name", type="string")
|
||||
*/
|
||||
private $machineName;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="major_version", type="integer")
|
||||
*/
|
||||
private $majorVersion;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="minor_version", type="integer")
|
||||
*/
|
||||
private $minorVersion;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="patch_version", type="integer")
|
||||
*/
|
||||
private $patchVersion;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="runnable", type="integer", nullable=true)
|
||||
*/
|
||||
private $runnable;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="embed_types", type="array", nullable=true)
|
||||
*/
|
||||
private $embedTypes;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="preloaded_js" , type="array", nullable=true)
|
||||
*/
|
||||
private $preloadedJs;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="preloaded_css", type="array", nullable=true)
|
||||
*/
|
||||
private $preloadedCss;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="library_path", type="string", length=255)
|
||||
*/
|
||||
private $libraryPath;
|
||||
|
||||
/**
|
||||
* @var Collection<int, H5pImport>
|
||||
*
|
||||
* @ORM\ManyToMany(targetEntity="H5pImport", inversedBy="libraries")
|
||||
*
|
||||
* @ORM\JoinTable(
|
||||
* name="plugin_h5p_import_rel_libraries",
|
||||
* joinColumns={@ORM\JoinColumn(name="h5p_import_library_id", referencedColumnName="iid", onDelete="CASCADE")},
|
||||
* inverseJoinColumns={@ORM\JoinColumn(name="h5p_import_id", referencedColumnName="iid", onDelete="CASCADE")}
|
||||
* )
|
||||
*/
|
||||
private $h5pImports;
|
||||
|
||||
/**
|
||||
* @var Course
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course")
|
||||
*
|
||||
* @ORM\JoinColumn(name="c_id", referencedColumnName="id", nullable=false)
|
||||
*/
|
||||
private $course;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->h5pImports = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getIid(): int
|
||||
{
|
||||
return $this->iid;
|
||||
}
|
||||
|
||||
public function setIid(int $iid): H5pImportLibrary
|
||||
{
|
||||
$this->iid = $iid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMachineName(): string
|
||||
{
|
||||
return $this->machineName;
|
||||
}
|
||||
|
||||
public function setMachineName(string $machineName): H5pImportLibrary
|
||||
{
|
||||
$this->machineName = $machineName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): H5pImportLibrary
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMajorVersion(): int
|
||||
{
|
||||
return $this->majorVersion;
|
||||
}
|
||||
|
||||
public function setMajorVersion(int $majorVersion): H5pImportLibrary
|
||||
{
|
||||
$this->majorVersion = $majorVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMinorVersion(): int
|
||||
{
|
||||
return $this->minorVersion;
|
||||
}
|
||||
|
||||
public function setMinorVersion(int $minorVersion): H5pImportLibrary
|
||||
{
|
||||
$this->minorVersion = $minorVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPatchVersion(): int
|
||||
{
|
||||
return $this->patchVersion;
|
||||
}
|
||||
|
||||
public function setPatchVersion(int $patchVersion): H5pImportLibrary
|
||||
{
|
||||
$this->patchVersion = $patchVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRunnable(): int
|
||||
{
|
||||
return $this->runnable;
|
||||
}
|
||||
|
||||
public function setRunnable(?int $runnable): H5pImportLibrary
|
||||
{
|
||||
$this->runnable = $runnable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPreloadedJs(): ?array
|
||||
{
|
||||
return $this->preloadedJs;
|
||||
}
|
||||
|
||||
public function setPreloadedJs(?array $preloadedJs): H5pImportLibrary
|
||||
{
|
||||
$this->preloadedJs = $preloadedJs;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPreloadedCss(): ?array
|
||||
{
|
||||
return $this->preloadedCss;
|
||||
}
|
||||
|
||||
public function setPreloadedCss(?array $preloadedCss): H5pImportLibrary
|
||||
{
|
||||
$this->preloadedCss = $preloadedCss;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmbedTypes(): ?array
|
||||
{
|
||||
return $this->embedTypes;
|
||||
}
|
||||
|
||||
public function setEmbedTypes(?array $embedTypes): H5pImportLibrary
|
||||
{
|
||||
$this->embedTypes = $embedTypes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLibraryPath(): string
|
||||
{
|
||||
return $this->libraryPath;
|
||||
}
|
||||
|
||||
public function setLibraryPath(string $libraryPath): H5pImportLibrary
|
||||
{
|
||||
$this->libraryPath = $libraryPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addH5pImport(H5pImport $h5pImport): self
|
||||
{
|
||||
if (!$this->h5pImports->contains($h5pImport)) {
|
||||
$this->h5pImports[] = $h5pImport;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeH5pImport(H5pImport $h5pImport): self
|
||||
{
|
||||
$this->h5pImports->removeElement($h5pImport);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getH5pImports(): Collection
|
||||
{
|
||||
return $this->h5pImports;
|
||||
}
|
||||
|
||||
public function getCourse(): Course
|
||||
{
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
public function setCourse(Course $course): self
|
||||
{
|
||||
$this->course = $course;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTime $createdAt): H5pImportLibrary
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getModifiedAt(): DateTime
|
||||
{
|
||||
return $this->modifiedAt;
|
||||
}
|
||||
|
||||
public function setModifiedAt(DateTime $modifiedAt): H5pImportLibrary
|
||||
{
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLibraryByMachineNameAndVersions(string $machineName, int $majorVersion, int $minorVersion)
|
||||
{
|
||||
if (
|
||||
$this->machineName === $machineName
|
||||
&& $this->majorVersion === $majorVersion
|
||||
&& $this->minorVersion === $minorVersion
|
||||
) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preloaded JS array of the imported library formatted as a comma-separated string.
|
||||
*
|
||||
* @return string the preloaded JS array of the imported library formatted as a comma-separated string
|
||||
*/
|
||||
public function getPreloadedJsFormatted(): string
|
||||
{
|
||||
$formattedJs = [];
|
||||
|
||||
foreach ($this->preloadedJs as $value) {
|
||||
if (is_string($value->path)) {
|
||||
$formattedJs[] = $value->path;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',', $formattedJs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preloaded CSS array of the imported library formatted as a comma-separated string.
|
||||
*
|
||||
* @return string the preloaded CSS array of the imported library formatted as a comma-separated string
|
||||
*/
|
||||
public function getPreloadedCssFormatted(): string
|
||||
{
|
||||
$formattedJCss = [];
|
||||
|
||||
foreach ($this->preloadedCss as $value) {
|
||||
if (is_string($value->path)) {
|
||||
$formattedJCss[] = $value->path;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',', $formattedJCss);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the embed types array formatted as a comma-separated string.
|
||||
*
|
||||
* @return string the embed types array formatted as a comma-separated string
|
||||
*/
|
||||
public function getEmbedTypesFormatted(): string
|
||||
{
|
||||
return implode(',', $this->getEmbedTypes());
|
||||
}
|
||||
}
|
||||
267
plugin/h5pimport/Entity/H5pImportResults.php
Normal file
267
plugin/h5pimport/Entity/H5pImportResults.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\H5pImport;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
use Chamilo\CoreBundle\Entity\Session;
|
||||
use Chamilo\CourseBundle\Entity\CLpItemView;
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* Class H5pImportResults.
|
||||
*
|
||||
* @ORM\Entity()
|
||||
*
|
||||
* @ORM\Table(name="plugin_h5p_import_results")
|
||||
*/
|
||||
class H5pImportResults
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="start_time", type="integer", nullable=false)
|
||||
*/
|
||||
protected $startTime;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="total_time", type="integer", nullable=false)
|
||||
*/
|
||||
protected $totalTime;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="iid", type="integer")
|
||||
*
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $iid;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="score", type="integer")
|
||||
*/
|
||||
private $score;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="max_score", type="integer")
|
||||
*/
|
||||
private $maxScore;
|
||||
|
||||
/**
|
||||
* @var Course
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Course")
|
||||
*
|
||||
* @ORM\JoinColumn(name="c_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
private $course;
|
||||
|
||||
/**
|
||||
* @var Session|null
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\Session")
|
||||
*
|
||||
* @ORM\JoinColumn(name="session_id", referencedColumnName="id")
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* @var H5pImport
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\PluginBundle\Entity\H5pImport\H5pImport")
|
||||
*
|
||||
* @ORM\JoinColumn(name="plugin_h5p_import_id", referencedColumnName="iid", nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
private $h5pImport;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\UserBundle\Entity\User")
|
||||
*
|
||||
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var CLpItemView
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLpItemView")
|
||||
*
|
||||
* @ORM\JoinColumn(name="c_lp_item_view_id", referencedColumnName="iid", nullable=true, onDelete="CASCADE")
|
||||
*/
|
||||
private $cLpItemView;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
*
|
||||
* @ORM\Column(name="created_at", type="datetime", nullable=false)
|
||||
*/
|
||||
private $createdAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
*
|
||||
* @ORM\Column(name="modified_at", type="datetime", nullable=false)
|
||||
*/
|
||||
private $modifiedAt;
|
||||
|
||||
public function getIid(): int
|
||||
{
|
||||
return $this->iid;
|
||||
}
|
||||
|
||||
public function setIid(int $iid): H5pImportResults
|
||||
{
|
||||
$this->iid = $iid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScore(): int
|
||||
{
|
||||
return $this->score;
|
||||
}
|
||||
|
||||
public function setScore(int $score): H5pImportResults
|
||||
{
|
||||
$this->score = $score;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxScore(): int
|
||||
{
|
||||
return $this->maxScore;
|
||||
}
|
||||
|
||||
public function setMaxScore(int $maxScore): H5pImportResults
|
||||
{
|
||||
$this->maxScore = $maxScore;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCourse(): Course
|
||||
{
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
public function setCourse(Course $course): H5pImportResults
|
||||
{
|
||||
$this->course = $course;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSession(): ?Session
|
||||
{
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
public function setSession(?Session $session): H5pImportResults
|
||||
{
|
||||
$this->session = $session;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getH5pImport(): H5pImport
|
||||
{
|
||||
return $this->h5pImport;
|
||||
}
|
||||
|
||||
public function setH5pImport(H5pImport $h5pImport): H5pImportResults
|
||||
{
|
||||
$this->h5pImport = $h5pImport;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(User $user): H5pImportResults
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTime
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getCLpItemView(): CLpItemView
|
||||
{
|
||||
return $this->cLpItemView;
|
||||
}
|
||||
|
||||
public function setCLpItemView(CLpItemView $cLpItemView): H5pImportResults
|
||||
{
|
||||
$this->cLpItemView = $cLpItemView;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStartTime(): int
|
||||
{
|
||||
return $this->startTime;
|
||||
}
|
||||
|
||||
public function setStartTime(int $startTime): H5pImportResults
|
||||
{
|
||||
$this->startTime = $startTime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTotalTime(): int
|
||||
{
|
||||
return $this->totalTime;
|
||||
}
|
||||
|
||||
public function setTotalTime(int $totalTime): H5pImportResults
|
||||
{
|
||||
$this->totalTime = $totalTime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTime $createdAt): H5pImportResults
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getModifiedAt(): DateTime
|
||||
{
|
||||
return $this->modifiedAt;
|
||||
}
|
||||
|
||||
public function setModifiedAt(DateTime $modifiedAt): H5pImportResults
|
||||
{
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
307
plugin/h5pimport/H5pImportPlugin.php
Normal file
307
plugin/h5pimport/H5pImportPlugin.php
Normal file
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportResults;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Doctrine\ORM\Tools\ToolsException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Define the H5pImportPlugin class as an extension of Plugin
|
||||
* install/uninstall the plugin.
|
||||
*/
|
||||
class H5pImportPlugin extends Plugin
|
||||
{
|
||||
public const TBL_H5P_IMPORT = 'plugin_h5p_import';
|
||||
public const TBL_H5P_IMPORT_LIBRARY = 'plugin_h5p_import_library';
|
||||
public const TBL_H5P_IMPORT_RESULTS = 'plugin_h5p_import_results';
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
$settings = [
|
||||
'tool_enable' => 'boolean',
|
||||
'frame' => 'boolean',
|
||||
'embed' => 'boolean',
|
||||
'copyright' => 'boolean',
|
||||
'icon' => 'boolean',
|
||||
];
|
||||
|
||||
parent::__construct(
|
||||
'0.1',
|
||||
'Borja Sanchez',
|
||||
$settings
|
||||
);
|
||||
}
|
||||
|
||||
public static function create(): ?H5pImportPlugin
|
||||
{
|
||||
static $result = null;
|
||||
|
||||
return $result ? $result : $result = new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates and returns the total duration in the view of an H5P learning path item in a course.
|
||||
*
|
||||
* @param int $lpItemId The ID of the learning path item
|
||||
* @param int $userId The user ID
|
||||
*
|
||||
* @return int The updated total duration in the learning path item view
|
||||
*/
|
||||
public static function fixTotalTimeInLpItemView(
|
||||
int $lpItemId,
|
||||
int $userId
|
||||
): int {
|
||||
$lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
|
||||
|
||||
$sql = "SELECT iid, score
|
||||
FROM $lpItemViewTable
|
||||
WHERE
|
||||
iid = $lpItemId
|
||||
ORDER BY view_count DESC
|
||||
LIMIT 1";
|
||||
$responseItemView = Database::query($sql);
|
||||
$lpItemView = Database::fetch_array($responseItemView);
|
||||
|
||||
// Get the total execution duration of the user in the learning path item view
|
||||
$sql = 'SELECT SUM(total_time) AS exe_duration
|
||||
FROM plugin_h5p_import_results
|
||||
WHERE
|
||||
user_id = '.$userId.' AND
|
||||
c_lp_item_view_id = '.$lpItemView['iid'].
|
||||
' ORDER BY total_time DESC';
|
||||
$sumScoreResult = Database::query($sql);
|
||||
$durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
|
||||
|
||||
if (!empty($durationRow['exe_duration'])) {
|
||||
// Update the total duration in the learning path item view
|
||||
$sqlUpdate = 'UPDATE '.$lpItemViewTable.'
|
||||
SET total_time = '.$durationRow['exe_duration'].'
|
||||
WHERE iid = '.$lpItemView['iid'];
|
||||
Database::query($sqlUpdate);
|
||||
|
||||
return (int) $durationRow['exe_duration'];
|
||||
} else {
|
||||
// Update c_lp_item_view status
|
||||
$sqlUpdate = 'UPDATE '.$lpItemViewTable.'
|
||||
SET status = "not attempted",
|
||||
total_time = 0
|
||||
WHERE iid = '.$lpItemView['iid'];
|
||||
Database::query($sqlUpdate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function getToolTitle(): string
|
||||
{
|
||||
$title = $this->get_lang('plugin_title');
|
||||
|
||||
if (!empty($title)) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
return $this->get_title();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ToolsException
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
$em = Database::getManager();
|
||||
if ($em->getConnection()
|
||||
->getSchemaManager()
|
||||
->tablesExist(
|
||||
[
|
||||
self::TBL_H5P_IMPORT,
|
||||
self::TBL_H5P_IMPORT_LIBRARY,
|
||||
self::TBL_H5P_IMPORT_RESULTS,
|
||||
]
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schemaTool->createSchema(
|
||||
[
|
||||
$em->getClassMetadata(H5pImport::class),
|
||||
$em->getClassMetadata(H5pImportLibrary::class),
|
||||
$em->getClassMetadata(H5pImportResults::class),
|
||||
]
|
||||
);
|
||||
$this->addCourseTools();
|
||||
}
|
||||
|
||||
public function addCourseTool(int $courseId)
|
||||
{
|
||||
// The $link param is set to "../plugin" as a hack to link correctly to the plugin URL in course tool.
|
||||
// Otherwise, the link en the course tool will link to "/main/" URL.
|
||||
$this->createLinkToCourseTool(
|
||||
$this->get_lang('plugin_title'),
|
||||
$courseId,
|
||||
'plugin_h5p_import.png',
|
||||
'../plugin/h5pimport/start.php',
|
||||
0,
|
||||
'authoring'
|
||||
);
|
||||
}
|
||||
|
||||
public function uninstall()
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
if (!$em->getConnection()
|
||||
->getSchemaManager()
|
||||
->tablesExist(
|
||||
[
|
||||
self::TBL_H5P_IMPORT,
|
||||
self::TBL_H5P_IMPORT_LIBRARY,
|
||||
self::TBL_H5P_IMPORT_RESULTS,
|
||||
]
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schemaTool->dropSchema(
|
||||
[
|
||||
$em->getClassMetadata(H5pImport::class),
|
||||
$em->getClassMetadata(H5pImportLibrary::class),
|
||||
$em->getClassMetadata(H5pImportResults::class),
|
||||
]
|
||||
);
|
||||
$this->deleteCourseToolLinks();
|
||||
$this->removeH5pDirectories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform actions after configuring the H5P import plugin.
|
||||
*
|
||||
* @return H5pImportPlugin The H5P import plugin instance.
|
||||
*/
|
||||
public function performActionsAfterConfigure(): H5pImportPlugin
|
||||
{
|
||||
$this->deleteCourseToolLinks();
|
||||
|
||||
if ('true' === $this->get('tool_enable')) {
|
||||
$this->addCourseTools();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view URL for an H5P import.
|
||||
*
|
||||
* @param H5pImport $h5pImport The H5P import object.
|
||||
*
|
||||
* @return string The view URL for the H5P import.
|
||||
*/
|
||||
public function getViewUrl(H5pImport $h5pImport): string
|
||||
{
|
||||
return api_get_path(WEB_PLUGIN_PATH).'h5pimport/view.php?id='.$h5pImport->getIid().'&'.api_get_cidreq();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the LP resource block for H5P imports.
|
||||
*
|
||||
* @param int $lpId The LP ID.
|
||||
*
|
||||
* @return string The HTML for the LP resource block.
|
||||
*/
|
||||
public function getLpResourceBlock(int $lpId): string
|
||||
{
|
||||
$cidReq = api_get_cidreq(true, true, 'lp');
|
||||
$webPath = api_get_path(WEB_PLUGIN_PATH).'h5pimport/';
|
||||
$course = api_get_course_entity();
|
||||
$session = api_get_session_entity();
|
||||
|
||||
$tools = Database::getManager()
|
||||
->getRepository(H5pImport::class)
|
||||
->findBy(['course' => $course, 'session' => $session]);
|
||||
|
||||
$importIcon = Display::return_icon('plugin_h5p_import_upload.png');
|
||||
$moveIcon = Display::url(
|
||||
Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY),
|
||||
'#',
|
||||
['class' => 'moved']
|
||||
);
|
||||
|
||||
$return = '<ul class="lp_resource">';
|
||||
$return .= '<li class="lp_resource_element">';
|
||||
$return .= $importIcon;
|
||||
$return .= Display::url(
|
||||
get_lang('Import'),
|
||||
$webPath."start.php?action=add&$cidReq&".http_build_query(['lp_id' => $lpId])
|
||||
);
|
||||
$return .= '</li>';
|
||||
|
||||
/** @var H5pImport $tool */
|
||||
foreach ($tools as $tool) {
|
||||
$toolAnchor = Display::url(
|
||||
Security::remove_XSS($tool->getName()),
|
||||
api_get_self()."?$cidReq&"
|
||||
.http_build_query(
|
||||
['action' => 'add_item', 'type' => TOOL_H5P, 'file' => $tool->getIid(), 'lp_id' => $lpId]
|
||||
),
|
||||
['class' => 'moved']
|
||||
);
|
||||
|
||||
$return .= Display::tag(
|
||||
'li',
|
||||
$moveIcon.$importIcon.$toolAnchor,
|
||||
[
|
||||
'class' => 'lp_resource_element',
|
||||
'data_id' => $tool->getIid(),
|
||||
'data_type' => TOOL_H5P,
|
||||
'title' => $tool->getName(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$return .= '</ul>';
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add course tools for all courses.
|
||||
*/
|
||||
private function addCourseTools(): void
|
||||
{
|
||||
$courses = Database::getManager()
|
||||
->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c')
|
||||
->getResult();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
$this->addCourseTool($course['id']);
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteCourseToolLinks()
|
||||
{
|
||||
Database::getManager()
|
||||
->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link')
|
||||
->execute(['category' => 'authoring', 'link' => '../plugin/h5pimport/start.php%']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes H5P directories for all courses.
|
||||
*/
|
||||
private function removeH5pDirectories(): void
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
$table = Database::get_main_table(TABLE_MAIN_COURSE);
|
||||
$sql = "SELECT id FROM $table ORDER BY id";
|
||||
$res = Database::query($sql);
|
||||
while ($row = Database::fetch_assoc($res)) {
|
||||
$courseInfo = api_get_course_info_by_id($row['id']);
|
||||
$fs->remove($courseInfo['course_sys_path'].'/h5p');
|
||||
}
|
||||
}
|
||||
}
|
||||
674
plugin/h5pimport/LICENSE.txt
Normal file
674
plugin/h5pimport/LICENSE.txt
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
40
plugin/h5pimport/README.md
Normal file
40
plugin/h5pimport/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# H5P Import Plugin for Chamilo
|
||||
|
||||
## Overview
|
||||
This plugin enables the loading and display of H5P packages within the Chamilo Learning Management System (LMS).
|
||||
With this plugin, users can import and view interactive H5P content seamlessly within Chamilo courses (tool shown as an
|
||||
H5P icon on the course homepage).
|
||||
|
||||
This plugin is currently in beta phase and may have some limitations or bugs. Feedback and bug reports are welcome to help improve the plugin.
|
||||
|
||||
Differences with the previous H5P plugin: this plugin allows for the upload of H5P packages in course-specific
|
||||
environments, avoiding a long list of H5P activities shared between all teachers. It allows for the management of H5P
|
||||
activities outside and inside of learning paths and tracks progress inside learning paths as if it was a Chamilo test.
|
||||
|
||||
H5P activities cannot be edited at this time, they are all visible by the students (cannot be hidden individually
|
||||
inside the tool, but the tool can be hidden and the H5P activities made visible one by one in a learning path).
|
||||
Activities do not show in the list of new elements "since your last visit".
|
||||
|
||||
## Requirements
|
||||
- The 'h5p/h5p-php-library' library requirement has been added to Chamilo's composer.json, so no additional steps are needed to install it.
|
||||
|
||||
## Installation
|
||||
If you have updated your Chamilo software from an official package, you don't need to do anything except enabling the plugin from the plugins page.
|
||||
|
||||
Otherwise (Git update), you will need to:
|
||||
1. Update the Chamilo library and namespace by running `composer update` within the Chamilo root directory.
|
||||
2. Add the plugin to your Chamilo installation's `main/admin/settings.php?category=Plugins` url.
|
||||
3. If the namespace has not been updated, run`composer dump-autoload`.
|
||||
|
||||
## Configuration
|
||||
1. Activate the plugin in the Chamilo administration panel.
|
||||
2. Configure the plugin options. The only required setting is to enable the plugin. The remaining settings are optional and can be customized based on your needs.
|
||||
|
||||
## Usage
|
||||
|
||||
Once the plugin is activated and configured, users will see an H5P tool icon in the course and be able to import and display H5P packages within Chamilo courses.
|
||||
Inside a course, select the H5P Import tool to upload H5P content to the course.
|
||||
Users can interact with the H5P content directly within the tool or through a learning path.
|
||||
|
||||
## Support and Feedback
|
||||
For any issues, questions, or feedback, please create an issue on the Chamilo-lms's GitHub repository at https://github.com/chamilo/chamilo-lms/.
|
||||
5
plugin/h5pimport/config.php
Normal file
5
plugin/h5pimport/config.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
8
plugin/h5pimport/install.php
Normal file
8
plugin/h5pimport/install.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
// For license terms, see /license.txt
|
||||
|
||||
if (!api_is_platform_admin()) {
|
||||
exit('You must have admin permissions to install plugins');
|
||||
}
|
||||
H5pImportPlugin::create()->install();
|
||||
23
plugin/h5pimport/lang/english.php
Normal file
23
plugin/h5pimport/lang/english.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
$strings['plugin_title'] = "H5P import";
|
||||
$strings['plugin_comment'] = "Import H5P contents and use it in learnpaths. Open README.md for installation instructions.";
|
||||
$strings['tool_enable'] = "Enable plugin";
|
||||
$strings['frame'] = "Frame";
|
||||
$strings['frame_help'] = "Show frame and buttons below H5P";
|
||||
$strings['embed'] = "Embed";
|
||||
$strings['embed_help'] = "Display embed button";
|
||||
$strings['copyright'] = "Copyright";
|
||||
$strings['copyright_help'] = "Display copyright button";
|
||||
$strings['icon'] = "Icon";
|
||||
$strings['icon_help'] = "Display H5P icon";
|
||||
$strings['attempts'] = "Number of attempts";
|
||||
$strings['upload_h5p'] = "Upload";
|
||||
$strings['import_h5p_package'] = "Upload H5P package";
|
||||
$strings['h5p_package'] = "H5P package";
|
||||
$strings['h5p_error_loading'] = "Error loading the H5P package";
|
||||
$strings['h5p_error_invalid_token'] = "Invalid security token";
|
||||
$strings['h5p_error_missing_core_asset'] = "Error loading H5P core assets";
|
||||
$strings['h5p_save_content_state'] = "Save user results";
|
||||
$strings['h5p_save_content_freq'] = "Save frequency";
|
||||
$strings['h5p_save_freq_help'] = "How often current content state should be saved, in seconds";
|
||||
$strings['start_attempt'] = "Start attempt";
|
||||
23
plugin/h5pimport/lang/french.php
Normal file
23
plugin/h5pimport/lang/french.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
$strings['plugin_title'] = "H5P import";
|
||||
$strings['plugin_comment'] = "Ce plugin permet d'importer des contenus H5P. Ouvrez le fichier README.md pour des instructions d'installation.";
|
||||
$strings['tool_enable'] = "Activer le plugin";
|
||||
$strings['frame'] = "Frame";
|
||||
$strings['frame_help'] = "Afficher le cadre et les boutons en dessous de H5P";
|
||||
$strings['embed'] = "Intégrer";
|
||||
$strings['embed_help'] = "Afficher le bouton d'intégration (embed)";
|
||||
$strings['copyright'] = "Droits d'auteur";
|
||||
$strings['copyright_help'] = "Afficher le bouton de droits d'auteur";
|
||||
$strings['icon'] = "Icône";
|
||||
$strings['icon_help'] = "Afficher l'icône H5P";
|
||||
$strings['attempts'] = "Nombre de tentatives";
|
||||
$strings['import_h5p_package'] = "Importer un paquet H5P";
|
||||
$strings['upload_h5p'] = "Importer";
|
||||
$strings['h5p_package'] = "Paquet H5P";
|
||||
$strings['h5p_error_loading'] = "Erreur de chargement du paquet H5P";
|
||||
$strings['h5p_error_missing_core_asset'] = "Erreur de chargement des assets H5P core";
|
||||
$strings['h5p_error_invalid_token'] = "Token de sécurité invalide";
|
||||
$strings['h5p_save_content_state'] = "Enregistrer les résultats de l'utilisateur";
|
||||
$strings['h5p_save_content_freq'] = "Fréquence d'enregistrement";
|
||||
$strings['h5p_save_freq_help'] = "Fréquence à laquelle l'état actuel du contenu doit être enregistré, en secondes";
|
||||
$strings['start_attempt'] = "Démarrer la tentative";
|
||||
23
plugin/h5pimport/lang/spanish.php
Normal file
23
plugin/h5pimport/lang/spanish.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
$strings['plugin_title'] = "H5P import";
|
||||
$strings['plugin_comment'] = "Este plugin permite importar paquetes en formato H5P e insertarlos como elementos en una lección. Abra el README.md para instrucciones de instalación.";
|
||||
$strings['tool_enable'] = "Activar plugin";
|
||||
$strings['frame'] = "Frame";
|
||||
$strings['frame_help'] = "Mostrar marco y botones debajo de H5P";
|
||||
$strings['embed'] = "Insertar";
|
||||
$strings['embed_help'] = "Mostrar botón de inserción";
|
||||
$strings['copyright'] = "Derechos de autor";
|
||||
$strings['copyright_help'] = "Mostrar botón de derechos de autor";
|
||||
$strings['icon'] = "Icono";
|
||||
$strings['icon_help'] = "Mostrar icono de H5P";
|
||||
$strings['attempts'] = "Número de intentos";
|
||||
$strings['import_h5p_package'] = "Subir paquete H5P";
|
||||
$strings['upload_h5p'] = "Subir";
|
||||
$strings['h5p_package'] = "Paquete H5P";
|
||||
$strings['h5p_error_loading'] = "Error cargando el paquete H5P";
|
||||
$strings['h5p_error_missing_core_asset'] = "Error cargando dependencias H5P";
|
||||
$strings['h5p_error_invalid_token'] = "Token no valido";
|
||||
$strings['h5p_save_content_state'] = "Guardar resultados del usuario";
|
||||
$strings['h5p_save_content_freq'] = "Frecuencia guardado";
|
||||
$strings['h5p_save_freq_help'] = "Con qué frecuencia se debe guardar el estado actual del contenido, en segundos";
|
||||
$strings['start_attempt'] = "Comenzar intento";
|
||||
5
plugin/h5pimport/plugin.php
Normal file
5
plugin/h5pimport/plugin.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
// For license terms, see /license.txt
|
||||
|
||||
$plugin_info = H5pImportPlugin::create()->get_info();
|
||||
375
plugin/h5pimport/src/H5pImplementation.php
Normal file
375
plugin/h5pimport/src/H5pImplementation.php
Normal file
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary;
|
||||
|
||||
class H5pImplementation implements \H5PFrameworkInterface
|
||||
{
|
||||
private $h5pImport;
|
||||
private $h5pImportLibraries;
|
||||
|
||||
public function __construct(H5pImport $h5pImport)
|
||||
{
|
||||
$this->h5pImport = $h5pImport;
|
||||
$this->h5pImportLibraries = $h5pImport->getLibraries();
|
||||
}
|
||||
|
||||
public function getPlatformInfo()
|
||||
{
|
||||
// TODO: Implement getPlatformInfo() method.
|
||||
}
|
||||
|
||||
public function fetchExternalData($url, $data = null, $blocking = true, $stream = null)
|
||||
{
|
||||
// TODO: Implement fetchExternalData() method.
|
||||
}
|
||||
|
||||
public function setLibraryTutorialUrl($machineName, $tutorialUrl)
|
||||
{
|
||||
// TODO: Implement setLibraryTutorialUrl() method.
|
||||
}
|
||||
|
||||
public function setErrorMessage($message, $code = null)
|
||||
{
|
||||
// TODO: Implement setErrorMessage() method.
|
||||
}
|
||||
|
||||
public function setInfoMessage($message)
|
||||
{
|
||||
// TODO: Implement setInfoMessage() method.
|
||||
}
|
||||
|
||||
public function getMessages($type)
|
||||
{
|
||||
// TODO: Implement getMessages() method.
|
||||
}
|
||||
|
||||
public function t($message, $replacements = [])
|
||||
{
|
||||
return get_lang($message);
|
||||
}
|
||||
|
||||
public function getLibraryFileUrl($libraryFolderName, $fileName)
|
||||
{
|
||||
// TODO: Implement getLibraryFileUrl() method.
|
||||
}
|
||||
|
||||
public function getUploadedH5pFolderPath()
|
||||
{
|
||||
// TODO: Implement getUploadedH5pFolderPath() method.
|
||||
}
|
||||
|
||||
public function getUploadedH5pPath()
|
||||
{
|
||||
// TODO: Implement getUploadedH5pPath() method.
|
||||
}
|
||||
|
||||
public function loadAddons()
|
||||
{
|
||||
$addons = [];
|
||||
$sql = "
|
||||
SELECT l1.machine_name, l1.major_version, l1.minor_version, l1.patch_version,
|
||||
l1.iid, l1.preloaded_js, l1.preloaded_css
|
||||
FROM plugin_h5p_import_library AS l1
|
||||
LEFT JOIN plugin_h5p_import_library AS l2
|
||||
ON l1.machine_name = l2.machine_name AND
|
||||
(l1.major_version < l2.major_version OR
|
||||
(l1.major_version = l2.major_version AND
|
||||
l1.minor_version < l2.minor_version))
|
||||
WHERE l2.machine_name IS null
|
||||
";
|
||||
|
||||
$result = \Database::query($sql);
|
||||
while ($row = \Database::fetch_array($result)) {
|
||||
$addons[] = \H5PCore::snakeToCamel($row);
|
||||
}
|
||||
|
||||
return $addons;
|
||||
}
|
||||
|
||||
public function getLibraryConfig($libraries = null)
|
||||
{
|
||||
// TODO: Implement getLibraryConfig() method.
|
||||
}
|
||||
|
||||
public function loadLibraries()
|
||||
{
|
||||
// TODO: Implement loadLibraries() method.
|
||||
}
|
||||
|
||||
public function getAdminUrl()
|
||||
{
|
||||
// TODO: Implement getAdminUrl() method.
|
||||
}
|
||||
|
||||
public function getLibraryId($machineName, $majorVersion = null, $minorVersion = null)
|
||||
{
|
||||
// TODO: Implement getLibraryId() method.
|
||||
}
|
||||
|
||||
public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist)
|
||||
{
|
||||
// TODO: Implement getWhitelist() method.
|
||||
}
|
||||
|
||||
public function isPatchedLibrary($library)
|
||||
{
|
||||
// TODO: Implement isPatchedLibrary() method.
|
||||
}
|
||||
|
||||
public function isInDevMode()
|
||||
{
|
||||
// TODO: Implement isInDevMode() method.
|
||||
}
|
||||
|
||||
public function mayUpdateLibraries()
|
||||
{
|
||||
// TODO: Implement mayUpdateLibraries() method.
|
||||
}
|
||||
|
||||
public function saveLibraryData(&$libraryData, $new = true)
|
||||
{
|
||||
// TODO: Implement saveLibraryData() method.
|
||||
}
|
||||
|
||||
public function insertContent($content, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement insertContent() method.
|
||||
}
|
||||
|
||||
public function updateContent($content, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement updateContent() method.
|
||||
}
|
||||
|
||||
public function resetContentUserData($contentId)
|
||||
{
|
||||
// TODO: Implement resetContentUserData() method.
|
||||
}
|
||||
|
||||
public function saveLibraryDependencies($libraryId, $dependencies, $dependency_type)
|
||||
{
|
||||
// TODO: Implement saveLibraryDependencies() method.
|
||||
}
|
||||
|
||||
public function copyLibraryUsage($contentId, $copyFromId, $contentMainId = null)
|
||||
{
|
||||
// TODO: Implement copyLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function deleteContentData($contentId)
|
||||
{
|
||||
// TODO: Implement deleteContentData() method.
|
||||
}
|
||||
|
||||
public function deleteLibraryUsage($contentId)
|
||||
{
|
||||
// TODO: Implement deleteLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function saveLibraryUsage($contentId, $librariesInUse)
|
||||
{
|
||||
// TODO: Implement saveLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function getLibraryUsage($libraryId, $skipContent = false)
|
||||
{
|
||||
// TODO: Implement getLibraryUsage() method.
|
||||
}
|
||||
|
||||
public function loadLibrary($machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
if ($this->h5pImportLibraries) {
|
||||
$foundLibrary = $this->h5pImportLibraries->filter(
|
||||
function (H5pImportLibrary $library) use ($machineName, $majorVersion, $minorVersion) {
|
||||
return $library->getLibraryByMachineNameAndVersions($machineName, $majorVersion, $minorVersion);
|
||||
}
|
||||
)->first();
|
||||
if ($foundLibrary) {
|
||||
return [
|
||||
'libraryId' => $foundLibrary->getIid(),
|
||||
'title' => $foundLibrary->getTitle(),
|
||||
'machineName' => $foundLibrary->getMachineName(),
|
||||
'majorVersion' => $foundLibrary->getMajorVersion(),
|
||||
'minorVersion' => $foundLibrary->getMinorVersion(),
|
||||
'patchVersion' => $foundLibrary->getPatchVersion(),
|
||||
'runnable' => $foundLibrary->getRunnable(),
|
||||
'preloadedJs' => $foundLibrary->getPreloadedJsFormatted(),
|
||||
'preloadedCss' => $foundLibrary->getPreloadedCssFormatted(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function loadLibrarySemantics($machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
// TODO: Implement loadLibrarySemantics() method.
|
||||
}
|
||||
|
||||
public function alterLibrarySemantics(&$semantics, $machineName, $majorVersion, $minorVersion)
|
||||
{
|
||||
// TODO: Implement alterLibrarySemantics() method.
|
||||
}
|
||||
|
||||
public function deleteLibraryDependencies($libraryId)
|
||||
{
|
||||
// TODO: Implement deleteLibraryDependencies() method.
|
||||
}
|
||||
|
||||
public function lockDependencyStorage()
|
||||
{
|
||||
// TODO: Implement lockDependencyStorage() method.
|
||||
}
|
||||
|
||||
public function unlockDependencyStorage()
|
||||
{
|
||||
// TODO: Implement unlockDependencyStorage() method.
|
||||
}
|
||||
|
||||
public function deleteLibrary($library)
|
||||
{
|
||||
// TODO: Implement deleteLibrary() method.
|
||||
}
|
||||
|
||||
public function loadContent($id): array
|
||||
{
|
||||
$contentJson = H5pPackageTools::getJson($this->h5pImport->getPath().'/content.json');
|
||||
$h5pJson = H5pPackageTools::getJson($this->h5pImport->getPath().'/h5p.json');
|
||||
|
||||
if ($contentJson && $h5pJson) {
|
||||
$params = json_encode($contentJson);
|
||||
$embedType = implode(',', $h5pJson->embedTypes);
|
||||
$title = $this->h5pImport->getName();
|
||||
$language = $h5pJson->language;
|
||||
$libraryId = $this->h5pImport->getMainLibrary()->getIid();
|
||||
$libraryName = $this->h5pImport->getMainLibrary()->getMachineName();
|
||||
$libraryMajorVersion = $this->h5pImport->getMainLibrary()->getMajorVersion();
|
||||
$libraryMinorVersion = $this->h5pImport->getMainLibrary()->getMinorVersion();
|
||||
$libraryEmbedTypes = $this->h5pImport->getMainLibrary()->getEmbedTypesFormatted();
|
||||
|
||||
// Create the associative array with the loaded content information. Use the unique folder name as id.
|
||||
return [
|
||||
'contentId' => basename($this->h5pImport->getPath()),
|
||||
'params' => $params,
|
||||
'embedType' => $embedType,
|
||||
'title' => $title,
|
||||
'language' => $language,
|
||||
'libraryId' => $libraryId,
|
||||
'libraryName' => $libraryName,
|
||||
'libraryMajorVersion' => $libraryMajorVersion,
|
||||
'libraryMinorVersion' => $libraryMinorVersion,
|
||||
'libraryEmbedTypes' => $libraryEmbedTypes,
|
||||
'libraryFullscreen' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function loadContentDependencies($id, $type = null): array
|
||||
{
|
||||
$h5pImportLibraries = $this->h5pImportLibraries;
|
||||
$dependencies = [];
|
||||
|
||||
/** @var H5pImportLibrary|null $library */
|
||||
foreach ($h5pImportLibraries as $library) {
|
||||
$dependencies[] = [
|
||||
'libraryId' => $library->getIid(),
|
||||
'machineName' => $library->getMachineName(),
|
||||
'majorVersion' => $library->getMajorVersion(),
|
||||
'minorVersion' => $library->getMinorVersion(),
|
||||
'patchVersion' => $library->getPatchVersion(),
|
||||
'preloadedJs' => $library->getPreloadedJsFormatted(),
|
||||
'preloadedCss' => $library->getPreloadedCssFormatted(),
|
||||
];
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
public function getOption($name, $default = null)
|
||||
{
|
||||
return api_get_course_plugin_setting('h5pimport', $name);
|
||||
}
|
||||
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
// TODO: Implement setOption() method.
|
||||
}
|
||||
|
||||
public function updateContentFields($id, $fields)
|
||||
{
|
||||
// TODO: Implement updateContentFields() method.
|
||||
}
|
||||
|
||||
public function clearFilteredParameters($library_ids)
|
||||
{
|
||||
// TODO: Implement clearFilteredParameters() method.
|
||||
}
|
||||
|
||||
public function getNumNotFiltered()
|
||||
{
|
||||
// TODO: Implement getNumNotFiltered() method.
|
||||
}
|
||||
|
||||
public function getNumContent($libraryId, $skip = null)
|
||||
{
|
||||
// TODO: Implement getNumContent() method.
|
||||
}
|
||||
|
||||
public function isContentSlugAvailable($slug)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getLibraryStats($type)
|
||||
{
|
||||
// TODO: Implement getLibraryStats() method.
|
||||
}
|
||||
|
||||
public function getNumAuthors()
|
||||
{
|
||||
// TODO: Implement getNumAuthors() method.
|
||||
}
|
||||
|
||||
public function saveCachedAssets($key, $libraries)
|
||||
{
|
||||
// TODO: Implement saveCachedAssets() method.
|
||||
}
|
||||
|
||||
public function deleteCachedAssets($library_id)
|
||||
{
|
||||
// TODO: Implement deleteCachedAssets() method.
|
||||
}
|
||||
|
||||
public function getLibraryContentCount()
|
||||
{
|
||||
// TODO: Implement getLibraryContentCount() method.
|
||||
}
|
||||
|
||||
public function afterExportCreated($content, $filename)
|
||||
{
|
||||
// TODO: Implement afterExportCreated() method.
|
||||
}
|
||||
|
||||
public function hasPermission($permission, $id = null)
|
||||
{
|
||||
// TODO: Implement hasPermission() method.
|
||||
}
|
||||
|
||||
public function replaceContentTypeCache($contentTypeCache)
|
||||
{
|
||||
// TODO: Implement replaceContentTypeCache() method.
|
||||
}
|
||||
|
||||
public function libraryHasUpgrade($library)
|
||||
{
|
||||
// TODO: Implement libraryHasUpgrade() method.
|
||||
}
|
||||
}
|
||||
78
plugin/h5pimport/src/H5pPackageImporter.php
Normal file
78
plugin/h5pimport/src/H5pPackageImporter.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
|
||||
/**
|
||||
* Class H5pPackageImporter.
|
||||
*/
|
||||
abstract class H5pPackageImporter
|
||||
{
|
||||
/**
|
||||
* @var Course
|
||||
*/
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* Path to course directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $courseDirectoryPath;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $packageFileInfo;
|
||||
|
||||
/**
|
||||
* The package type is usually a MIME type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $packageType;
|
||||
protected $h5pJsonContent;
|
||||
|
||||
/**
|
||||
* H5pPackageImporter constructor.
|
||||
*/
|
||||
protected function __construct(array $fileInfo, Course $course)
|
||||
{
|
||||
$this->packageFileInfo = $fileInfo;
|
||||
$this->course = $course;
|
||||
$this->courseDirectoryPath = api_get_path(SYS_COURSE_PATH).$this->course->getDirectory();
|
||||
$this->packageType = $fileInfo['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create(array $fileInfo, Course $course): ZipPackageImporter
|
||||
{
|
||||
if (
|
||||
'application/octet-stream' !== $fileInfo['type']
|
||||
&& 'h5p' !== pathinfo($fileInfo['name'], PATHINFO_EXTENSION)
|
||||
) {
|
||||
throw new \Exception('Not a H5P package');
|
||||
}
|
||||
|
||||
return new ZipPackageImporter($fileInfo, $course);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the package and unzip it, checking if it has the 'h5p.json' file or some php script.
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function import(): string;
|
||||
|
||||
public function getPackageType(): string
|
||||
{
|
||||
return $this->packageType;
|
||||
}
|
||||
}
|
||||
327
plugin/h5pimport/src/H5pPackageTools.php
Normal file
327
plugin/h5pimport/src/H5pPackageTools.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Chamilo\CoreBundle\Entity\Course;
|
||||
use Chamilo\CoreBundle\Entity\Session;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary;
|
||||
use Database;
|
||||
use H5PCore;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
class H5pPackageTools
|
||||
{
|
||||
/**
|
||||
* Help read JSON from the archive.
|
||||
*
|
||||
* @return mixed JSON content if valid or FALSE for invalid
|
||||
*/
|
||||
public static function getJson(string $file, bool $assoc = false)
|
||||
{
|
||||
$fs = new Filesystem();
|
||||
$json = false;
|
||||
|
||||
if ($fs->exists($file)) {
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
// Decode the data
|
||||
$json = json_decode($contents, $assoc);
|
||||
if (null === $json) {
|
||||
// JSON cannot be decoded or the recursion limit has been reached.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the integrity of an H5P package by verifying the existence of libraries
|
||||
* and moves them to the "libraries" directory.
|
||||
*
|
||||
* @param object $h5pJson the H5P JSON object
|
||||
* @param string $extractedDir the path to the extracted directory
|
||||
*
|
||||
* @return bool true if the package integrity is valid, false otherwise
|
||||
*/
|
||||
public static function checkPackageIntegrity(object $h5pJson, string $extractedDir): bool
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$h5pDir = dirname($extractedDir, 2);
|
||||
$sharedLibrariesDir = $h5pDir.'/libraries';
|
||||
|
||||
// Move 'content' directory one level back (H5P specification)
|
||||
$filesystem->mirror($extractedDir.'/content', $extractedDir, null, ['override' => true]);
|
||||
$filesystem->remove($extractedDir.'/content');
|
||||
// Get the list of preloaded dependencies
|
||||
$preloadedDependencies = $h5pJson->preloadedDependencies;
|
||||
|
||||
// Check the existence of each library in the extracted directory
|
||||
foreach ($preloadedDependencies as $dependency) {
|
||||
$libraryName = $dependency->machineName;
|
||||
$majorVersion = $dependency->majorVersion;
|
||||
$minorVersion = $dependency->minorVersion;
|
||||
|
||||
$libraryFolderName = api_replace_dangerous_char($libraryName.'-'.$majorVersion.'.'.$minorVersion);
|
||||
$libraryPath = $extractedDir.'/'.$libraryFolderName;
|
||||
|
||||
// Check if the library folder exists
|
||||
if (!$filesystem->exists($libraryPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the entire folder to the "libraries" directory
|
||||
$targetPath = $sharedLibrariesDir.'/'.$libraryFolderName;
|
||||
|
||||
$filesystem->rename($libraryPath, $targetPath, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the H5P package information in the database.
|
||||
*
|
||||
* @param string $packagePath the path to the H5P package file
|
||||
* @param object $h5pJson the parsed H5P JSON object
|
||||
* @param Course $course the course entity related to the package
|
||||
* @param Session|null $session the session entity related to the package
|
||||
* @param array|null $values the advance options in upload form
|
||||
*/
|
||||
public static function storeH5pPackage(
|
||||
string $packagePath,
|
||||
object $h5pJson,
|
||||
Course $course,
|
||||
Session $session = null,
|
||||
array $values = null
|
||||
) {
|
||||
$entityManager = \Database::getManager();
|
||||
// Go back 2 directories
|
||||
$h5pDir = dirname($packagePath, 2);
|
||||
$sharedLibrariesDir = $h5pDir.'/libraries';
|
||||
|
||||
$mainLibraryName = $h5pJson->mainLibrary;
|
||||
$relativePath = api_get_path(REL_COURSE_PATH).$course->getDirectory().'/h5p/';
|
||||
|
||||
$h5pImport = new H5pImport();
|
||||
$h5pImport->setName($h5pJson->title);
|
||||
$h5pImport->setPath($packagePath);
|
||||
if ($values) {
|
||||
$h5pImport->setDescription($values['description']);
|
||||
}
|
||||
$h5pImport->setRelativePath($relativePath);
|
||||
$h5pImport->setCourse($course);
|
||||
$h5pImport->setSession($session);
|
||||
$entityManager->persist($h5pImport);
|
||||
|
||||
$libraries = $h5pJson->preloadedDependencies;
|
||||
|
||||
foreach ($libraries as $libraryData) {
|
||||
$library = $entityManager
|
||||
->getRepository(H5pImportLibrary::class)
|
||||
->findOneBy(
|
||||
[
|
||||
'machineName' => $libraryData->machineName,
|
||||
'majorVersion' => $libraryData->majorVersion,
|
||||
'minorVersion' => $libraryData->minorVersion,
|
||||
'course' => $course,
|
||||
]
|
||||
)
|
||||
;
|
||||
|
||||
if (null === $library) {
|
||||
$auxFullName = $libraryData->machineName.'-'.$libraryData->majorVersion.'.'.$libraryData->minorVersion;
|
||||
$libraryOwnJson = self::getJson($sharedLibrariesDir.'/'.$auxFullName.'/library.json');
|
||||
|
||||
$library = new H5pImportLibrary();
|
||||
$library->setMachineName($libraryData->machineName);
|
||||
$library->setTitle($libraryOwnJson->title);
|
||||
$library->setMajorVersion($libraryData->majorVersion);
|
||||
$library->setMinorVersion($libraryData->minorVersion);
|
||||
$library->setPatchVersion($libraryOwnJson->patchVersion);
|
||||
$library->setRunnable($libraryOwnJson->runnable);
|
||||
$library->setEmbedTypes($libraryOwnJson->embedTypes);
|
||||
$library->setPreloadedJs($libraryOwnJson->preloadedJs);
|
||||
$library->setPreloadedCss($libraryOwnJson->preloadedCss);
|
||||
$library->setLibraryPath($sharedLibrariesDir.'/'.$auxFullName);
|
||||
$library->setCourse($course);
|
||||
$entityManager->persist($library);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
$h5pImport->addLibraries($library);
|
||||
if ($mainLibraryName === $libraryData->machineName) {
|
||||
$h5pImport->setMainLibrary($library);
|
||||
}
|
||||
$entityManager->persist($h5pImport);
|
||||
$entityManager->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an H5P package from the database and the disk.
|
||||
*
|
||||
* @param H5pImport $h5pImport the H5P import entity representing the package to delete
|
||||
*
|
||||
* @return bool true if the package was successfully deleted, false otherwise
|
||||
*/
|
||||
public static function deleteH5pPackage(H5pImport $h5pImport): bool
|
||||
{
|
||||
$packagePath = $h5pImport->getPath();
|
||||
$entityManager = \Database::getManager();
|
||||
$entityManager->remove($h5pImport);
|
||||
$entityManager->flush();
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
if ($filesystem->exists($packagePath)) {
|
||||
try {
|
||||
$filesystem->remove($packagePath);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core settings for H5P content.
|
||||
*
|
||||
* @param H5pImport $h5pImport the H5pImport object
|
||||
* @param \H5PCore $h5pCore the H5PCore object
|
||||
*
|
||||
* @return array the core settings for H5P content
|
||||
*/
|
||||
public static function getCoreSettings(H5pImport $h5pImport, H5PCore $h5pCore): array
|
||||
{
|
||||
$originIsLearnpath = 'learnpath' === api_get_origin();
|
||||
|
||||
$settings = [
|
||||
'baseUrl' => api_get_path(WEB_PATH),
|
||||
'url' => $h5pImport->getRelativePath(),
|
||||
'postUserStatistics' => true,
|
||||
'ajax' => [
|
||||
'setFinished' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=set_finished&h5pId='.$h5pImport->getIid().'&learnpath='.$originIsLearnpath.'&token='.\H5PCore::createToken('result'),
|
||||
'contentUserData' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=content_user_data&h5pId='.$h5pImport->getIid().'&token='.\H5PCore::createToken('content'),
|
||||
],
|
||||
'saveFreq' => false,
|
||||
'l10n' => [
|
||||
'H5P' => $h5pCore->getLocalization(),
|
||||
],
|
||||
// 'hubIsEnabled' => variable_get('h5p_hub_is_enabled', TRUE) ? TRUE : FALSE,
|
||||
'crossorigin' => false,
|
||||
// 'crossoriginCacheBuster' => variable_get('h5p_crossorigin_cache_buster', NULL),
|
||||
// 'libraryConfig' => $core->h5pF->getLibraryConfig(),
|
||||
'pluginCacheBuster' => '?0',
|
||||
'libraryUrl' => $h5pImport->getMainLibrary()->getLibraryPath().'/js',
|
||||
];
|
||||
|
||||
$loggedUser = api_get_user_info();
|
||||
if ($loggedUser) {
|
||||
$settings['user'] = [
|
||||
'name' => $loggedUser['complete_name'],
|
||||
'mail' => $loggedUser['email'],
|
||||
];
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the core assets.
|
||||
*
|
||||
* @return array[]|bool an array containing CSS and JS assets or false if some core assets missing
|
||||
*/
|
||||
public static function getCoreAssets()
|
||||
{
|
||||
$assets = [
|
||||
'css' => [],
|
||||
'js' => [],
|
||||
];
|
||||
|
||||
// Add CSS assets
|
||||
foreach (\H5PCore::$styles as $style) {
|
||||
$auxAssetPath = 'vendor/h5p/h5p-core/'.$style;
|
||||
$assets['css'][] = api_get_path(WEB_PATH).$auxAssetPath;
|
||||
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add JS assets
|
||||
foreach (\H5PCore::$scripts as $script) {
|
||||
$auxAssetPath = 'vendor/h5p/h5p-core/'.$script;
|
||||
$auxUrl = api_get_path(WEB_PATH).$auxAssetPath;
|
||||
$assets['js'][] = $auxUrl;
|
||||
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content body for the H5PIntegration javascript object.
|
||||
*
|
||||
* @param mixed $h5pNode
|
||||
*/
|
||||
public static function getContentSettings($h5pNode, H5PCore $h5pCore): array
|
||||
{
|
||||
$filtered = $h5pCore->filterParameters($h5pNode);
|
||||
$contentUserData = [
|
||||
0 => [
|
||||
'state' => '{}',
|
||||
],
|
||||
];
|
||||
|
||||
// ToDo Use $h5pCore->getDisplayOptionsForView() function
|
||||
$displayOptions = [
|
||||
'frame' => api_get_course_plugin_setting('h5pimport', 'frame'),
|
||||
'embed' => api_get_course_plugin_setting('h5pimport', 'embed'),
|
||||
'copyright' => api_get_course_plugin_setting('h5pimport', 'copyright'),
|
||||
'icon' => api_get_course_plugin_setting('h5pimport', 'icon'),
|
||||
];
|
||||
|
||||
return [
|
||||
'library' => \H5PCore::libraryToString($h5pNode['library']),
|
||||
'jsonContent' => $h5pNode['params'],
|
||||
'fullScreen' => $h5pNode['library']['fullscreen'],
|
||||
'exportUrl' => '',
|
||||
'language' => 'en',
|
||||
'filtered' => $filtered,
|
||||
'embedCode' => '<iframe src="'.api_get_course_url().'h5p/embed/'.$h5pNode['mainId'].'" width=":w" height=":h" frameborder="0" allowfullscreen="allowfullscreen" allow="geolocation *; microphone *; camera *; midi *; encrypted-media *" title="'.$h5pNode['title'].'"></iframe>',
|
||||
'resizeCode' => '',
|
||||
'mainId' => $h5pNode['mainId'],
|
||||
'url' => $h5pNode['url'],
|
||||
'contentUserData' => $contentUserData,
|
||||
'displayOptions' => $displayOptions,
|
||||
'metadata' => $h5pNode['metadata'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert H5P dependencies to a library list.
|
||||
*
|
||||
* @param array $dependencies the H5P dependencies
|
||||
*
|
||||
* @return array the library list with machine names as keys and version information as values
|
||||
*/
|
||||
public static function h5pDependenciesToLibraryList(array $dependencies): array
|
||||
{
|
||||
$libraryList = [];
|
||||
|
||||
foreach ($dependencies as $dependency) {
|
||||
$libraryList[$dependency['machineName']] = [
|
||||
'majorVersion' => $dependency['majorVersion'],
|
||||
'minorVersion' => $dependency['minorVersion'],
|
||||
];
|
||||
}
|
||||
|
||||
return $libraryList;
|
||||
}
|
||||
}
|
||||
183
plugin/h5pimport/src/ZipPackageImporter.php
Normal file
183
plugin/h5pimport/src/ZipPackageImporter.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
namespace Chamilo\PluginBundle\H5pImport\H5pImporter;
|
||||
|
||||
use Exception;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* Class ZipPackageImporter.
|
||||
*/
|
||||
class ZipPackageImporter extends H5pPackageImporter
|
||||
{
|
||||
/*
|
||||
* Allowed file extensions
|
||||
* List obtained from H5P: https://h5p.org/allowed-file-extensions
|
||||
* */
|
||||
private const ALLOWED_EXTENSIONS = [
|
||||
'json',
|
||||
'png',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'bmp',
|
||||
'tif',
|
||||
'tiff',
|
||||
'svg',
|
||||
'eot',
|
||||
'ttf',
|
||||
'woff',
|
||||
'woff2',
|
||||
'otf',
|
||||
'webm',
|
||||
'mp4',
|
||||
'ogg',
|
||||
'mp3',
|
||||
'm4a',
|
||||
'wav',
|
||||
'txt',
|
||||
'pdf',
|
||||
'rtf',
|
||||
'doc',
|
||||
'docx',
|
||||
'xls',
|
||||
'xlsx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'odt',
|
||||
'ods',
|
||||
'odp',
|
||||
'xml',
|
||||
'csv',
|
||||
'diff',
|
||||
'patch',
|
||||
'swf',
|
||||
'md',
|
||||
'textile',
|
||||
'vtt',
|
||||
'webvtt',
|
||||
'gltf',
|
||||
'gl',
|
||||
'js',
|
||||
'css',
|
||||
];
|
||||
|
||||
/**
|
||||
* Import an H5P package. No DB change.
|
||||
*
|
||||
* @throws Exception When the H5P package is invalid.
|
||||
*
|
||||
* @return string The path to the extracted package directory.
|
||||
*/
|
||||
public function import(): string
|
||||
{
|
||||
$zipFile = new \PclZip($this->packageFileInfo['tmp_name']);
|
||||
$zipContent = $zipFile->listContent();
|
||||
|
||||
if ($this->validateH5pPackageContent($zipContent)) {
|
||||
$packageSize = array_reduce(
|
||||
$zipContent,
|
||||
function ($accumulator, $zipEntry) {
|
||||
return $accumulator + $zipEntry['size'];
|
||||
}
|
||||
);
|
||||
|
||||
$this->validateEnoughSpace($packageSize);
|
||||
|
||||
$pathInfo = pathinfo($this->packageFileInfo['name']);
|
||||
|
||||
$packageDirectoryPath = $this->generatePackageDirectory($pathInfo['filename']);
|
||||
$zipFile->extract($packageDirectoryPath);
|
||||
|
||||
return "{$packageDirectoryPath}";
|
||||
}
|
||||
|
||||
throw new Exception('Invalid H5P package');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 store package.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an H5P package.
|
||||
* Check if 'h5p.json' or 'content/content.json' files exist
|
||||
* and if the files are in a file whitelist (ALLOWED_EXTENSIONS).
|
||||
*
|
||||
* @param array $h5pPackageContent the content of the H5P package
|
||||
*
|
||||
* @return bool whether the H5P package is valid or not
|
||||
*/
|
||||
private function validateH5pPackageContent(array $h5pPackageContent): bool
|
||||
{
|
||||
$validPackage = false;
|
||||
|
||||
if (!empty($h5pPackageContent)) {
|
||||
foreach ($h5pPackageContent as $content) {
|
||||
$filename = $content['filename'];
|
||||
|
||||
if (0 !== preg_match('/(^[\._]|\/[\._]|\\\[\._])/', $filename)) {
|
||||
// Skip any file or folder starting with a . or _
|
||||
continue;
|
||||
}
|
||||
|
||||
$fileExtension = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
|
||||
if (in_array($fileExtension, self::ALLOWED_EXTENSIONS)) {
|
||||
$validPackage = 'h5p.json' === $filename || 'content/content.json' === $filename;
|
||||
if ($validPackage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $validPackage;
|
||||
}
|
||||
|
||||
private function generatePackageDirectory(string $name): string
|
||||
{
|
||||
$baseDirectory = $this->courseDirectoryPath.'/h5p/content/';
|
||||
$safeName = api_replace_dangerous_char($name);
|
||||
$directoryPath = $baseDirectory.$safeName;
|
||||
|
||||
$fs = new Filesystem();
|
||||
|
||||
if ($fs->exists($directoryPath)) {
|
||||
$counter = 1;
|
||||
|
||||
// Add numeric suffix to the name until a unique directory name is found
|
||||
while ($fs->exists($directoryPath)) {
|
||||
$modifiedName = $safeName.'_'.$counter;
|
||||
$directoryPath = $baseDirectory.$modifiedName;
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
$fs->mkdir(
|
||||
$directoryPath,
|
||||
api_get_permissions_for_new_directories()
|
||||
);
|
||||
|
||||
$sharedLibrariesDir = $this->courseDirectoryPath.'/h5p/libraries';
|
||||
|
||||
if (!$fs->exists($sharedLibrariesDir)) {
|
||||
$fs->mkdir(
|
||||
$sharedLibrariesDir,
|
||||
api_get_permissions_for_new_directories()
|
||||
);
|
||||
}
|
||||
|
||||
return $directoryPath;
|
||||
}
|
||||
}
|
||||
90
plugin/h5pimport/src/ajax.php
Normal file
90
plugin/h5pimport/src/ajax.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
use Chamilo\CourseBundle\Entity\CLpItem;
|
||||
use Chamilo\CourseBundle\Entity\CLpItemView;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportResults;
|
||||
use ChamiloSession as Session;
|
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? null;
|
||||
$h5pId = isset($_REQUEST['h5pId']) ? intval($_REQUEST['h5pId']) : 0;
|
||||
|
||||
$course = api_get_course_entity(api_get_course_int_id());
|
||||
$session = api_get_session_entity(api_get_session_id());
|
||||
|
||||
$plugin = H5pImportPlugin::create();
|
||||
$em = Database::getManager();
|
||||
$h5pImportRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImport');
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
|
||||
if ('set_finished' === $action && 0 !== $h5pId) {
|
||||
if (!H5PCore::validToken('result', filter_input(INPUT_GET, 'token'))) {
|
||||
H5PCore::ajaxError($plugin->get_lang('h5p_error_invalid_token'));
|
||||
}
|
||||
|
||||
if (is_numeric($_POST['score']) && is_numeric($_POST['maxScore'])) {
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $h5pImportRepo->find($h5pId);
|
||||
$entityManager = Database::getManager();
|
||||
|
||||
$h5pImportResults = new H5pImportResults();
|
||||
$h5pImportResults->setH5pImport($h5pImport);
|
||||
$h5pImportResults->setCourse($course);
|
||||
$h5pImportResults->setSession($session);
|
||||
$h5pImportResults->setUser($user);
|
||||
$h5pImportResults->setScore((int) $_POST['score']);
|
||||
$h5pImportResults->setMaxScore((int) $_POST['maxScore']);
|
||||
$h5pImportResults->setStartTime((int) $_POST['opened']);
|
||||
$h5pImportResults->setTotalTime(time() - $_POST['opened']);
|
||||
|
||||
$entityManager->persist($h5pImportResults);
|
||||
|
||||
// If it comes from an LP, update in c_lp_item_view
|
||||
if (1 == $_REQUEST['learnpath'] && Session::has('oLP')) {
|
||||
$lpObject = Session::read('oLP');
|
||||
$clpItemViewRepo = $em->getRepository('ChamiloCourseBundle:CLpItemView');
|
||||
|
||||
/** @var CLpItemView|null $lpItemView */
|
||||
$lpItemView = $clpItemViewRepo->findOneBy(
|
||||
[
|
||||
'lpViewId' => $lpObject->lp_view_id,
|
||||
'lpItemId' => $lpObject->current,
|
||||
]
|
||||
);
|
||||
|
||||
/** @var CLpItem|null $lpItem */
|
||||
$lpItem = $entityManager->find('ChamiloCourseBundle:CLpItem', $lpItemView->getLpItemId());
|
||||
if ('h5p' !== $lpItem->getItemType()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lpItemView->setScore($_POST['score']);
|
||||
$lpItemView->setMaxScore($_POST['maxScore']);
|
||||
$lpItemView->setStatus('completed');
|
||||
$lpItemView->setTotalTime($lpItemView->getTotalTime() + $h5pImportResults->getTotalTime());
|
||||
$lpItem->setMaxScore($_POST['maxScore']);
|
||||
$h5pImportResults->setCLpItemView($lpItemView);
|
||||
$entityManager->persist($h5pImportResults);
|
||||
$entityManager->persist($lpItem);
|
||||
$entityManager->persist($lpItemView);
|
||||
}
|
||||
$entityManager->flush();
|
||||
|
||||
H5PCore::ajaxSuccess();
|
||||
} else {
|
||||
H5PCore::ajaxError();
|
||||
}
|
||||
} elseif ('content_user_data' === $action && 0 !== $h5pId) {
|
||||
if (!H5PCore::validToken('content', filter_input(INPUT_GET, 'token'))) {
|
||||
H5PCore::ajaxError($plugin->get_lang('h5p_error_invalid_token'));
|
||||
}
|
||||
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $h5pImportRepo->find($h5pId);
|
||||
} else {
|
||||
H5PCore::ajaxError(get_lang('InvalidAction'));
|
||||
}
|
||||
240
plugin/h5pimport/start.php
Normal file
240
plugin/h5pimport/start.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\H5pImport\H5pImporter\H5pPackageImporter;
|
||||
use Chamilo\PluginBundle\H5pImport\H5pImporter\H5pPackageTools;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
api_block_anonymous_users();
|
||||
api_protect_course_script(true);
|
||||
$cidReq = api_get_cidreq();
|
||||
$pluginIndex = "./start.php?$cidReq";
|
||||
$plugin = H5pImportPlugin::create();
|
||||
|
||||
if ('false' === $plugin->get('tool_enable')) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
|
||||
$isAllowedToEdit = api_is_allowed_to_edit(true);
|
||||
|
||||
$action = $_REQUEST['action'] ?? null;
|
||||
|
||||
$em = Database::getManager();
|
||||
$h5pRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImport');
|
||||
$h5pResultsRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImportResults');
|
||||
|
||||
$course = api_get_course_entity(api_get_course_int_id());
|
||||
$session = api_get_session_entity(api_get_session_id());
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
|
||||
$actions = [];
|
||||
|
||||
$view = new Template($plugin->getToolTitle());
|
||||
$view->assign('is_allowed_to_edit', $isAllowedToEdit);
|
||||
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
if (!$isAllowedToEdit) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
|
||||
$actions[] = Display::url(
|
||||
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
|
||||
api_get_self()
|
||||
);
|
||||
|
||||
// Set the max upload size in php.ini in the file uploader
|
||||
$maxFileSize = getIniMaxFileSizeInBytes();
|
||||
$form = new FormValidator('frm_edit');
|
||||
$form->addFile('file', $plugin->get_lang('h5p_package'), ['accept' => '.h5p']);
|
||||
$form->addRule(
|
||||
'file',
|
||||
'The file size cannot exceed: '.$maxFileSize.' bytes',
|
||||
'maxfilesize',
|
||||
$maxFileSize,
|
||||
'client'
|
||||
);
|
||||
$form->addButtonAdvancedSettings('advanced_params');
|
||||
$form->addHtml('<div id="advanced_params_options" style="display:none">');
|
||||
$form->addTextarea('description', get_lang('Description'));
|
||||
$form->applyFilter('description', 'trim');
|
||||
$form->addHtml('</div>');
|
||||
$form->addButtonUpdate(get_lang('Add'));
|
||||
$form->addHidden('action', 'add');
|
||||
$form->protect();
|
||||
|
||||
if ($form->validate()) {
|
||||
$values = $form->exportValues();
|
||||
|
||||
$zipFileInfo = $_FILES['file'];
|
||||
|
||||
try {
|
||||
$importer = H5pPackageImporter::create($zipFileInfo, $course);
|
||||
$packageFile = $importer->import();
|
||||
|
||||
// Get the h5p.json and content.json contents
|
||||
$h5pJson = H5pPackageTools::getJson($packageFile.DIRECTORY_SEPARATOR.'h5p.json');
|
||||
$contentJson = H5pPackageTools::getJson(
|
||||
$packageFile.DIRECTORY_SEPARATOR.'content'.DIRECTORY_SEPARATOR.'content.json'
|
||||
);
|
||||
if ($h5pJson && $contentJson) {
|
||||
if (H5pPackageTools::checkPackageIntegrity($h5pJson, $packageFile)) {
|
||||
H5pPackageTools::storeH5pPackage($packageFile, $h5pJson, $course, $session, $values);
|
||||
|
||||
Display::addFlash(
|
||||
Display::return_message(get_lang('Added'), 'success')
|
||||
);
|
||||
} else {
|
||||
Display::addFlash(
|
||||
Display::return_message(get_lang('Error'), 'error')
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
header('Location: '.api_get_self());
|
||||
}
|
||||
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
Display::addFlash(
|
||||
Display::return_message($e->getMessage(), 'error')
|
||||
);
|
||||
|
||||
header("Location: $pluginIndex");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$view->assign('header', $plugin->get_lang('import_h5p_package'));
|
||||
$view->assign('form', $form->returnForm());
|
||||
|
||||
break;
|
||||
case 'delete':
|
||||
$h5pImportId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0;
|
||||
|
||||
if (!$h5pImportId && !Security::check_token('get')) {
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $h5pRepo->find($h5pImportId);
|
||||
|
||||
if (!$h5pImport) {
|
||||
Display::addFlash(Display::return_message($plugin->get_lang('ContentNotFound'), 'danger'));
|
||||
|
||||
break;
|
||||
}
|
||||
if (H5pPackageTools::deleteH5pPackage($h5pImport)) {
|
||||
Display::addFlash(
|
||||
Display::return_message(get_lang('Deleted'), 'success')
|
||||
);
|
||||
} else {
|
||||
Display::addFlash(
|
||||
Display::return_message(get_lang('Error'), 'danger')
|
||||
);
|
||||
}
|
||||
|
||||
header('Location: '.api_get_self());
|
||||
exit;
|
||||
default:
|
||||
|
||||
/** @var array|H5pImport[] $h5pImports */
|
||||
$h5pImports = $h5pRepo->findBy(['course' => $course, 'session' => $session]);
|
||||
|
||||
$tableData = [];
|
||||
/** @var H5pImport $h5pImport */
|
||||
foreach ($h5pImports as $h5pImport) {
|
||||
$h5pImportsResults = $h5pResultsRepo->count(
|
||||
[
|
||||
'course' => $course,
|
||||
'session' => $session,
|
||||
'user' => $user,
|
||||
'h5pImport' => $h5pImport,
|
||||
]
|
||||
);
|
||||
$data = [
|
||||
Display::url(
|
||||
$h5pImport->getName(),
|
||||
$plugin->getViewUrl($h5pImport)
|
||||
),
|
||||
$h5pImport->getDescription(),
|
||||
$h5pImportsResults,
|
||||
];
|
||||
|
||||
if ($isAllowedToEdit) {
|
||||
$data[] = $h5pImport;
|
||||
}
|
||||
|
||||
$tableData[] = $data;
|
||||
}
|
||||
|
||||
if ($isAllowedToEdit) {
|
||||
$btnAdd = Display::toolbarButton(
|
||||
get_lang('Upload'),
|
||||
api_get_self().'?action=add',
|
||||
'file-code-o',
|
||||
'primary'
|
||||
);
|
||||
|
||||
$view->assign(
|
||||
'actions',
|
||||
Display::toolbarAction($plugin->get_name(), [$btnAdd])
|
||||
);
|
||||
|
||||
if (in_array($action, ['add', 'edit'])) {
|
||||
$view->assign('form', $form->returnForm());
|
||||
}
|
||||
}
|
||||
|
||||
$table = new SortableTableFromArray($tableData, 0);
|
||||
$table->set_header(0, get_lang('Title'));
|
||||
$table->set_header(1, get_lang('Description'));
|
||||
$table->set_header(2, $plugin->get_lang('attempts'));
|
||||
|
||||
if ($isAllowedToEdit) {
|
||||
$table->set_header(
|
||||
3,
|
||||
get_lang('Actions'),
|
||||
false,
|
||||
'th-header text-right',
|
||||
['class' => 'text-right']
|
||||
);
|
||||
$table->set_column_filter(
|
||||
3,
|
||||
function (H5pImport $value) use ($isAllowedToEdit) {
|
||||
$actions = [];
|
||||
|
||||
if ($isAllowedToEdit) {
|
||||
$actions[] = Display::url(
|
||||
Display::return_icon('delete.png', get_lang('Delete')),
|
||||
api_get_self().'?'.http_build_query([
|
||||
'action' => 'delete',
|
||||
'id' => $value->getIid(),
|
||||
'sec_token' => Security::getTokenFromSession(),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $actions);
|
||||
}
|
||||
);
|
||||
}
|
||||
$view->assign('h5pImports', $h5pImports);
|
||||
$view->assign('table', $table->return_table());
|
||||
|
||||
}
|
||||
|
||||
$content = $view->fetch('h5pimport/view/index.tpl');
|
||||
if ($actions) {
|
||||
$actions = implode(PHP_EOL, $actions);
|
||||
|
||||
$view->assign(
|
||||
'actions',
|
||||
Display::toolbarAction($plugin->get_name(), [$actions])
|
||||
);
|
||||
}
|
||||
|
||||
$view->assign('content', $content);
|
||||
$view->display_one_col_template();
|
||||
6
plugin/h5pimport/uninstall.php
Normal file
6
plugin/h5pimport/uninstall.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
// For license terms, see /license.txt
|
||||
|
||||
require_once __DIR__.'/config.php';
|
||||
H5pImportPlugin::create()->uninstall();
|
||||
189
plugin/h5pimport/view.php
Normal file
189
plugin/h5pimport/view.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
// For licensing terms, see /license.txt
|
||||
|
||||
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport;
|
||||
use Chamilo\PluginBundle\H5pImport\H5pImporter\H5pImplementation;
|
||||
use Chamilo\PluginBundle\H5pImport\H5pImporter\H5pPackageTools;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
api_block_anonymous_users();
|
||||
api_protect_course_script(true);
|
||||
|
||||
$plugin = H5pImportPlugin::create();
|
||||
|
||||
if ('false' === $plugin->get('tool_enabled')) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
|
||||
$isAllowedToEdit = api_is_allowed_to_edit(true);
|
||||
|
||||
$em = Database::getManager();
|
||||
$embedRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImport');
|
||||
|
||||
$course = api_get_course_entity(api_get_course_int_id());
|
||||
$session = api_get_session_entity(api_get_session_id());
|
||||
|
||||
$h5pImportId = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0;
|
||||
$originIsLearnpath = 'learnpath' === api_get_origin();
|
||||
|
||||
if (!$h5pImportId) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
|
||||
/** @var H5pImport|null $h5pImport */
|
||||
$h5pImport = $embedRepo->find($h5pImportId);
|
||||
|
||||
if (!$h5pImport) {
|
||||
api_not_allowed(
|
||||
true,
|
||||
Display::return_message($plugin->get_lang('ContentNotFound'), 'danger')
|
||||
);
|
||||
}
|
||||
|
||||
if ($course->getId() !== $h5pImport->getCourse()->getId()) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
|
||||
if ($session && $h5pImport->getSession()) {
|
||||
if ($session->getId() !== $h5pImport->getSession()->getId()) {
|
||||
api_not_allowed(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$originIsLearnpath) {
|
||||
$interbreadcrumb[] = [
|
||||
'name' => $plugin->getToolTitle(),
|
||||
'url' => api_get_path(WEB_PLUGIN_PATH).$plugin->get_name().'/start.php',
|
||||
];
|
||||
|
||||
$actions = Display::url(
|
||||
Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM),
|
||||
api_get_path(WEB_PLUGIN_PATH).$plugin->get_name().'/start.php?'.api_get_cidreq()
|
||||
);
|
||||
}
|
||||
|
||||
$formTarget = $originIsLearnpath ? '_self' : '_blank';
|
||||
$htmlContent = '';
|
||||
if ($_REQUEST['view']) {
|
||||
$interface = new H5pImplementation($h5pImport);
|
||||
$h5pCore = new H5PCore(
|
||||
$interface,
|
||||
$h5pImport->getPath(),
|
||||
api_get_self(),
|
||||
'en',
|
||||
false
|
||||
);
|
||||
|
||||
$h5pNode = $h5pCore->loadContent($h5pImport->getIid());
|
||||
|
||||
if (empty($h5pNode)) {
|
||||
Display::addFlash(
|
||||
Display::return_message(get_lang('Error'), 'error')
|
||||
);
|
||||
} else {
|
||||
$coreAssets = H5pPackageTools::getCoreAssets();
|
||||
|
||||
if (!$coreAssets) {
|
||||
Display::addFlash(
|
||||
Display::return_message($plugin->get_lang('h5p_error_missing_core_asset'), 'danger')
|
||||
);
|
||||
} else {
|
||||
$htmlContent .= Display::div(
|
||||
['class' => 'exercise_overview_options']
|
||||
);
|
||||
$integration = H5pPackageTools::getCoreSettings($h5pImport, $h5pCore);
|
||||
$embedType = H5PCore::determineEmbedType($h5pNode['embedType'], $h5pNode['library']['embedTypes']);
|
||||
$integration['contents']['cid-'.$h5pNode['contentId']] =
|
||||
H5pPackageTools::getContentSettings($h5pNode, $h5pCore);
|
||||
|
||||
$preloadedDependencies = $h5pCore->loadContentDependencies($h5pNode['id'], 'preloaded');
|
||||
$files = $h5pCore->getDependenciesFiles(
|
||||
$preloadedDependencies,
|
||||
api_get_path(WEB_COURSE_PATH).$course->getDirectory().'/h5p'
|
||||
);
|
||||
$libraryList = H5pPackageTools::h5pDependenciesToLibraryList($preloadedDependencies);
|
||||
|
||||
foreach ($coreAssets['js'] as $script) {
|
||||
$htmlHeadXtra[] = api_get_js_simple($script);
|
||||
}
|
||||
foreach ($coreAssets['css'] as $style) {
|
||||
$htmlHeadXtra[] = api_get_css($style);
|
||||
}
|
||||
|
||||
if ('div' === $embedType) {
|
||||
foreach ($files['scripts'] as $script) {
|
||||
$htmlHeadXtra[] = api_get_js_simple($script->path.$script->version);
|
||||
$integration['loadedJs'] = $script->path.$script->version;
|
||||
}
|
||||
foreach ($files['styles'] as $script) {
|
||||
$htmlHeadXtra[] = api_get_css($script->path.$script->version);
|
||||
$integration['loadedCss'][] = $script->path.$script->version;
|
||||
}
|
||||
|
||||
$htmlContent = '<div class="h5p-content" data-content-id="'.$h5pNode['contentId'].'"></div>';
|
||||
} elseif ('iframe' === $embedType) {
|
||||
$integration['core']['scripts'] = $coreAssets['js'];
|
||||
$integration['core']['styles'] = $coreAssets['css'];
|
||||
$integration['contents']['cid-'.$h5pNode['contentId']]['styles'] =
|
||||
$h5pCore->getAssetsUrls($files['styles']);
|
||||
$integration['contents']['cid-'.$h5pNode['contentId']]['scripts'] =
|
||||
$h5pCore->getAssetsUrls($files['scripts']);
|
||||
|
||||
$htmlContent = '<div class="h5p-iframe-wrapper">
|
||||
<iframe
|
||||
id="h5p-iframe-'.$h5pNode['contentId'].'"
|
||||
class="h5p-iframe"
|
||||
data-content-id="'.$h5pNode['contentId'].'"
|
||||
style="height:1px"
|
||||
src="about:blank" frameBorder="0" scrolling="no"
|
||||
allowfullscreen="allowfullscreen"
|
||||
allow="geolocation *; microphone *; camera *; midi *; encrypted-media *"
|
||||
title="'.$h5pNode['title'].'">
|
||||
</iframe>
|
||||
</div>';
|
||||
}
|
||||
|
||||
if (!isset($htmlContent)) {
|
||||
Display::addFlash(
|
||||
Display::return_message($plugin->get_lang('h5p_error_loading'), 'danger')
|
||||
);
|
||||
} else {
|
||||
$htmlContent .= '<script> H5PIntegration = '.json_encode($integration).'</script>';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$frmNewAttempt = new FormValidator(
|
||||
'view',
|
||||
'post',
|
||||
$plugin->getViewUrl($h5pImport).'&view=1',
|
||||
'',
|
||||
['target' => $formTarget],
|
||||
FormValidator::LAYOUT_INLINE
|
||||
);
|
||||
$frmNewAttempt->addHidden('id', $h5pImport->getIid());
|
||||
$frmNewAttempt->addButton(
|
||||
'submit',
|
||||
$plugin->get_lang('start_attempt'),
|
||||
'external-link fa-fw',
|
||||
'success'
|
||||
);
|
||||
$htmlContent = Display::div(
|
||||
$frmNewAttempt->returnForm(),
|
||||
['class' => 'exercise_overview_options']
|
||||
);
|
||||
}
|
||||
|
||||
$view = new Template($h5pImport->getName());
|
||||
$view->assign('header', $h5pImport->getName());
|
||||
if (!$originIsLearnpath) {
|
||||
$view->assign('actions', Display::toolbarAction($plugin->get_name(), [$actions]));
|
||||
}
|
||||
$view->assign(
|
||||
'content',
|
||||
$htmlContent
|
||||
);
|
||||
|
||||
$view->display_one_col_template();
|
||||
6
plugin/h5pimport/view/index.tpl
Normal file
6
plugin/h5pimport/view/index.tpl
Normal file
@@ -0,0 +1,6 @@
|
||||
{% if is_allowed_to_edit %}
|
||||
{% if form is defined %}
|
||||
{{ form }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ table }}
|
||||
Reference in New Issue
Block a user