Actualización
This commit is contained in:
379
main/common_cartridge/import/src/base/CcBase.php
Normal file
379
main/common_cartridge/import/src/base/CcBase.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class CcBase
|
||||
{
|
||||
public const CC_TYPE_FORUM = 'imsdt_xmlv1p3';
|
||||
public const CC_TYPE_QUIZ = 'imsqti_xmlv1p3/imscc_xmlv1p3/assessment';
|
||||
public const CC_TYPE_QUESTION_BANK = 'imsqti_xmlv1p3/imscc_xmlv1p3/question-bank';
|
||||
public const CC_TYPE_WEBLINK = 'imswl_xmlv1p3';
|
||||
public const CC_TYPE_WEBCONTENT = 'webcontent';
|
||||
public const CC_TYPE_ASSOCIATED_CONTENT = 'associatedcontent/imscc_xmlv1p3/learning-application-resource';
|
||||
public const CC_TYPE_EMPTY = '';
|
||||
|
||||
public static $restypes = ['associatedcontent/imscc_xmlv1p0/learning-application-resource', 'webcontent'];
|
||||
public static $forumns = ['dt' => 'http://www.imsglobal.org/xsd/imsdt_v1p0'];
|
||||
public static $quizns = ['xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2'];
|
||||
public static $resourcens = ['wl' => 'http://www.imsglobal.org/xsd/imswl_v1p0'];
|
||||
|
||||
public static $instances = [];
|
||||
public static $manifest;
|
||||
public static $pathToManifestFolder;
|
||||
|
||||
public static $namespaces = ['imscc' => 'http://www.imsglobal.org/xsd/imscc/imscp_v1p1',
|
||||
'lomimscc' => 'http://ltsc.ieee.org/xsd/imscc/LOM',
|
||||
'lom' => 'http://ltsc.ieee.org/xsd/LOM',
|
||||
'voc' => 'http://ltsc.ieee.org/xsd/LOM/vocab',
|
||||
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'cc' => 'http://www.imsglobal.org/xsd/imsccauth_v1p0', ];
|
||||
|
||||
public function __construct($path_to_manifest)
|
||||
{
|
||||
static::$manifest = new DOMDocument();
|
||||
static::$manifest->validateOnParse = false;
|
||||
|
||||
static::$pathToManifestFolder = dirname($path_to_manifest);
|
||||
|
||||
static::logAction('Proccess start');
|
||||
static::logAction('Load the manifest file: '.$path_to_manifest);
|
||||
|
||||
if (!static::$manifest->load($path_to_manifest, LIBXML_NONET)) {
|
||||
static::logAction('Cannot load the manifest file: '.$path_to_manifest, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getquizns()
|
||||
{
|
||||
return static::$quizns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getforumns()
|
||||
{
|
||||
return static::$forumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getresourcens()
|
||||
{
|
||||
return static::$resourcens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the imsmanifest.xml file inside the given folder and return its path.
|
||||
*
|
||||
* @param string $folder Full path name of the folder in which we expect to find imsmanifest.xml
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public static function getManifest(string $folder)
|
||||
{
|
||||
if (!is_dir($folder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Before iterate over directories, try to find one manifest at top level
|
||||
if (file_exists($folder.'/imsmanifest.xml')) {
|
||||
return $folder.'/imsmanifest.xml';
|
||||
}
|
||||
|
||||
$result = false;
|
||||
try {
|
||||
$dirIter = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
|
||||
$recIter = new RecursiveIteratorIterator($dirIter, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
foreach ($recIter as $info) {
|
||||
if ($info->isFile() && ($info->getFilename() == 'imsmanifest.xml')) {
|
||||
$result = $info->getPathname();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isAuth()
|
||||
{
|
||||
$xpath = static::newxPath(static::$manifest, static::$namespaces);
|
||||
|
||||
$count_auth = $xpath->evaluate('count(/imscc:manifest/cc:authorizations)');
|
||||
|
||||
if ($count_auth > 0) {
|
||||
$response = true;
|
||||
} else {
|
||||
$response = false;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function getNodesByCriteria($key, $value)
|
||||
{
|
||||
$response = [];
|
||||
|
||||
if (array_key_exists('index', static::$instances)) {
|
||||
foreach (static::$instances['index'] as $item) {
|
||||
if ($item[$key] == $value) {
|
||||
$response[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function countInstances($type)
|
||||
{
|
||||
$quantity = 0;
|
||||
|
||||
if (array_key_exists('index', static::$instances)) {
|
||||
if (static::$instances['index'] && $type) {
|
||||
foreach (static::$instances['index'] as $instance) {
|
||||
if (!empty($instance['tool_type'])) {
|
||||
$types[] = $instance['tool_type'];
|
||||
}
|
||||
}
|
||||
|
||||
$quantityInstances = array_count_values($types);
|
||||
$quantity = array_key_exists($type, $quantityInstances) ? $quantityInstances[$type] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $quantity;
|
||||
}
|
||||
|
||||
public function getItemCcType($identifier)
|
||||
{
|
||||
$xpath = static::newxPath(static::$manifest, static::$namespaces);
|
||||
|
||||
$nodes = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]/@type');
|
||||
|
||||
if ($nodes && !empty($nodes->item(0)->nodeValue)) {
|
||||
return $nodes->item(0)->nodeValue;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function getItemHref($identifier)
|
||||
{
|
||||
$xpath = static::newxPath(static::$manifest, static::$namespaces);
|
||||
|
||||
$nodes = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]/imscc:file/@href');
|
||||
|
||||
if ($nodes && !empty($nodes->item(0)->nodeValue)) {
|
||||
return $nodes->item(0)->nodeValue;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public static function newxPath(DOMDocument $manifest, $namespaces = '')
|
||||
{
|
||||
$xpath = new DOMXPath($manifest);
|
||||
|
||||
if (!empty($namespaces)) {
|
||||
foreach ($namespaces as $prefix => $ns) {
|
||||
if (!$xpath->registerNamespace($prefix, $ns)) {
|
||||
static::logAction('Cannot register the namespace: '.$prefix.':'.$ns, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $xpath;
|
||||
}
|
||||
|
||||
public static function logFile()
|
||||
{
|
||||
return static::$pathToManifestFolder.DIRECTORY_SEPARATOR.'cc_import.log';
|
||||
}
|
||||
|
||||
public static function logAction($text, $criticalError = false)
|
||||
{
|
||||
$full_message = strtoupper(date("j/n/Y g:i:s a"))." - ".$text."\r";
|
||||
|
||||
file_put_contents(static::logFile(), $full_message, FILE_APPEND);
|
||||
|
||||
if ($criticalError) {
|
||||
static::criticalError($text);
|
||||
}
|
||||
}
|
||||
|
||||
public function convertToToolType($ccType)
|
||||
{
|
||||
$type = TYPE_UNKNOWN;
|
||||
|
||||
if ($ccType == static::CC_TYPE_FORUM) {
|
||||
$type = TOOL_TYPE_FORUM;
|
||||
}
|
||||
|
||||
if ($ccType == static::CC_TYPE_QUIZ) {
|
||||
$type = TOOL_TYPE_QUIZ;
|
||||
}
|
||||
|
||||
if ($ccType == static::CC_TYPE_WEBLINK) {
|
||||
$type = TOOL_TYPE_WEBLINK;
|
||||
}
|
||||
|
||||
if ($ccType == static::CC_TYPE_WEBCONTENT) {
|
||||
$type = TOOL_TYPE_DOCUMENT;
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
protected function getMetadata($section, $key)
|
||||
{
|
||||
$xpath = static::newxPath(static::$manifest, static::$namespaces);
|
||||
|
||||
$metadata = $xpath->query('/imscc:manifest/imscc:metadata/lomimscc:lom/lomimscc:'.$section.'/lomimscc:'.$key.'/lomimscc:string');
|
||||
$value = !empty($metadata->item(0)->nodeValue) ? $metadata->item(0)->nodeValue : '';
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is activity visible or not.
|
||||
*
|
||||
* @param string $identifier
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
protected function getModuleVisible($identifier)
|
||||
{
|
||||
//Should item be hidden or not
|
||||
$mod_visible = 1;
|
||||
if (!empty($identifier)) {
|
||||
$xpath = static::newxPath(static::$manifest, static::$namespaces);
|
||||
$query = '/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$identifier.'"]';
|
||||
$query .= '//lom:intendedEndUserRole/voc:vocabulary/lom:value';
|
||||
$intendeduserrole = $xpath->query($query);
|
||||
if (!empty($intendeduserrole) && ($intendeduserrole->length > 0)) {
|
||||
$role = trim($intendeduserrole->item(0)->nodeValue);
|
||||
if (strcasecmp('Instructor', $role) == 0) {
|
||||
$mod_visible = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mod_visible;
|
||||
}
|
||||
|
||||
protected function createInstances($items, $level = 0, &$array_index = 0, $index_root = 0)
|
||||
{
|
||||
$level++;
|
||||
$i = 1;
|
||||
|
||||
if ($items) {
|
||||
$xpath = self::newxPath(static::$manifest, static::$namespaces);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$array_index++;
|
||||
$title = $path = $tool_type = $identifierref = '';
|
||||
if ($item->nodeName == 'item') {
|
||||
if ($item->hasAttribute('identifierref')) {
|
||||
$identifierref = $item->getAttribute('identifierref');
|
||||
}
|
||||
|
||||
$titles = $xpath->query('imscc:title', $item);
|
||||
if ($titles->length > 0) {
|
||||
$title = $titles->item(0)->nodeValue;
|
||||
}
|
||||
|
||||
$ccType = $this->getItemCcType($identifierref);
|
||||
$tool_type = $this->convertToToolType($ccType);
|
||||
//Fix the label issue - MDL-33523
|
||||
if (empty($identifierref) && empty($title)) {
|
||||
$tool_type = TYPE_UNKNOWN;
|
||||
}
|
||||
} elseif ($item->nodeName == 'resource') {
|
||||
$identifierref = $xpath->query('@identifier', $item);
|
||||
$identifierref = !empty($identifierref->item(0)->nodeValue) ? $identifierref->item(0)->nodeValue : '';
|
||||
|
||||
$ccType = $this->getItemCcType($identifierref);
|
||||
$tool_type = $this->convertToToolType($ccType);
|
||||
if (self::CC_TYPE_WEBCONTENT == $ccType) {
|
||||
$path = $this->getItemHref($identifierref);
|
||||
$title = basename($path);
|
||||
} else { // A resource but not a file... we assume it's a quiz bank and its assigned identifier is irrelevant to its name
|
||||
$title = 'Quiz Bank '.($this->countInstances($tool_type) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($level == ROOT_DEEP) {
|
||||
$index_root = $array_index;
|
||||
}
|
||||
|
||||
static::$instances['index'][$array_index] = [
|
||||
'common_cartridge_type' => $ccType,
|
||||
'tool_type' => $tool_type,
|
||||
'title' => $title ? $title : '',
|
||||
'root_parent' => $index_root,
|
||||
'index' => $array_index,
|
||||
'deep' => $level,
|
||||
'instance' => $this->countInstances($tool_type),
|
||||
'resource_identifier' => $identifierref,
|
||||
];
|
||||
|
||||
static::$instances['instances'][$tool_type][] = [
|
||||
'title' => $title,
|
||||
'instance' => static::$instances['index'][$array_index]['instance'],
|
||||
'common_cartridge_type' => $ccType,
|
||||
'resource_identifier' => $identifierref,
|
||||
'deep' => $level,
|
||||
'src' => $path,
|
||||
];
|
||||
|
||||
$more_items = $xpath->query('imscc:item', $item);
|
||||
|
||||
if ($more_items->length > 0) {
|
||||
$this->createInstances($more_items, $level, $array_index, $index_root);
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function criticalError($text)
|
||||
{
|
||||
$path_to_log = static::logFile();
|
||||
|
||||
echo '
|
||||
|
||||
<p>
|
||||
<hr />A critical error has been found!
|
||||
|
||||
<p>'.$text.'</p>
|
||||
|
||||
|
||||
<p>
|
||||
The process has been stopped. Please see the <a href="'.$path_to_log.'">log file</a> for more information.</p>
|
||||
|
||||
<p>Log: '.$path_to_log.'</p>
|
||||
|
||||
<hr />
|
||||
|
||||
</p>
|
||||
';
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
protected function createCourseCode($title)
|
||||
{
|
||||
//Making sure that text of the short name does not go over the DB limit.
|
||||
//and leaving the space to add additional characters by the platform
|
||||
$code = substr(strtoupper(str_replace(' ', '', trim($title))), 0, 94);
|
||||
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class AssesmentValidator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::ASSESMENT_VALIDATOR13, $location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class BltiValidator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::BLTI_VALIDATOR13, $location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class CcValidateType
|
||||
{
|
||||
public const MANIFEST_VALIDATOR1 = 'cclibxml2validator.xsd';
|
||||
public const ASSESMENT_VALIDATOR1 = '/domainProfile_4/ims_qtiasiv1p2_localised.xsd';
|
||||
public const DISCUSSION_VALIDATOR1 = '/domainProfile_6/imsdt_v1p0_localised.xsd';
|
||||
public const WEBLINK_VALIDATOR1 = '/domainProfile_5/imswl_v1p0_localised.xsd';
|
||||
|
||||
public const MANIFEST_VALIDATOR11 = 'cc11libxml2validator.xsd';
|
||||
public const BLTI_VALIDATOR11 = 'imslticc_v1p0p1.xsd';
|
||||
public const ASSESMENT_VALIDATOR11 = 'ccv1p1_qtiasiv1p2p1_v1p0.xsd';
|
||||
public const DISCUSSION_VALIDATOR11 = 'ccv1p1_imsdt_v1p1.xsd';
|
||||
public const WEBLINK_VALIDATOR11 = 'ccv1p1_imswl_v1p1.xsd';
|
||||
|
||||
public const MANIFEST_VALIDATOR13 = 'cc13libxml2validator.xsd';
|
||||
public const BLTI_VALIDATOR13 = 'imslticc_v1p3.xsd';
|
||||
public const ASSESMENT_VALIDATOR13 = 'ccv1p3_qtiasiv1p2p1_v1p0.xsd';
|
||||
public const DISCUSSION_VALIDATOR13 = 'ccv1p3_imsdt_v1p3.xsd';
|
||||
public const WEBLINK_VALIDATOR13 = 'ccv1p3_imswl_v1p3.xsd';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $location = null;
|
||||
|
||||
public function __construct($type, $location)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->location = $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the item.
|
||||
*
|
||||
* @param string $element - File path for the xml
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($element)
|
||||
{
|
||||
$this->last_error = null;
|
||||
$celement = realpath($element);
|
||||
$cvalidator = realpath($this->location.DIRECTORY_SEPARATOR.$this->type);
|
||||
$result = (empty($celement) || empty($cvalidator));
|
||||
if (!$result) {
|
||||
$xml_error = new LibxmlErrorsMgr();
|
||||
$doc = new DOMDocument();
|
||||
$doc->validateOnParse = false;
|
||||
$result = $doc->load($celement, LIBXML_NONET) &&
|
||||
$doc->schemaValidate($cvalidator);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class DiscussionValidator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::DISCUSSION_VALIDATOR13, $location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
final class ErrorMessages
|
||||
{
|
||||
/**
|
||||
* @static ErrorMessages
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $items = [];
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Casting to string method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ErrorMessages
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
if (empty(self::$instance)) {
|
||||
$c = __CLASS__;
|
||||
self::$instance = new $c();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $msg
|
||||
*/
|
||||
public function add($msg)
|
||||
{
|
||||
if (!empty($msg)) {
|
||||
$this->items[] = $msg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function errors()
|
||||
{
|
||||
$this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empties the error content.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->items = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $web
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString($web = false)
|
||||
{
|
||||
$result = '';
|
||||
if ($web) {
|
||||
$result .= '<ol>'.PHP_EOL;
|
||||
}
|
||||
foreach ($this->items as $error) {
|
||||
if ($web) {
|
||||
$result .= '<li>';
|
||||
}
|
||||
|
||||
$result .= $error.PHP_EOL;
|
||||
|
||||
if ($web) {
|
||||
$result .= '</li>'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
if ($web) {
|
||||
$result .= '</ol>'.PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
final class LibxmlErrorsMgr
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $previous = false;
|
||||
|
||||
/**
|
||||
* @param bool $reset
|
||||
*/
|
||||
public function __construct($reset = false)
|
||||
{
|
||||
if ($reset) {
|
||||
ErrorMessages::instance()->reset();
|
||||
}
|
||||
$this->previous = libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->collectErrors();
|
||||
if (!$this->previous) {
|
||||
libxml_use_internal_errors($this->previous);
|
||||
}
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
$this->collectErrors();
|
||||
}
|
||||
|
||||
private function collectErrors($filename = '')
|
||||
{
|
||||
$errors = libxml_get_errors();
|
||||
static $error_types = [
|
||||
LIBXML_ERR_ERROR => 'Error', LIBXML_ERR_FATAL => 'Fatal Error', LIBXML_ERR_WARNING => 'Warning',
|
||||
];
|
||||
$result = [];
|
||||
foreach ($errors as $error) {
|
||||
$add = '';
|
||||
if (!empty($filename)) {
|
||||
$add = " in {$filename}";
|
||||
} elseif (!empty($error->file)) {
|
||||
$add = " in {$error->file}";
|
||||
}
|
||||
$line = '';
|
||||
if (!empty($error->line)) {
|
||||
$line = " at line {$error->line}";
|
||||
}
|
||||
$err = "{$error_types[$error->level]}{$add}: {$error->message}{$line}";
|
||||
ErrorMessages::instance()->add($err);
|
||||
}
|
||||
libxml_clear_errors();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class Manifest10Validator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::MANIFEST_VALIDATOR1, $location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class ManifestValidator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::MANIFEST_VALIDATOR13, $location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/validator.php under GNU/GPL license */
|
||||
|
||||
class WeblinkValidator extends CcValidateType
|
||||
{
|
||||
public function __construct($location)
|
||||
{
|
||||
parent::__construct(self::WEBLINK_VALIDATOR13, $location);
|
||||
}
|
||||
}
|
||||
57
main/common_cartridge/import/src/converter/Cc13Entities.php
Normal file
57
main/common_cartridge/import/src/converter/Cc13Entities.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class Cc13Entities extends CcEntities
|
||||
{
|
||||
public function getExternalXml($identifier)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath(Cc1p3Convert::$manifest, Cc1p3Convert::$namespaces);
|
||||
$files = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.
|
||||
$identifier.'"]/imscc:file/@href');
|
||||
$response = empty($files) || ($files->length == 0) ? '' : $files->item(0)->nodeValue;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getAllFiles()
|
||||
{
|
||||
$permDirs = api_get_permissions_for_new_directories();
|
||||
|
||||
$allFiles = [];
|
||||
$xpath = Cc1p3Convert::newxPath(Cc1p3Convert::$manifest, Cc1p3Convert::$namespaces);
|
||||
foreach (Cc1p3Convert::$restypes as $type) {
|
||||
$files = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@type="'.
|
||||
$type.'"]/imscc:file/@href');
|
||||
if (empty($files) || ($files->length == 0)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
//omit html files
|
||||
//this is a bit too simplistic
|
||||
$ext = strtolower(pathinfo($file->nodeValue, PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['html', 'htm', 'xhtml'])) {
|
||||
continue;
|
||||
}
|
||||
$allFiles[] = $file->nodeValue;
|
||||
}
|
||||
unset($files);
|
||||
}
|
||||
|
||||
//are there any labels?
|
||||
$xquery = "//imscc:item/imscc:item/imscc:item[imscc:title][not(@identifierref)]";
|
||||
$labels = $xpath->query($xquery);
|
||||
if (!empty($labels) && ($labels->length > 0)) {
|
||||
$tname = 'course_files';
|
||||
$dpath = Cc1p3Convert::$pathToManifestFolder.DIRECTORY_SEPARATOR.$tname;
|
||||
$rfpath = 'files.gif';
|
||||
$fpath = $dpath.DIRECTORY_SEPARATOR.'files.gif';
|
||||
if (!file_exists($dpath)) {
|
||||
mkdir($dpath, $permDirs, true);
|
||||
}
|
||||
$allFiles[] = $rfpath;
|
||||
}
|
||||
$allFiles = empty($allFiles) ? '' : $allFiles;
|
||||
|
||||
return $allFiles;
|
||||
}
|
||||
}
|
||||
166
main/common_cartridge/import/src/converter/Cc13Forum.php
Normal file
166
main/common_cartridge/import/src/converter/Cc13Forum.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
|
||||
|
||||
class Cc13Forum extends Cc13Entities
|
||||
{
|
||||
public function fullPath($path, $dir_sep = DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$token = '/\$(?:IMS|1EdTech)[-_]CC[-_]FILEBASE\$/';
|
||||
$path = preg_replace($token, '', $path);
|
||||
|
||||
if (is_string($path) && ($path != '')) {
|
||||
$dot_dir = '.';
|
||||
$up_dir = '..';
|
||||
$length = strlen($path);
|
||||
$rtemp = trim($path);
|
||||
$start = strrpos($path, $dir_sep);
|
||||
$can_continue = ($start !== false);
|
||||
$result = $can_continue ? '' : $path;
|
||||
$rcount = 0;
|
||||
|
||||
while ($can_continue) {
|
||||
$dir_part = ($start !== false) ? substr($rtemp, $start + 1, $length - $start) : $rtemp;
|
||||
$can_continue = ($dir_part !== false);
|
||||
|
||||
if ($can_continue) {
|
||||
if ($dir_part != $dot_dir) {
|
||||
if ($dir_part == $up_dir) {
|
||||
$rcount++;
|
||||
} else {
|
||||
if ($rcount > 0) {
|
||||
$rcount--;
|
||||
} else {
|
||||
$result = ($result == '') ? $dir_part : $dir_part.$dir_sep.$result;
|
||||
}
|
||||
}
|
||||
}
|
||||
$rtemp = substr($path, 0, $start);
|
||||
$start = strrpos($rtemp, $dir_sep);
|
||||
$can_continue = (($start !== false) || (strlen($rtemp) > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function generateData()
|
||||
{
|
||||
$data = [];
|
||||
if (!empty(Cc1p3Convert::$instances['instances']['forum'])) {
|
||||
foreach (Cc1p3Convert::$instances['instances']['forum'] as $instance) {
|
||||
$data[] = $this->getForumData($instance);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getForumData($instance)
|
||||
{
|
||||
$topic_data = $this->getTopicData($instance);
|
||||
|
||||
$values = [];
|
||||
if (!empty($topic_data)) {
|
||||
$values = [
|
||||
'instance' => $instance['instance'],
|
||||
'title' => self::safexml($topic_data['title']),
|
||||
'description' => self::safexml($topic_data['description']),
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
public function storeForums($forums)
|
||||
{
|
||||
// Create a Forum category for the import CC 1.3.
|
||||
$courseInfo = api_get_course_info();
|
||||
$catForumValues['forum_category_title'] = 'CC1p3';
|
||||
$catForumValues['forum_category_comment'] = '';
|
||||
$catId = store_forumcategory(
|
||||
$catForumValues,
|
||||
$courseInfo,
|
||||
false
|
||||
);
|
||||
|
||||
foreach ($forums as $forum) {
|
||||
$forumValues = [];
|
||||
$forumValues['forum_title'] = $forum['title'];
|
||||
$forumValues['forum_image'] = '';
|
||||
$forumValues['forum_comment'] = strip_tags($forum['description']);
|
||||
$forumValues['forum_category'] = $catId;
|
||||
$forumValues['moderated'] = 0;
|
||||
store_forum($forumValues, $courseInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTopicData($instance)
|
||||
{
|
||||
$topic_data = [];
|
||||
|
||||
$topic_file = $this->getExternalXml($instance['resource_identifier']);
|
||||
|
||||
if (!empty($topic_file)) {
|
||||
$topic_file_path = Cc1p3Convert::$pathToManifestFolder.DIRECTORY_SEPARATOR.$topic_file;
|
||||
$topic_file_dir = dirname($topic_file_path);
|
||||
$topic = $this->loadXmlResource($topic_file_path);
|
||||
|
||||
if (!empty($topic)) {
|
||||
$xpath = Cc1p3Convert::newxPath($topic, Cc1p3Convert::$forumns);
|
||||
|
||||
$topic_title = $xpath->query('/dt:topic/dt:title');
|
||||
if ($topic_title->length > 0 && !empty($topic_title->item(0)->nodeValue)) {
|
||||
$topic_title = $topic_title->item(0)->nodeValue;
|
||||
} else {
|
||||
$topic_title = 'Untitled Topic';
|
||||
}
|
||||
|
||||
$topic_text = $xpath->query('/dt:topic/dt:text');
|
||||
$topic_text = !empty($topic_text->item(0)->nodeValue) ? $this->updateSources($topic_text->item(0)->nodeValue, dirname($topic_file)) : '';
|
||||
$topic_text = !empty($topic_text) ? str_replace("%24", "\$", $this->includeTitles($topic_text)) : '';
|
||||
|
||||
if (!empty($topic_title)) {
|
||||
$topic_data['title'] = $topic_title;
|
||||
$topic_data['description'] = $topic_text;
|
||||
}
|
||||
}
|
||||
|
||||
$topic_attachments = $xpath->query('/dt:topic/dt:attachments/dt:attachment/@href');
|
||||
|
||||
if ($topic_attachments->length > 0) {
|
||||
$attachment_html = '';
|
||||
|
||||
foreach ($topic_attachments as $file) {
|
||||
$attachment_html .= $this->generateAttachmentHtml($this->fullPath($file->nodeValue, '/'));
|
||||
}
|
||||
|
||||
$topic_data['description'] = !empty($attachment_html) ? $topic_text.'<p>Attachments:</p>'.$attachment_html : $topic_text;
|
||||
}
|
||||
}
|
||||
|
||||
return $topic_data;
|
||||
}
|
||||
|
||||
private function generateAttachmentHtml(string $filename, ?string $rootPath)
|
||||
{
|
||||
$images_extensions = ['gif', 'jpeg', 'jpg', 'jif', 'jfif', 'png', 'bmp', 'webp'];
|
||||
|
||||
$fileinfo = pathinfo($filename);
|
||||
if (empty($rootPath)) {
|
||||
$rootPath = '';
|
||||
}
|
||||
|
||||
if (in_array($fileinfo['extension'], $images_extensions)) {
|
||||
return '<img src="'.$rootPath.$filename.'" title="'.$fileinfo['basename'].'" alt="'.$fileinfo['basename'].'" /><br />';
|
||||
} else {
|
||||
return '<a href="'.$rootPath.$filename.'" title="'.$fileinfo['basename'].'" alt="'.$fileinfo['basename'].'">'.$fileinfo['basename'].'</a><br />';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
763
main/common_cartridge/import/src/converter/Cc13Quiz.php
Normal file
763
main/common_cartridge/import/src/converter/Cc13Quiz.php
Normal file
@@ -0,0 +1,763 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class Cc13Quiz extends Cc13Entities
|
||||
{
|
||||
/**
|
||||
* Get all data from the object instance (coming from the xml file) into a clean array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generateData()
|
||||
{
|
||||
$data = [];
|
||||
$instances = $this->generateInstances();
|
||||
if (!empty($instances)) {
|
||||
foreach ($instances as $instance) {
|
||||
if ($instance['is_question_bank'] == 0) {
|
||||
$data[] = $this->getQuizData($instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a quiz based on the information available in the assessment structure.
|
||||
*
|
||||
* @param $quiz
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function storeQuiz($quiz)
|
||||
{
|
||||
$token = '/\$(?:IMS|1EdTech)[-_]CC[-_]FILEBASE\$\.\.\//';
|
||||
$courseInfo = api_get_course_info();
|
||||
// Replace by the path in documents in which we place all relevant CC files
|
||||
$replacementPath = '/courses/'.$courseInfo['directory'].'/document/commoncartridge/';
|
||||
$exercise = new Exercise($courseInfo['real_id']);
|
||||
$title = Exercise::format_title_variable($quiz['title']);
|
||||
$exercise->updateTitle($title);
|
||||
$description = preg_replace($token, $replacementPath, $quiz['description']);
|
||||
$exercise->updateDescription($description);
|
||||
$exercise->updateAttempts($quiz['max_attempts']);
|
||||
$exercise->updateFeedbackType(0);
|
||||
|
||||
$exercise->setRandom(0);
|
||||
$exercise->updateRandomAnswers(!empty($moduleValues['shuffleanswers']));
|
||||
$exercise->updateExpiredTime((int) $quiz['timelimit']);
|
||||
$exercise->updateType(1);
|
||||
|
||||
// Create the new Quiz
|
||||
$exercise->save();
|
||||
|
||||
if (!empty($quiz['questions'])) {
|
||||
foreach ($quiz['questions'] as $question) {
|
||||
$qtype = $question['type'];
|
||||
|
||||
$types = [
|
||||
'unique_answer' => UNIQUE_ANSWER,
|
||||
'multiple_answer' => MULTIPLE_ANSWER,
|
||||
'fib' => FILL_IN_BLANKS,
|
||||
'essay' => FREE_ANSWER,
|
||||
];
|
||||
$questionType = $types[$qtype];
|
||||
|
||||
$questionInstance = Question::getInstance($questionType);
|
||||
if (empty($questionInstance)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$questionInstance->updateTitle(substr(Security::remove_XSS(strip_tags_blacklist($question['title'], ['br', 'p'])), 0, 20));
|
||||
$questionText = Security::remove_XSS(strip_tags_blacklist($question['title'], ['br', 'p']));
|
||||
// Replace the path from $1EdTech-CC-FILEBASE$ to a correct chamilo path
|
||||
$questionText = preg_replace($token, $replacementPath, $questionText);
|
||||
$questionInstance->updateDescription($questionText);
|
||||
|
||||
$questionInstance->updateLevel(1);
|
||||
$questionInstance->updateCategory(0);
|
||||
|
||||
//Save normal question if NOT media
|
||||
if ($questionInstance->type != MEDIA_QUESTION) {
|
||||
$questionInstance->save($exercise);
|
||||
// modify the exercise
|
||||
$exercise->addToList($questionInstance->iid);
|
||||
$exercise->update_question_positions();
|
||||
}
|
||||
|
||||
if ($qtype == 'unique_answer') {
|
||||
$objAnswer = new Answer($questionInstance->iid);
|
||||
$questionWeighting = 0;
|
||||
foreach ($question['answers'] as $slot => $answerValues) {
|
||||
$correct = $answerValues['score'] ? (int) $answerValues['score'] : 0;
|
||||
$answer = Security::remove_XSS(preg_replace($token, $replacementPath, $answerValues['title']));
|
||||
$comment = Security::remove_XSS(preg_replace($token, $replacementPath, $answerValues['feedback']));
|
||||
$weighting = $answerValues['score'];
|
||||
$weighting = abs($weighting);
|
||||
if ($weighting > 0) {
|
||||
$questionWeighting += $weighting;
|
||||
}
|
||||
$goodAnswer = $correct ? true : false;
|
||||
|
||||
$objAnswer->createAnswer(
|
||||
$answer,
|
||||
$goodAnswer,
|
||||
$comment,
|
||||
$weighting,
|
||||
$slot + 1,
|
||||
null,
|
||||
null,
|
||||
''
|
||||
);
|
||||
}
|
||||
// saves the answers into the database
|
||||
$objAnswer->save();
|
||||
// sets the total weighting of the question
|
||||
$questionInstance->updateWeighting($questionWeighting);
|
||||
$questionInstance->save($exercise);
|
||||
} else {
|
||||
$objAnswer = new Answer($questionInstance->iid);
|
||||
$questionWeighting = 0;
|
||||
if (is_array($question['answers'])) {
|
||||
foreach ($question['answers'] as $slot => $answerValues) {
|
||||
$answer = Security::remove_XSS(preg_replace($token, $replacementPath, $answerValues['title']));
|
||||
$comment = Security::remove_XSS(preg_replace($token, $replacementPath, $answerValues['feedback']));
|
||||
$weighting = $answerValues['score'];
|
||||
if ($weighting > 0) {
|
||||
$questionWeighting += $weighting;
|
||||
}
|
||||
$goodAnswer = $weighting > 0;
|
||||
|
||||
$objAnswer->createAnswer(
|
||||
$answer,
|
||||
$goodAnswer,
|
||||
$comment,
|
||||
$weighting,
|
||||
$slot + 1,
|
||||
null,
|
||||
null,
|
||||
''
|
||||
);
|
||||
}
|
||||
} elseif ($qtype == 'essay') {
|
||||
$questionWeighting = $question['ponderation'];
|
||||
}
|
||||
|
||||
// saves the answers into the database
|
||||
$objAnswer->save();
|
||||
// sets the total weighting of the question
|
||||
$questionInstance->updateWeighting($questionWeighting);
|
||||
$questionInstance->save($exercise);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function storeQuizzes($quizzes)
|
||||
{
|
||||
if (!empty($quizzes)) {
|
||||
foreach ($quizzes as $quiz) {
|
||||
$this->storeQuiz($quiz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getQuizData($instance)
|
||||
{
|
||||
$values = [];
|
||||
if (!empty($instance)) {
|
||||
$questions = [];
|
||||
if (!empty($instance['questions'])) {
|
||||
foreach ($instance['questions'] as $question) {
|
||||
$questions[$question['id']] = [
|
||||
'title' => $question['title'],
|
||||
'type' => $question['qtype'],
|
||||
'ponderation' => $question['defaultgrade'],
|
||||
'answers' => $question['answers'],
|
||||
];
|
||||
}
|
||||
}
|
||||
$values = [
|
||||
'id' => $instance['id'],
|
||||
'title' => $instance['title'],
|
||||
'description' => $instance['description'],
|
||||
'timelimit' => $instance['options']['timelimit'],
|
||||
'max_attempts' => $instance['options']['max_attempts'],
|
||||
'questions' => $questions,
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function generateInstances()
|
||||
{
|
||||
$lastInstanceId = 0;
|
||||
$lastQuestionId = 0;
|
||||
$lastAnswerId = 0;
|
||||
|
||||
$instances = [];
|
||||
|
||||
$types = [TOOL_TYPE_QUIZ];
|
||||
|
||||
foreach ($types as $type) {
|
||||
if (!empty(Cc1p3Convert::$instances['instances'][$type])) {
|
||||
foreach (Cc1p3Convert::$instances['instances'][$type] as $instance) {
|
||||
if ($type == TOOL_TYPE_QUIZ) {
|
||||
$is_question_bank = 0;
|
||||
} else {
|
||||
$is_question_bank = 1;
|
||||
}
|
||||
|
||||
// Get the path of the assessment.xml file
|
||||
$assessmentFile = $this->getExternalXml($instance['resource_identifier']);
|
||||
|
||||
if (!empty($assessmentFile)) {
|
||||
$assessment = $this->loadXmlResource(Cc1p3Convert::$pathToManifestFolder.DIRECTORY_SEPARATOR.$assessmentFile);
|
||||
|
||||
if (!empty($assessment)) {
|
||||
$replaceValues = ['unlimited' => 0];
|
||||
|
||||
$questions = $this->getQuestions($assessment, $lastQuestionId, $lastAnswerId, dirname($assessmentFile), $is_question_bank);
|
||||
$questionCount = count($questions);
|
||||
|
||||
if (!empty($questionCount)) {
|
||||
$lastInstanceId++;
|
||||
|
||||
$instances[$instance['resource_identifier']]['questions'] = $questions;
|
||||
$instances[$instance['resource_identifier']]['id'] = $lastInstanceId;
|
||||
$instances[$instance['resource_identifier']]['title'] = $instance['title'];
|
||||
$instances[$instance['resource_identifier']]['description'] = $this->getQuizDescription($assessment);
|
||||
$instances[$instance['resource_identifier']]['is_question_bank'] = $is_question_bank;
|
||||
$instances[$instance['resource_identifier']]['options']['timelimit'] = $this->getGlobalConfig($assessment, 'qmd_timelimit', 0);
|
||||
$instances[$instance['resource_identifier']]['options']['max_attempts'] = $this->getGlobalConfig($assessment, 'cc_maxattempts', 0, $replaceValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
private function getGlobalConfig($assessment, $option, $defaultValue, $replaceValues = '')
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
$metadata = $xpath->query('/xmlns:questestinterop/xmlns:assessment/xmlns:qtimetadata/xmlns:qtimetadatafield');
|
||||
|
||||
foreach ($metadata as $field) {
|
||||
$fieldLabel = $xpath->query('xmlns:fieldlabel', $field);
|
||||
$fieldLabel = !empty($fieldLabel->item(0)->nodeValue) ? $fieldLabel->item(0)->nodeValue : '';
|
||||
|
||||
if (strtolower($fieldLabel) == strtolower($option)) {
|
||||
$fieldEntry = $xpath->query('xmlns:fieldentry', $field);
|
||||
$response = !empty($fieldEntry->item(0)->nodeValue) ? $fieldEntry->item(0)->nodeValue : '';
|
||||
}
|
||||
}
|
||||
|
||||
$response = !empty($response) ? trim($response) : '';
|
||||
|
||||
if (!empty($replaceValues)) {
|
||||
foreach ($replaceValues as $key => $value) {
|
||||
$response = ($key == $response) ? $value : $response;
|
||||
}
|
||||
}
|
||||
|
||||
$response = empty($response) ? $defaultValue : $response;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function getQuizDescription(DOMDocument $assessment): string
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
$fieldEntry = $xpath->query('/xmlns:questestinterop/xmlns:assessment/xmlns:rubric/xmlns:material/xmlns:mattext');
|
||||
$response = !empty($fieldEntry->item(0)->nodeValue) ? $fieldEntry->item(0)->nodeValue : '';
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function getQuestions($assessment, &$lastQuestionId, &$last_answer_id, $rootPath, $is_question_bank)
|
||||
{
|
||||
$questions = [];
|
||||
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
if (!$is_question_bank) {
|
||||
$questionsItems = $xpath->query('/xmlns:questestinterop/xmlns:assessment/xmlns:section/xmlns:item');
|
||||
} else {
|
||||
$questionsItems = $xpath->query('/xmlns:questestinterop/xmlns:objectbank/xmlns:item');
|
||||
}
|
||||
|
||||
foreach ($questionsItems as $question_item) {
|
||||
$countQuestions = $xpath->evaluate('count(xmlns:presentation/xmlns:flow/xmlns:material/xmlns:mattext)', $question_item);
|
||||
|
||||
if ($countQuestions == 0) {
|
||||
$questionTitle = $xpath->query('xmlns:presentation/xmlns:material/xmlns:mattext', $question_item);
|
||||
} else {
|
||||
$questionTitle = $xpath->query('xmlns:presentation/xmlns:flow/xmlns:material/xmlns:mattext', $question_item);
|
||||
}
|
||||
|
||||
$questionTitle = !empty($questionTitle->item(0)->nodeValue) ? $questionTitle->item(0)->nodeValue : '';
|
||||
|
||||
$questionIdentifier = $xpath->query('@ident', $question_item);
|
||||
$questionIdentifier = !empty($questionIdentifier->item(0)->nodeValue) ? $questionIdentifier->item(0)->nodeValue : '';
|
||||
|
||||
if (!empty($questionIdentifier)) {
|
||||
$questionType = $this->getQuestionType($questionIdentifier, $assessment);
|
||||
|
||||
if (!empty($questionType['qtype'])) {
|
||||
$lastQuestionId++;
|
||||
|
||||
$questions[$questionIdentifier]['id'] = $lastQuestionId;
|
||||
|
||||
$questionTitle = $this->updateSources($questionTitle, $rootPath);
|
||||
$questionTitle = !empty($questionTitle) ? str_replace("%24", "\$", $this->includeTitles($questionTitle)) : '';
|
||||
|
||||
$questionname = $xpath->query('@title', $question_item);
|
||||
$questionname = !empty($questionname->item(0)->nodeValue) ? $questionname->item(0)->nodeValue : '';
|
||||
|
||||
$questions[$questionIdentifier]['title'] = $questionTitle;
|
||||
$questions[$questionIdentifier]['name'] = $questionname;
|
||||
$questions[$questionIdentifier]['identifier'] = $questionIdentifier;
|
||||
$questions[$questionIdentifier]['qtype'] = $questionType['qtype'];
|
||||
$questions[$questionIdentifier]['cc_type'] = $questionType['cc'];
|
||||
$questions[$questionIdentifier]['feedback'] = $this->getGeneralFeedback($assessment, $questionIdentifier);
|
||||
$questions[$questionIdentifier]['defaultgrade'] = $this->getDefaultgrade($assessment, $questionIdentifier);
|
||||
$questions[$questionIdentifier]['answers'] = $this->getAnswers($questionIdentifier, $assessment, $lastAnswerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$questions = !empty($questions) ? $questions : '';
|
||||
|
||||
return $questions;
|
||||
}
|
||||
|
||||
private function getDefaultgrade($assessment, $questionIdentifier)
|
||||
{
|
||||
$result = 1;
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
$query = '//xmlns:item[@ident="'.$questionIdentifier.'"]';
|
||||
$query .= '//xmlns:qtimetadatafield[xmlns:fieldlabel="cc_weighting"]/xmlns:fieldentry';
|
||||
$defgrade = $xpath->query($query);
|
||||
if (!empty($defgrade) && ($defgrade->length > 0)) {
|
||||
$resp = (int) $defgrade->item(0)->nodeValue;
|
||||
if ($resp >= 0 && $resp <= 99) {
|
||||
$result = $resp;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getGeneralFeedback($assessment, $questionIdentifier)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$respconditions = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition');
|
||||
|
||||
if (!empty($respconditions)) {
|
||||
foreach ($respconditions as $respcondition) {
|
||||
$continue = $respcondition->getAttributeNode('continue');
|
||||
$continue = !empty($continue->nodeValue) ? strtolower($continue->nodeValue) : '';
|
||||
|
||||
if ($continue == 'yes') {
|
||||
$displayFeedback = $xpath->query('xmlns:displayfeedback', $respcondition);
|
||||
|
||||
if (!empty($displayFeedback)) {
|
||||
foreach ($displayFeedback as $feedback) {
|
||||
$feedbackIdentifier = $feedback->getAttributeNode('linkrefid');
|
||||
$feedbackIdentifier = !empty($feedbackIdentifier->nodeValue) ? $feedbackIdentifier->nodeValue : '';
|
||||
|
||||
if (!empty($feedbackIdentifier)) {
|
||||
$feedbackIdentifiers[] = $feedbackIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$feedback = '';
|
||||
$feedbackIdentifiers = empty($feedbackIdentifiers) ? '' : $feedbackIdentifiers;
|
||||
|
||||
if (!empty($feedbackIdentifiers)) {
|
||||
foreach ($feedbackIdentifiers as $feedbackIdentifier) {
|
||||
$feedbacks = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:itemfeedback[@ident="'.$feedbackIdentifier.'"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
|
||||
$feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue.' ' : '';
|
||||
}
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
|
||||
private function getFeedback($assessment, $identifier, $itemIdentifier, $questionType)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$resourceProcessing = $xpath->query('//xmlns:item[@ident="'.$itemIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition');
|
||||
|
||||
if (!empty($resourceProcessing)) {
|
||||
foreach ($resourceProcessing as $response) {
|
||||
$varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response);
|
||||
$varequal = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
|
||||
|
||||
if (strtolower($varequal) == strtolower($identifier) || ($questionType == CC_QUIZ_ESSAY)) {
|
||||
$displayFeedback = $xpath->query('xmlns:displayfeedback', $response);
|
||||
|
||||
if (!empty($displayFeedback)) {
|
||||
foreach ($displayFeedback as $feedback) {
|
||||
$feedbackIdentifier = $feedback->getAttributeNode('linkrefid');
|
||||
$feedbackIdentifier = !empty($feedbackIdentifier->nodeValue) ? $feedbackIdentifier->nodeValue : '';
|
||||
|
||||
if (!empty($feedbackIdentifier)) {
|
||||
$feedbackIdentifiers[] = $feedbackIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$feedback = '';
|
||||
$feedbackIdentifiers = empty($feedbackIdentifiers) ? '' : $feedbackIdentifiers;
|
||||
|
||||
if (!empty($feedbackIdentifiers)) {
|
||||
foreach ($feedbackIdentifiers as $feedbackIdentifier) {
|
||||
$feedbacks = $xpath->query('//xmlns:item[@ident="'.$itemIdentifier.'"]/xmlns:itemfeedback[@ident="'.$feedbackIdentifier.'"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
|
||||
$feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue.' ' : '';
|
||||
}
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
|
||||
private function getAnswersFib($questionIdentifier, $identifier, $assessment, &$lastAnswerId)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$correctanswersfib = [];
|
||||
$incorrectanswersfib = [];
|
||||
|
||||
$responseItems = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition');
|
||||
|
||||
$correctrespcond = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition/xmlns:setvar[text()="100"]/..');
|
||||
$correctanswers = $xpath->query('xmlns:conditionvar/xmlns:varequal', $correctrespcond->item(0));
|
||||
|
||||
// Correct answers.
|
||||
foreach ($correctanswers as $correctans) {
|
||||
$answertitle = !empty($correctans->nodeValue) ? $correctans->nodeValue : '';
|
||||
if (empty($answertitle)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lastAnswerId++;
|
||||
|
||||
$correctanswersfib[$answertitle] = [
|
||||
'id' => $lastAnswerId,
|
||||
'title' => $answertitle,
|
||||
'score' => 1,
|
||||
'feedback' => '',
|
||||
'case' => 0, ];
|
||||
}
|
||||
|
||||
// Handle incorrect answers and feedback for all items.
|
||||
foreach ($responseItems as $response_item) {
|
||||
$setvar = $xpath->query('xmlns:setvar', $response_item);
|
||||
if (!empty($setvar->length) && $setvar->item(0)->nodeValue == '100') {
|
||||
// Skip the correct answer responsecondition.
|
||||
continue;
|
||||
}
|
||||
|
||||
$varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response_item);
|
||||
if (empty($varequal->length)) {
|
||||
// Skip respcondition elements that don't have varequal containing an answer
|
||||
continue;
|
||||
}
|
||||
$answerTitle = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
|
||||
|
||||
$displayFeedback = $xpath->query('xmlns:displayfeedback', $response_item);
|
||||
|
||||
unset($feedbackIdentifiers);
|
||||
|
||||
if (!empty($displayFeedback)) {
|
||||
foreach ($displayFeedback as $feedback) {
|
||||
$feedbackIdentifier = $feedback->getAttributeNode('linkrefid');
|
||||
$feedbackIdentifier = !empty($feedbackIdentifier->nodeValue) ? $feedbackIdentifier->nodeValue : '';
|
||||
|
||||
if (!empty($feedbackIdentifier)) {
|
||||
$feedbackIdentifiers[] = $feedbackIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$feedback = '';
|
||||
$feedbackIdentifiers = empty($feedbackIdentifiers) ? '' : $feedbackIdentifiers;
|
||||
|
||||
if (!empty($feedbackIdentifiers)) {
|
||||
foreach ($feedbackIdentifiers as $feedbackIdentifier) {
|
||||
$feedbacks = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:itemfeedback[@ident="'.$feedbackIdentifier.'"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
|
||||
$feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue.' ' : '';
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($answerTitle, $correctanswersfib)) {
|
||||
// Already a correct answer, just need the feedback for the correct answer.
|
||||
$correctanswerfib[$answerTitle]['feedback'] = $feedback;
|
||||
} else {
|
||||
// Need to add an incorrect answer.
|
||||
$lastAnswerId++;
|
||||
$incorrectanswersfib[] = [
|
||||
'id' => $lastAnswerId,
|
||||
'title' => $answerTitle,
|
||||
'score' => 0,
|
||||
'feedback' => $feedback,
|
||||
'case' => 0, ];
|
||||
}
|
||||
}
|
||||
|
||||
$answersFib = array_merge($correctanswersfib, $incorrectanswersfib);
|
||||
$answersFib = empty($answersFib) ? '' : $answersFib;
|
||||
|
||||
return $answersFib;
|
||||
}
|
||||
|
||||
private function getAnswersPatternMatch($questionIdentifier, $identifier, $assessment, &$lastAnswerId)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$answersFib = [];
|
||||
|
||||
$responseItems = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition');
|
||||
|
||||
foreach ($responseItems as $response_item) {
|
||||
$setvar = $xpath->query('xmlns:setvar', $response_item);
|
||||
$setvar = is_object($setvar->item(0)) ? $setvar->item(0)->nodeValue : '';
|
||||
|
||||
if ($setvar != '') {
|
||||
$lastAnswerId++;
|
||||
|
||||
$answerTitle = $xpath->query('xmlns:conditionvar/xmlns:varequal[@respident="'.$identifier.'"]', $response_item);
|
||||
$answerTitle = !empty($answerTitle->item(0)->nodeValue) ? $answerTitle->item(0)->nodeValue : '';
|
||||
|
||||
if (empty($answerTitle)) {
|
||||
$answerTitle = $xpath->query('xmlns:conditionvar/xmlns:varsubstring[@respident="'.$identifier.'"]', $response_item);
|
||||
$answerTitle = !empty($answerTitle->item(0)->nodeValue) ? '*'.$answerTitle->item(0)->nodeValue.'*' : '';
|
||||
}
|
||||
|
||||
if (empty($answerTitle)) {
|
||||
$answerTitle = '*';
|
||||
}
|
||||
|
||||
$case = $xpath->query('xmlns:conditionvar/xmlns:varequal/@case', $response_item);
|
||||
$case = is_object($case->item(0)) ? $case->item(0)->nodeValue : 'no'
|
||||
;
|
||||
$case = strtolower($case) == 'yes' ? 1 :
|
||||
0;
|
||||
|
||||
$displayFeedback = $xpath->query('xmlns:displayfeedback', $response_item);
|
||||
|
||||
unset($feedbackIdentifiers);
|
||||
|
||||
if (!empty($displayFeedback)) {
|
||||
foreach ($displayFeedback as $feedback) {
|
||||
$feedbackIdentifier = $feedback->getAttributeNode('linkrefid');
|
||||
$feedbackIdentifier = !empty($feedbackIdentifier->nodeValue) ? $feedbackIdentifier->nodeValue : '';
|
||||
|
||||
if (!empty($feedbackIdentifier)) {
|
||||
$feedbackIdentifiers[] = $feedbackIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$feedback = '';
|
||||
$feedbackIdentifiers = empty($feedbackIdentifiers) ? '' : $feedbackIdentifiers;
|
||||
|
||||
if (!empty($feedbackIdentifiers)) {
|
||||
foreach ($feedbackIdentifiers as $feedbackIdentifier) {
|
||||
$feedbacks = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:itemfeedback[@ident="'.$feedbackIdentifier.'"]/xmlns:flow_mat/xmlns:material/xmlns:mattext');
|
||||
$feedback .= !empty($feedbacks->item(0)->nodeValue) ? $feedbacks->item(0)->nodeValue.' ' : '';
|
||||
}
|
||||
}
|
||||
|
||||
$answersFib[] = ['id' => $lastAnswerId,
|
||||
'title' => $answerTitle,
|
||||
'score' => $setvar,
|
||||
'feedback' => $feedback,
|
||||
'case' => $case, ];
|
||||
}
|
||||
}
|
||||
|
||||
$answersFib = empty($answersFib) ? '' : $answersFib;
|
||||
|
||||
return $answersFib;
|
||||
}
|
||||
|
||||
private function getAnswers($identifier, $assessment, &$lastAnswerId)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$answers = [];
|
||||
|
||||
$questionCcType = $this->getQuestionType($identifier, $assessment);
|
||||
$questionCcType = $questionCcType['cc'];
|
||||
$isMultiresponse = ($questionCcType == CC_QUIZ_MULTIPLE_RESPONSE);
|
||||
|
||||
if ($questionCcType == CC_QUIZ_MULTIPLE_CHOICE || $isMultiresponse || $questionCcType == CC_QUIZ_TRUE_FALSE) {
|
||||
$queryAnswers = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:response_lid/xmlns:render_choice/xmlns:response_label';
|
||||
$queryAnswersWithFlow = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:flow/xmlns:response_lid/xmlns:render_choice/xmlns:response_label';
|
||||
|
||||
$queryIndentifer = '@ident';
|
||||
$queryTitle = 'xmlns:material/xmlns:mattext';
|
||||
}
|
||||
|
||||
if ($questionCcType == CC_QUIZ_ESSAY) {
|
||||
$queryAnswers = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:response_str';
|
||||
$queryAnswersWithFlow = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:flow/xmlns:response_str';
|
||||
|
||||
$queryIndentifer = '@ident';
|
||||
$queryTitle = 'xmlns:render_fib';
|
||||
}
|
||||
|
||||
if ($questionCcType == CC_QUIZ_FIB || $questionCcType == CC_QUIZ_PATTERN_MACHT) {
|
||||
$xpathQuery = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:response_str/@ident';
|
||||
$xpathQueryWithFlow = '//xmlns:item[@ident="'.$identifier.'"]/xmlns:presentation/xmlns:flow/xmlns:response_str/@ident';
|
||||
|
||||
$countResponse = $xpath->evaluate('count('.$xpathQueryWithFlow.')');
|
||||
|
||||
if ($countResponse == 0) {
|
||||
$answerIdentifier = $xpath->query($xpathQuery);
|
||||
} else {
|
||||
$answerIdentifier = $xpath->query($xpathQueryWithFlow);
|
||||
}
|
||||
|
||||
$answerIdentifier = !empty($answerIdentifier->item(0)->nodeValue) ? $answerIdentifier->item(0)->nodeValue : '';
|
||||
|
||||
if ($questionCcType == CC_QUIZ_FIB) {
|
||||
$answers = $this->getAnswersFib($identifier, $answerIdentifier, $assessment, $lastAnswerId);
|
||||
} else {
|
||||
$answers = $this->getAnswersPatternMatch($identifier, $answerIdentifier, $assessment, $lastAnswerId);
|
||||
}
|
||||
} else {
|
||||
$countResponse = $xpath->evaluate('count('.$queryAnswersWithFlow.')');
|
||||
|
||||
if ($countResponse == 0) {
|
||||
$responseItems = $xpath->query($queryAnswers);
|
||||
} else {
|
||||
$responseItems = $xpath->query($queryAnswersWithFlow);
|
||||
}
|
||||
|
||||
if (!empty($responseItems)) {
|
||||
if ($isMultiresponse) {
|
||||
$correctAnswerScore = 0;
|
||||
//get the correct answers count
|
||||
$canswers_query = "//xmlns:item[@ident='{$identifier}']//xmlns:setvar[@varname='SCORE'][.=100]/../xmlns:conditionvar//xmlns:varequal[@case='Yes'][not(parent::xmlns:not)]";
|
||||
$canswers = $xpath->query($canswers_query);
|
||||
if ($canswers->length > 0) {
|
||||
$correctAnswerScore = round(1.0 / (float) $canswers->length, 7); //weird
|
||||
$correctAanswersIdent = [];
|
||||
foreach ($canswers as $cnode) {
|
||||
$correctAanswersIdent[$cnode->nodeValue] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($responseItems as $response_item) {
|
||||
$lastAnswerId++;
|
||||
|
||||
$answerIdentifier = $xpath->query($queryIndentifer, $response_item);
|
||||
$answerIdentifier = !empty($answerIdentifier->item(0)->nodeValue) ? $answerIdentifier->item(0)->nodeValue : '';
|
||||
|
||||
$answerTitle = $xpath->query($queryTitle, $response_item);
|
||||
$answerTitle = !empty($answerTitle->item(0)->nodeValue) ? $answerTitle->item(0)->nodeValue : '';
|
||||
|
||||
$answerFeedback = $this->getFeedback($assessment, $answerIdentifier, $identifier, $questionCcType);
|
||||
|
||||
$answer_score = $this->getScore($assessment, $answerIdentifier, $identifier);
|
||||
|
||||
if ($isMultiresponse && isset($correctAanswersIdent[$answerIdentifier])) {
|
||||
$answer_score = $correctAnswerScore;
|
||||
}
|
||||
|
||||
$answers[] = ['id' => $lastAnswerId,
|
||||
'title' => $answerTitle,
|
||||
'score' => $answer_score,
|
||||
'identifier' => $answerIdentifier,
|
||||
'feedback' => $answerFeedback, ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$answers = empty($answers) ? '' : $answers;
|
||||
|
||||
return $answers;
|
||||
}
|
||||
|
||||
private function getScore($assessment, $identifier, $questionIdentifier)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$resourceProcessing = $xpath->query('//xmlns:item[@ident="'.$questionIdentifier.'"]/xmlns:resprocessing/xmlns:respcondition');
|
||||
|
||||
if (!empty($resourceProcessing)) {
|
||||
foreach ($resourceProcessing as $response) {
|
||||
$questionCcType = $this->getQuestionType($questionIdentifier, $assessment);
|
||||
$questionCcType = $questionCcType['cc'];
|
||||
|
||||
$varequal = $xpath->query('xmlns:conditionvar/xmlns:varequal', $response);
|
||||
$varequal = !empty($varequal->item(0)->nodeValue) ? $varequal->item(0)->nodeValue : '';
|
||||
|
||||
if (strtolower($varequal) == strtolower($identifier)) {
|
||||
$score = $xpath->query('xmlns:setvar', $response);
|
||||
$score = !empty($score->item(0)->nodeValue) ? $score->item(0)->nodeValue : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$score = empty($score) ? "0.0000000" : '1.0000000';
|
||||
|
||||
return $score;
|
||||
}
|
||||
|
||||
private function getQuestionType($identifier, $assessment)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath($assessment, Cc1p3Convert::getquizns());
|
||||
|
||||
$metadata = $xpath->query('//xmlns:item[@ident="'.$identifier.'"]/xmlns:itemmetadata/xmlns:qtimetadata/xmlns:qtimetadatafield');
|
||||
|
||||
foreach ($metadata as $field) {
|
||||
$fieldLabel = $xpath->query('xmlns:fieldlabel', $field);
|
||||
$fieldLabel = !empty($fieldLabel->item(0)->nodeValue) ? $fieldLabel->item(0)->nodeValue : '';
|
||||
|
||||
if ($fieldLabel == 'cc_profile') {
|
||||
$fieldEntry = $xpath->query('xmlns:fieldentry', $field);
|
||||
$type = !empty($fieldEntry->item(0)->nodeValue) ? $fieldEntry->item(0)->nodeValue : '';
|
||||
}
|
||||
}
|
||||
|
||||
$returnType = [];
|
||||
|
||||
$returnType['qtype'] = '';
|
||||
$returnType['cc'] = $type;
|
||||
|
||||
switch ($type) {
|
||||
case CC_QUIZ_MULTIPLE_CHOICE:
|
||||
$returnType['qtype'] = 'unique_answer';
|
||||
break;
|
||||
case CC_QUIZ_MULTIPLE_RESPONSE:
|
||||
$returnType['qtype'] = 'multiple_answer';
|
||||
break;
|
||||
case CC_QUIZ_FIB:
|
||||
$returnType['qtype'] = 'fib';
|
||||
break;
|
||||
case CC_QUIZ_ESSAY:
|
||||
$returnType['qtype'] = 'essay';
|
||||
break;
|
||||
}
|
||||
|
||||
return $returnType;
|
||||
}
|
||||
}
|
||||
207
main/common_cartridge/import/src/converter/Cc13Resource.php
Normal file
207
main/common_cartridge/import/src/converter/Cc13Resource.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class Cc13Resource extends Cc13Entities
|
||||
{
|
||||
public function generateData($resource_type)
|
||||
{
|
||||
$data = [];
|
||||
if (!empty(Cc1p3Convert::$instances['instances'][$resource_type])) {
|
||||
foreach (Cc1p3Convert::$instances['instances'][$resource_type] as $instance) {
|
||||
$data[] = $this->getResourceData($instance);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function storeLinks($links)
|
||||
{
|
||||
foreach ($links as $link) {
|
||||
$_POST['title'] = $link[1];
|
||||
$_POST['url'] = $link[4];
|
||||
$_POST['description'] = '';
|
||||
$_POST['category_id'] = 0;
|
||||
$_POST['target'] = '_blank';
|
||||
Link::addlinkcategory('link');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function storeDocuments($documents, $path)
|
||||
{
|
||||
$courseInfo = api_get_course_info();
|
||||
$sessionId = api_get_session_id();
|
||||
$groupId = api_get_group_id();
|
||||
$documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['directory'].'/document';
|
||||
|
||||
foreach ($documents as $document) {
|
||||
if ($document[2] == 'file') {
|
||||
$filepath = $path.DIRECTORY_SEPARATOR.$document[4];
|
||||
$files = [];
|
||||
$files['file']['name'] = $document[1];
|
||||
$files['file']['tmp_name'] = $filepath;
|
||||
$files['file']['type'] = mime_content_type($filepath);
|
||||
$files['file']['error'] = 0;
|
||||
$files['file']['size'] = filesize($filepath);
|
||||
$files['file']['from_file'] = true;
|
||||
$files['file']['move_file'] = true;
|
||||
$_POST['language'] = $courseInfo['language'];
|
||||
$_POST['cc_import'] = true;
|
||||
|
||||
$destDir = dirname($document[4]);
|
||||
// Create the subdirectory if necessary
|
||||
create_unexisting_directory(
|
||||
$courseInfo,
|
||||
api_get_user_id(),
|
||||
$sessionId,
|
||||
$groupId,
|
||||
null,
|
||||
$documentPath,
|
||||
'/commoncartridge/'.$destDir,
|
||||
$destDir,
|
||||
1
|
||||
);
|
||||
DocumentManager::upload_document(
|
||||
$files,
|
||||
'/commoncartridge/'.$destDir,
|
||||
$document[1],
|
||||
'',
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getResourceData($instance)
|
||||
{
|
||||
//var_dump($instance);
|
||||
|
||||
$xpath = Cc1p3Convert::newxPath(Cc1p3Convert::$manifest, Cc1p3Convert::$namespaces);
|
||||
$link = '';
|
||||
|
||||
if ($instance['common_cartridge_type'] == Cc1p3Convert::CC_TYPE_WEBCONTENT || $instance['common_cartridge_type'] == Cc1p3Convert::CC_TYPE_ASSOCIATED_CONTENT) {
|
||||
$resource = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.$instance['resource_identifier'].'"]/@href');
|
||||
if ($resource->length > 0) {
|
||||
$resource = !empty($resource->item(0)->nodeValue) ? $resource->item(0)->nodeValue : '';
|
||||
} else {
|
||||
$resource = '';
|
||||
}
|
||||
|
||||
if (empty($resource)) {
|
||||
unset($resource);
|
||||
// src has been set in CcBase::createInstances() based on the contents of <file href="...">
|
||||
$resource = $instance['src'];
|
||||
}
|
||||
if (!empty($resource)) {
|
||||
$link = $resource;
|
||||
}
|
||||
}
|
||||
|
||||
if ($instance['common_cartridge_type'] == Cc1p3Convert::CC_TYPE_WEBLINK) {
|
||||
// src has been set in CcBase::createInstances() based on the contents of <file href="...">
|
||||
$external_resource = $instance['src'];
|
||||
|
||||
if ($external_resource) {
|
||||
$resource = $this->loadXmlResource(Cc1p3Convert::$pathToManifestFolder.DIRECTORY_SEPARATOR.$external_resource);
|
||||
|
||||
if (!empty($resource)) {
|
||||
$xpath = Cc1p3Convert::newxPath($resource, Cc1p3Convert::$resourcens);
|
||||
$resource = $xpath->query('/wl:webLink/wl:url/@href');
|
||||
if ($resource->length > 0) {
|
||||
$rawlink = $resource->item(0)->nodeValue;
|
||||
if (!validateUrlSyntax($rawlink, 's+')) {
|
||||
$changed = rawurldecode($rawlink);
|
||||
if (validateUrlSyntax($changed, 's+')) {
|
||||
$link = $changed;
|
||||
} else {
|
||||
$link = 'http://invalidurldetected/';
|
||||
}
|
||||
} else {
|
||||
$link = htmlspecialchars(trim($rawlink), ENT_COMPAT, 'UTF-8', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$mod_type = 'file';
|
||||
$mod_options = 'objectframe';
|
||||
$mod_reference = $link;
|
||||
$mod_alltext = '';
|
||||
// detect if we are dealing with html file
|
||||
if (!empty($link) && ($instance['common_cartridge_type'] == Cc1p3Convert::CC_TYPE_WEBCONTENT)) {
|
||||
$ext = strtolower(pathinfo($link, PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['html', 'htm', 'xhtml'])) {
|
||||
$mod_type = 'html';
|
||||
//extract the content of the file and treat it
|
||||
$rootpath = realpath(Cc1p3Convert::$pathToManifestFolder);
|
||||
$htmlpath = realpath($rootpath.DIRECTORY_SEPARATOR.$link);
|
||||
$dirpath = dirname($htmlpath);
|
||||
if (file_exists($htmlpath)) {
|
||||
$fcontent = file_get_contents($htmlpath);
|
||||
$mod_alltext = $this->prepareContent($fcontent);
|
||||
$mod_reference = '';
|
||||
$mod_options = '';
|
||||
/**
|
||||
* try to handle embedded resources
|
||||
* images, linked static resources, applets, videos.
|
||||
*/
|
||||
$doc = new DOMDocument();
|
||||
$cdir = getcwd();
|
||||
chdir($dirpath);
|
||||
try {
|
||||
$doc->loadHTML($mod_alltext);
|
||||
$xpath = new DOMXPath($doc);
|
||||
$attributes = ['href', 'src', 'background', 'archive', 'code'];
|
||||
$qtemplate = "//*[@##][not(contains(@##,'://'))]/@##";
|
||||
$query = '';
|
||||
foreach ($attributes as $attrname) {
|
||||
if (!empty($query)) {
|
||||
$query .= " | ";
|
||||
}
|
||||
$query .= str_replace('##', $attrname, $qtemplate);
|
||||
}
|
||||
$list = $xpath->query($query);
|
||||
$searches = [];
|
||||
$replaces = [];
|
||||
foreach ($list as $resrc) {
|
||||
$rpath = $resrc->nodeValue;
|
||||
$rtp = realpath($rpath);
|
||||
if (($rtp !== false) && is_file($rtp)) {
|
||||
//file is there - we are in business
|
||||
$strip = str_replace("\\", "/", str_ireplace($rootpath, '/', $rtp));
|
||||
//$encoded_file = '$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $strip);
|
||||
$encoded_file = $strip;
|
||||
$searches[] = $resrc->nodeValue;
|
||||
$replaces[] = $encoded_file;
|
||||
}
|
||||
}
|
||||
$mod_alltext = str_replace($searches, $replaces, $mod_alltext);
|
||||
} catch (Exception $e) {
|
||||
//silence the complaints
|
||||
}
|
||||
chdir($cdir);
|
||||
$mod_alltext = self::safexml($mod_alltext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$values = [
|
||||
$instance['instance'],
|
||||
self::safexml($instance['title']),
|
||||
$mod_type,
|
||||
$mod_alltext,
|
||||
$mod_reference, // src or href
|
||||
$mod_options,
|
||||
];
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
248
main/common_cartridge/import/src/converter/CcEntities.php
Normal file
248
main/common_cartridge/import/src/converter/CcEntities.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class CcEntities
|
||||
{
|
||||
/**
|
||||
* Prepares convert for inclusion into XML.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function safexml($value)
|
||||
{
|
||||
$result = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'),
|
||||
ENT_NOQUOTES,
|
||||
'UTF-8',
|
||||
false);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function loadXmlResource($path_to_file)
|
||||
{
|
||||
$resource = new DOMDocument();
|
||||
|
||||
Cc1p3Convert::logAction('Load the XML resource file: '.$path_to_file);
|
||||
|
||||
if (!$resource->load($path_to_file)) {
|
||||
Cc1p3Convert::logAction('Cannot load the XML resource file: '.$path_to_file, false);
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
public function updateSources($html, $rootPath = '')
|
||||
{
|
||||
$document = $this->loadHtml($html);
|
||||
|
||||
$tags = ['img' => 'src', 'a' => 'href'];
|
||||
|
||||
foreach ($tags as $tag => $attribute) {
|
||||
$elements = $document->getElementsByTagName($tag);
|
||||
|
||||
foreach ($elements as $element) {
|
||||
$attribute_value = $element->getAttribute($attribute);
|
||||
$protocol = parse_url($attribute_value, PHP_URL_SCHEME);
|
||||
|
||||
if (empty($protocol)) {
|
||||
// The CC standard changed with the renaming of IMS Global to 1stEdTech, so we don't know what we're going to get
|
||||
$attribute_value = str_replace("\$1EdTech[-_]CC[-_]FILEBASE\$", "", $attribute_value);
|
||||
$attribute_value = str_replace("\$IMS[-_]CC[-_]FILEBASE\$", "", $attribute_value);
|
||||
$attribute_value = $this->fullPath($rootPath."/".$attribute_value, "/");
|
||||
//$attribute_value = "\$@FILEPHP@\$"."/".$attribute_value;
|
||||
}
|
||||
|
||||
$element->setAttribute($attribute, $attribute_value);
|
||||
}
|
||||
}
|
||||
|
||||
$html = $this->htmlInsidebody($document);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function fullPath($path, $dir_sep = DIRECTORY_SEPARATOR)
|
||||
{
|
||||
$token = '/\$(?:IMS|1EdTech)[-_]CC[-_]FILEBASE\$/';
|
||||
$path = preg_replace($token, '', $path);
|
||||
|
||||
if (is_string($path) && ($path != '')) {
|
||||
$dot_dir = '.';
|
||||
$up_dir = '..';
|
||||
$length = strlen($path);
|
||||
$rtemp = trim($path);
|
||||
$start = strrpos($path, $dir_sep);
|
||||
$can_continue = ($start !== false);
|
||||
$result = $can_continue ? '' : $path;
|
||||
$rcount = 0;
|
||||
|
||||
while ($can_continue) {
|
||||
$dir_part = ($start !== false) ? substr($rtemp, $start + 1, $length - $start) : $rtemp;
|
||||
$can_continue = ($dir_part !== false);
|
||||
|
||||
if ($can_continue) {
|
||||
if ($dir_part != $dot_dir) {
|
||||
if ($dir_part == $up_dir) {
|
||||
$rcount++;
|
||||
} else {
|
||||
if ($rcount > 0) {
|
||||
$rcount--;
|
||||
} else {
|
||||
$result = ($result == '') ? $dir_part : $dir_part.$dir_sep.$result;
|
||||
}
|
||||
}
|
||||
}
|
||||
$rtemp = substr($path, 0, $start);
|
||||
$start = strrpos($rtemp, $dir_sep);
|
||||
$can_continue = (($start !== false) || (strlen($rtemp) > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function includeTitles($html)
|
||||
{
|
||||
$document = $this->loadHtml($html);
|
||||
|
||||
$images = $document->getElementsByTagName('img');
|
||||
|
||||
foreach ($images as $image) {
|
||||
$src = $image->getAttribute('src');
|
||||
$alt = $image->getAttribute('alt');
|
||||
$title = $image->getAttribute('title');
|
||||
|
||||
$filename = pathinfo($src);
|
||||
$filename = $filename['filename'];
|
||||
|
||||
$alt = empty($alt) ? $filename : $alt;
|
||||
$title = empty($title) ? $filename : $title;
|
||||
|
||||
$image->setAttribute('alt', $alt);
|
||||
$image->setAttribute('title', $title);
|
||||
}
|
||||
|
||||
$html = $this->htmlInsidebody($document);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function getExternalXml($identifier)
|
||||
{
|
||||
$xpath = Cc1p3Convert::newxPath(Cc1p3Convert::$manifest, Cc1p3Convert::$namespaces);
|
||||
|
||||
$files = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="'.
|
||||
$identifier.'"]/imscc:file/@href');
|
||||
|
||||
if (empty($files)) {
|
||||
$response = '';
|
||||
} else {
|
||||
$response = $files->item(0)->nodeValue;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function generateRandomString($length = 6)
|
||||
{
|
||||
$response = '';
|
||||
$source = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
if ($length > 0) {
|
||||
$response = '';
|
||||
$source = str_split($source, 1);
|
||||
|
||||
for ($i = 1; $i <= $length; $i++) {
|
||||
$num = mt_rand(1, count($source));
|
||||
$response .= $source[$num - 1];
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function truncateText($text, $max, $remove_html)
|
||||
{
|
||||
if ($max > 10) {
|
||||
$text = substr($text, 0, ($max - 6)).' [...]';
|
||||
} else {
|
||||
$text = substr($text, 0, $max);
|
||||
}
|
||||
|
||||
$text = $remove_html ? strip_tags($text) : $text;
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
protected function prepareContent($content)
|
||||
{
|
||||
$result = $content;
|
||||
if (empty($result)) {
|
||||
return '';
|
||||
}
|
||||
$encoding = null;
|
||||
$xml_error = new LibxmlErrorsMgr();
|
||||
$dom = new DOMDocument();
|
||||
$dom->validateOnParse = false;
|
||||
$dom->strictErrorChecking = false;
|
||||
if ($dom->loadHTML($content)) {
|
||||
$encoding = $dom->xmlEncoding;
|
||||
}
|
||||
if (empty($encoding)) {
|
||||
$encoding = mb_detect_encoding($content, 'auto', true);
|
||||
}
|
||||
if (!empty($encoding) && !mb_check_encoding($content, 'UTF-8')) {
|
||||
$result = mb_convert_encoding($content, 'UTF-8', $encoding);
|
||||
}
|
||||
|
||||
// See if we can strip off body tag and anything outside of it.
|
||||
foreach (['body', 'html'] as $tagname) {
|
||||
$regex = str_replace('##', $tagname, "/<##[^>]*>(.+)<\/##>/is");
|
||||
if (preg_match($regex, $result, $matches)) {
|
||||
$result = $matches[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $html
|
||||
*
|
||||
* @return DOMDocument
|
||||
*/
|
||||
private function loadHtml($html)
|
||||
{
|
||||
// Need to make sure that the html passed has charset meta tag.
|
||||
$metatag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
|
||||
if (strpos($html, $metatag) === false) {
|
||||
$html = '<html><head>'.$metatag.'</head><body>'.$html.'</body></html>';
|
||||
}
|
||||
|
||||
$document = new DOMDocument();
|
||||
@$document->loadHTML($html);
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DOMDocument $domdocument
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function htmlInsidebody($domdocument)
|
||||
{
|
||||
$html = '';
|
||||
$bodyitems = $domdocument->getElementsByTagName('body');
|
||||
if ($bodyitems->length > 0) {
|
||||
$body = $bodyitems->item(0);
|
||||
$html = str_ireplace(['<body>', '</body>'], '', $body->C14N());
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
46
main/common_cartridge/import/src/inc/constants.php
Normal file
46
main/common_cartridge/import/src/inc/constants.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
// GENERAL PARAMETERS ************************************************************************************************* //
|
||||
define('ROOT_DEEP', 2);
|
||||
|
||||
// PACKAGES FORMATS *************************************************************************************************** //
|
||||
define('FORMAT_UNKNOWN', 'NA');
|
||||
define('FORMAT_COMMON_CARTRIDGE', 'CC');
|
||||
define('FORMAT_BLACK_BOARD', 'BB');
|
||||
|
||||
// FORMATS NAMESPACES ************************************************************************************************* //
|
||||
define('NS_COMMON_CARTRIDGE', 'http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1');
|
||||
define('NS_BLACK_BOARD', 'http://www.blackboard.com/content-packaging');
|
||||
|
||||
// CC RESOURCES TYPE ************************************************************************************************** //
|
||||
define('CC_TYPE_FORUM', 'imsdt_xmlv1p3');
|
||||
define('CC_TYPE_QUIZ', 'imsqti_xmlv1p3/imscc_xmlv1p3/assessment');
|
||||
define('CC_TYPE_QUESTION_BANK', 'imsqti_xmlv1p3/imscc_xmlv1p3/question-bank');
|
||||
define('CC_TYPE_WEBLINK', 'imswl_xmlv1p3');
|
||||
define('CC_TYPE_WEBCONTENT', 'webcontent');
|
||||
define('CC_TYPE_ASSOCIATED_CONTENT', 'associatedcontent/imscc_xmlv1p3/learning-application-resource');
|
||||
define('CC_TYPE_BASICLTI', 'imsbasiclti_xmlv1p3');
|
||||
define('CC_TYPE_EMPTY', '');
|
||||
|
||||
// COURSE RESOURCES TYPE ********************************************************************************************** //
|
||||
define('TOOL_TYPE_FORUM', 'forum');
|
||||
define('TOOL_TYPE_QUIZ', 'quiz');
|
||||
define('TOOL_TYPE_DOCUMENT', 'document');
|
||||
define('TOOL_TYPE_WEBLINK', 'link');
|
||||
|
||||
// UNKNOWN TYPE ******************************************************************************************************* //
|
||||
define('TYPE_UNKNOWN', '[UNKNOWN]');
|
||||
|
||||
// CC QUESTIONS TYPES ************************************************************************************************* //
|
||||
define('CC_QUIZ_MULTIPLE_CHOICE', 'cc.multiple_choice.v0p1');
|
||||
define('CC_QUIZ_TRUE_FALSE', 'cc.true_false.v0p1');
|
||||
define('CC_QUIZ_FIB', 'cc.fib.v0p1');
|
||||
define('CC_QUIZ_MULTIPLE_RESPONSE', 'cc.multiple_response.v0p1');
|
||||
define('CC_QUIZ_PATTERN_MACHT', 'cc.pattern_match.v0p1');
|
||||
define('CC_QUIZ_ESSAY', 'cc.essay.v0p1');
|
||||
|
||||
//COURSE QUESTIONS TYPES ********************************************************************************************** //
|
||||
define('TOOL_QUIZ_MULTIPLE_CHOICE', 'multichoice');
|
||||
define('TOOL_QUIZ_MULTIANSWER', 'multianswer');
|
||||
define('TOOL_QUIZ_MULTIPLE_RESPONSE', 'multichoice');
|
||||
447
main/common_cartridge/import/src/lib/validateurlsyntax.php
Normal file
447
main/common_cartridge/import/src/lib/validateurlsyntax.php
Normal file
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/lib/validateurlsyntax.php under GNU/GPL license */
|
||||
|
||||
/**
|
||||
* BEGINNING OF validateUrlSyntax() function.
|
||||
*/
|
||||
function validateUrlSyntax($urladdr, $options = "")
|
||||
{
|
||||
// Force Options parameter to be lower case
|
||||
// DISABLED PERMAMENTLY - OK to remove from code
|
||||
// $options = strtolower($options);
|
||||
|
||||
// Check Options Parameter
|
||||
if (!preg_match('/^([sHSEFRuPaIpfqr][+?-])*$/', $options)) {
|
||||
trigger_error("Options attribute malformed", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Set Options Array, set defaults if options are not specified
|
||||
// Scheme
|
||||
if (strpos($options, 's') === false) {
|
||||
$aOptions['s'] = '?';
|
||||
} else {
|
||||
$aOptions['s'] = substr($options, strpos($options, 's') + 1, 1);
|
||||
}
|
||||
// http://
|
||||
if (strpos($options, 'H') === false) {
|
||||
$aOptions['H'] = '?';
|
||||
} else {
|
||||
$aOptions['H'] = substr($options, strpos($options, 'H') + 1, 1);
|
||||
}
|
||||
// https:// (SSL)
|
||||
if (strpos($options, 'S') === false) {
|
||||
$aOptions['S'] = '?';
|
||||
} else {
|
||||
$aOptions['S'] = substr($options, strpos($options, 'S') + 1, 1);
|
||||
}
|
||||
// mailto: (email)
|
||||
if (strpos($options, 'E') === false) {
|
||||
$aOptions['E'] = '-';
|
||||
} else {
|
||||
$aOptions['E'] = substr($options, strpos($options, 'E') + 1, 1);
|
||||
}
|
||||
// ftp://
|
||||
if (strpos($options, 'F') === false) {
|
||||
$aOptions['F'] = '-';
|
||||
} else {
|
||||
$aOptions['F'] = substr($options, strpos($options, 'F') + 1, 1);
|
||||
}
|
||||
// rtmp://
|
||||
if (strpos($options, 'R') === false) {
|
||||
$aOptions['R'] = '-';
|
||||
} else {
|
||||
$aOptions['R'] = substr($options, strpos($options, 'R') + 1, 1);
|
||||
}
|
||||
// User section
|
||||
if (strpos($options, 'u') === false) {
|
||||
$aOptions['u'] = '?';
|
||||
} else {
|
||||
$aOptions['u'] = substr($options, strpos($options, 'u') + 1, 1);
|
||||
}
|
||||
// Password in user section
|
||||
if (strpos($options, 'P') === false) {
|
||||
$aOptions['P'] = '?';
|
||||
} else {
|
||||
$aOptions['P'] = substr($options, strpos($options, 'P') + 1, 1);
|
||||
}
|
||||
// Address Section
|
||||
if (strpos($options, 'a') === false) {
|
||||
$aOptions['a'] = '+';
|
||||
} else {
|
||||
$aOptions['a'] = substr($options, strpos($options, 'a') + 1, 1);
|
||||
}
|
||||
// IP Address in address section
|
||||
if (strpos($options, 'I') === false) {
|
||||
$aOptions['I'] = '?';
|
||||
} else {
|
||||
$aOptions['I'] = substr($options, strpos($options, 'I') + 1, 1);
|
||||
}
|
||||
// Port number
|
||||
if (strpos($options, 'p') === false) {
|
||||
$aOptions['p'] = '?';
|
||||
} else {
|
||||
$aOptions['p'] = substr($options, strpos($options, 'p') + 1, 1);
|
||||
}
|
||||
// File Path
|
||||
if (strpos($options, 'f') === false) {
|
||||
$aOptions['f'] = '?';
|
||||
} else {
|
||||
$aOptions['f'] = substr($options, strpos($options, 'f') + 1, 1);
|
||||
}
|
||||
// Query Section
|
||||
if (strpos($options, 'q') === false) {
|
||||
$aOptions['q'] = '?';
|
||||
} else {
|
||||
$aOptions['q'] = substr($options, strpos($options, 'q') + 1, 1);
|
||||
}
|
||||
// Fragment (Anchor)
|
||||
if (strpos($options, 'r') === false) {
|
||||
$aOptions['r'] = '?';
|
||||
} else {
|
||||
$aOptions['r'] = substr($options, strpos($options, 'r') + 1, 1);
|
||||
}
|
||||
|
||||
// Loop through options array, to search for and replace "-" to "{0}" and "+" to ""
|
||||
foreach ($aOptions as $key => $value) {
|
||||
if ($value == '-') {
|
||||
$aOptions[$key] = '{0}';
|
||||
}
|
||||
if ($value == '+') {
|
||||
$aOptions[$key] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUGGING - Unescape following line to display to screen current option values
|
||||
// echo '<pre>'; print_r($aOptions); echo '</pre>';
|
||||
|
||||
// Preset Allowed Characters
|
||||
$alphanum = '[a-zA-Z0-9]'; // Alpha Numeric
|
||||
$unreserved = '[a-zA-Z0-9_.!~*'.'\''.'()-]';
|
||||
$escaped = '(%[0-9a-fA-F]{2})'; // Escape sequence - In Hex - %6d would be a 'm'
|
||||
$reserved = '[;/?:@&=+$,]'; // Special characters in the URI
|
||||
|
||||
// Beginning Regular Expression
|
||||
// Scheme - Allows for 'http://', 'https://', 'mailto:', 'ftp://' or 'rtmp://'
|
||||
$scheme = '(';
|
||||
if ($aOptions['H'] === '') {
|
||||
$scheme .= 'http://';
|
||||
} elseif ($aOptions['S'] === '') {
|
||||
$scheme .= 'https://';
|
||||
} elseif ($aOptions['E'] === '') {
|
||||
$scheme .= 'mailto:';
|
||||
} elseif ($aOptions['F'] === '') {
|
||||
$scheme .= 'ftp://';
|
||||
} elseif ($aOptions['R'] === '') {
|
||||
$scheme .= 'rtmp://';
|
||||
} else {
|
||||
if ($aOptions['H'] === '?') {
|
||||
$scheme .= '|(http://)';
|
||||
}
|
||||
if ($aOptions['S'] === '?') {
|
||||
$scheme .= '|(https://)';
|
||||
}
|
||||
if ($aOptions['E'] === '?') {
|
||||
$scheme .= '|(mailto:)';
|
||||
}
|
||||
if ($aOptions['F'] === '?') {
|
||||
$scheme .= '|(ftp://)';
|
||||
}
|
||||
if ($aOptions['R'] === '?') {
|
||||
$scheme .= '|(rtmp://)';
|
||||
}
|
||||
$scheme = str_replace('(|', '(', $scheme); // fix first pipe
|
||||
}
|
||||
$scheme .= ')'.$aOptions['s'];
|
||||
// End setting scheme
|
||||
|
||||
// User Info - Allows for 'username@' or 'username:password@'. Note: contrary to rfc, I removed ':' from username section, allowing it only in password.
|
||||
// /---------------- Username -----------------------\ /-------------------------------- Password ------------------------------\
|
||||
$userinfo = '(('.$unreserved.'|'.$escaped.'|[;&=+$,]'.')+(:('.$unreserved.'|'.$escaped.'|[;:&=+$,]'.')+)'.$aOptions['P'].'@)'.$aOptions['u'];
|
||||
|
||||
// IP ADDRESS - Allows 0.0.0.0 to 255.255.255.255
|
||||
$ipaddress = '((((2(([0-4][0-9])|(5[0-5])))|([01]?[0-9]?[0-9]))\.){3}((2(([0-4][0-9])|(5[0-5])))|([01]?[0-9]?[0-9])))';
|
||||
|
||||
// Tertiary Domain(s) - Optional - Multi - Although some sites may use other characters, the RFC says tertiary domains have the same naming restrictions as second level domains
|
||||
$domain_tertiary = '('.$alphanum.'(([a-zA-Z0-9-]{0,62})'.$alphanum.')?\.)*';
|
||||
$domain_toplevel = '([a-zA-Z](([a-zA-Z0-9-]*)[a-zA-Z0-9])?)';
|
||||
|
||||
if ($aOptions['I'] === '{0}') { // IP Address Not Allowed
|
||||
$address = '('.$domain_tertiary. /* MDL-9295 $domain_secondary . */ $domain_toplevel.')';
|
||||
} elseif ($aOptions['I'] === '') { // IP Address Required
|
||||
$address = '('.$ipaddress.')';
|
||||
} else { // IP Address Optional
|
||||
$address = '(('.$ipaddress.')|('.$domain_tertiary. /* MDL-9295 $domain_secondary . */ $domain_toplevel.'))';
|
||||
}
|
||||
$address = $address.$aOptions['a'];
|
||||
|
||||
// Port Number - :80 or :8080 or :65534 Allows range of :0 to :65535
|
||||
// (0-59999) |(60000-64999) |(65000-65499) |(65500-65529) |(65530-65535)
|
||||
$port_number = '(:(([0-5]?[0-9]{1,4})|(6[0-4][0-9]{3})|(65[0-4][0-9]{2})|(655[0-2][0-9])|(6553[0-5])))'.$aOptions['p'];
|
||||
|
||||
// Path - Can be as simple as '/' or have multiple folders and filenames
|
||||
$path = '(/((;)?('.$unreserved.'|'.$escaped.'|'.'[:@&=+$,]'.')+(/)?)*)'.$aOptions['f'];
|
||||
|
||||
// Query Section - Accepts ?var1=value1&var2=value2 or ?2393,1221 and much more
|
||||
$querystring = '(\?('.$reserved.'|'.$unreserved.'|'.$escaped.')*)'.$aOptions['q'];
|
||||
|
||||
// Fragment Section - Accepts anchors such as #top
|
||||
$fragment = '(\#('.$reserved.'|'.$unreserved.'|'.$escaped.')*)'.$aOptions['r'];
|
||||
|
||||
// Building Regular Expression
|
||||
$regexp = '#^'.$scheme.$userinfo.$address.$port_number.$path.$querystring.$fragment.'$#i';
|
||||
|
||||
// DEBUGGING - Uncomment Line Below To Display The Regular Expression Built
|
||||
// echo '<pre>' . htmlentities(wordwrap($regexp,70,"\n",1)) . '</pre>';
|
||||
|
||||
// Running the regular expression
|
||||
if (preg_match($regexp, $urladdr)) {
|
||||
return true; // The domain passed
|
||||
} else {
|
||||
return false; // The domain didn't pass the expression
|
||||
}
|
||||
} // END Function validateUrlSyntax()
|
||||
|
||||
/**
|
||||
* About ValidateEmailSyntax():
|
||||
* This function uses the ValidateUrlSyntax() function to easily check the
|
||||
* syntax of an email address. It accepts the same options as ValidateURLSyntax
|
||||
* but defaults them for email addresses.
|
||||
*
|
||||
* Released under same license as validateUrlSyntax()
|
||||
*/
|
||||
function validateEmailSyntax($emailaddr, $options = "")
|
||||
{
|
||||
// Check Options Parameter
|
||||
if (!preg_match('/^([sHSEFuPaIpfqr][+?-])*$/', $options)) {
|
||||
trigger_error("Options attribute malformed", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Set Options Array, set defaults if options are not specified
|
||||
// Scheme
|
||||
if (strpos($options, 's') === false) {
|
||||
$aOptions['s'] = '-';
|
||||
} else {
|
||||
$aOptions['s'] = substr($options, strpos($options, 's') + 1, 1);
|
||||
}
|
||||
// http://
|
||||
if (strpos($options, 'H') === false) {
|
||||
$aOptions['H'] = '-';
|
||||
} else {
|
||||
$aOptions['H'] = substr($options, strpos($options, 'H') + 1, 1);
|
||||
}
|
||||
// https:// (SSL)
|
||||
if (strpos($options, 'S') === false) {
|
||||
$aOptions['S'] = '-';
|
||||
} else {
|
||||
$aOptions['S'] = substr($options, strpos($options, 'S') + 1, 1);
|
||||
}
|
||||
// mailto: (email)
|
||||
if (strpos($options, 'E') === false) {
|
||||
$aOptions['E'] = '?';
|
||||
} else {
|
||||
$aOptions['E'] = substr($options, strpos($options, 'E') + 1, 1);
|
||||
}
|
||||
// ftp://
|
||||
if (strpos($options, 'F') === false) {
|
||||
$aOptions['F'] = '-';
|
||||
} else {
|
||||
$aOptions['F'] = substr($options, strpos($options, 'F') + 1, 1);
|
||||
}
|
||||
// User section
|
||||
if (strpos($options, 'u') === false) {
|
||||
$aOptions['u'] = '+';
|
||||
} else {
|
||||
$aOptions['u'] = substr($options, strpos($options, 'u') + 1, 1);
|
||||
}
|
||||
// Password in user section
|
||||
if (strpos($options, 'P') === false) {
|
||||
$aOptions['P'] = '-';
|
||||
} else {
|
||||
$aOptions['P'] = substr($options, strpos($options, 'P') + 1, 1);
|
||||
}
|
||||
// Address Section
|
||||
if (strpos($options, 'a') === false) {
|
||||
$aOptions['a'] = '+';
|
||||
} else {
|
||||
$aOptions['a'] = substr($options, strpos($options, 'a') + 1, 1);
|
||||
}
|
||||
// IP Address in address section
|
||||
if (strpos($options, 'I') === false) {
|
||||
$aOptions['I'] = '-';
|
||||
} else {
|
||||
$aOptions['I'] = substr($options, strpos($options, 'I') + 1, 1);
|
||||
}
|
||||
// Port number
|
||||
if (strpos($options, 'p') === false) {
|
||||
$aOptions['p'] = '-';
|
||||
} else {
|
||||
$aOptions['p'] = substr($options, strpos($options, 'p') + 1, 1);
|
||||
}
|
||||
// File Path
|
||||
if (strpos($options, 'f') === false) {
|
||||
$aOptions['f'] = '-';
|
||||
} else {
|
||||
$aOptions['f'] = substr($options, strpos($options, 'f') + 1, 1);
|
||||
}
|
||||
// Query Section
|
||||
if (strpos($options, 'q') === false) {
|
||||
$aOptions['q'] = '-';
|
||||
} else {
|
||||
$aOptions['q'] = substr($options, strpos($options, 'q') + 1, 1);
|
||||
}
|
||||
// Fragment (Anchor)
|
||||
if (strpos($options, 'r') === false) {
|
||||
$aOptions['r'] = '-';
|
||||
} else {
|
||||
$aOptions['r'] = substr($options, strpos($options, 'r') + 1, 1);
|
||||
}
|
||||
|
||||
// Generate options
|
||||
$newoptions = '';
|
||||
foreach ($aOptions as $key => $value) {
|
||||
$newoptions .= $key.$value;
|
||||
}
|
||||
|
||||
// DEBUGGING - Uncomment line below to display generated options
|
||||
// echo '<pre>' . $newoptions . '</pre>';
|
||||
|
||||
// Send to validateUrlSyntax() and return result
|
||||
return validateUrlSyntax($emailaddr, $newoptions);
|
||||
} // END Function validateEmailSyntax()
|
||||
|
||||
/**
|
||||
* About ValidateFtpSyntax():
|
||||
* This function uses the ValidateUrlSyntax() function to easily check the
|
||||
* syntax of an FTP address. It accepts the same options as ValidateURLSyntax
|
||||
* but defaults them for FTP addresses.
|
||||
*
|
||||
* Usage:
|
||||
* <code>
|
||||
* validateFtpSyntax( url_to_check[, options])
|
||||
* </code>
|
||||
* url_to_check - string - The url to check
|
||||
*
|
||||
* options - string - A optional string of options to set which parts of
|
||||
* the url are required, optional, or not allowed. Each option
|
||||
* must be followed by a "+" for required, "?" for optional, or
|
||||
* "-" for not allowed. See ValidateUrlSyntax() docs for option list.
|
||||
*
|
||||
* The default options are changed to:
|
||||
* s?H-S-E-F+u?P?a+I?p?f?q-r-
|
||||
*
|
||||
* Examples:
|
||||
* <code>
|
||||
* validateFtpSyntax('ftp://netscape.com')
|
||||
* validateFtpSyntax('moz:iesucks@netscape.com')
|
||||
* validateFtpSyntax('ftp://netscape.com:2121/browsers/ns7/', 'u-')
|
||||
* </code>
|
||||
*
|
||||
* Author(s):
|
||||
* Rod Apeldoorn - rod(at)canowhoopass(dot)com
|
||||
*
|
||||
*
|
||||
* Homepage:
|
||||
* http://www.canowhoopass.com/
|
||||
*
|
||||
*
|
||||
* License:
|
||||
* Copyright 2004 - Rod Apeldoorn
|
||||
*
|
||||
* Released under same license as validateUrlSyntax(). For details, contact me.
|
||||
*/
|
||||
function validateFtpSyntax($ftpaddr, $options = "")
|
||||
{
|
||||
// Check Options Parameter
|
||||
if (!preg_match('/^([sHSEFuPaIpfqr][+?-])*$/', $options)) {
|
||||
trigger_error("Options attribute malformed", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Set Options Array, set defaults if options are not specified
|
||||
// Scheme
|
||||
if (strpos($options, 's') === false) {
|
||||
$aOptions['s'] = '?';
|
||||
} else {
|
||||
$aOptions['s'] = substr($options, strpos($options, 's') + 1, 1);
|
||||
}
|
||||
// http://
|
||||
if (strpos($options, 'H') === false) {
|
||||
$aOptions['H'] = '-';
|
||||
} else {
|
||||
$aOptions['H'] = substr($options, strpos($options, 'H') + 1, 1);
|
||||
}
|
||||
// https:// (SSL)
|
||||
if (strpos($options, 'S') === false) {
|
||||
$aOptions['S'] = '-';
|
||||
} else {
|
||||
$aOptions['S'] = substr($options, strpos($options, 'S') + 1, 1);
|
||||
}
|
||||
// mailto: (email)
|
||||
if (strpos($options, 'E') === false) {
|
||||
$aOptions['E'] = '-';
|
||||
} else {
|
||||
$aOptions['E'] = substr($options, strpos($options, 'E') + 1, 1);
|
||||
}
|
||||
// ftp://
|
||||
if (strpos($options, 'F') === false) {
|
||||
$aOptions['F'] = '+';
|
||||
} else {
|
||||
$aOptions['F'] = substr($options, strpos($options, 'F') + 1, 1);
|
||||
}
|
||||
// User section
|
||||
if (strpos($options, 'u') === false) {
|
||||
$aOptions['u'] = '?';
|
||||
} else {
|
||||
$aOptions['u'] = substr($options, strpos($options, 'u') + 1, 1);
|
||||
}
|
||||
// Password in user section
|
||||
if (strpos($options, 'P') === false) {
|
||||
$aOptions['P'] = '?';
|
||||
} else {
|
||||
$aOptions['P'] = substr($options, strpos($options, 'P') + 1, 1);
|
||||
}
|
||||
// Address Section
|
||||
if (strpos($options, 'a') === false) {
|
||||
$aOptions['a'] = '+';
|
||||
} else {
|
||||
$aOptions['a'] = substr($options, strpos($options, 'a') + 1, 1);
|
||||
}
|
||||
// IP Address in address section
|
||||
if (strpos($options, 'I') === false) {
|
||||
$aOptions['I'] = '?';
|
||||
} else {
|
||||
$aOptions['I'] = substr($options, strpos($options, 'I') + 1, 1);
|
||||
}
|
||||
// Port number
|
||||
if (strpos($options, 'p') === false) {
|
||||
$aOptions['p'] = '?';
|
||||
} else {
|
||||
$aOptions['p'] = substr($options, strpos($options, 'p') + 1, 1);
|
||||
}
|
||||
// File Path
|
||||
if (strpos($options, 'f') === false) {
|
||||
$aOptions['f'] = '?';
|
||||
} else {
|
||||
$aOptions['f'] = substr($options, strpos($options, 'f') + 1, 1);
|
||||
}
|
||||
// Query Section
|
||||
if (strpos($options, 'q') === false) {
|
||||
$aOptions['q'] = '-';
|
||||
} else {
|
||||
$aOptions['q'] = substr($options, strpos($options, 'q') + 1, 1);
|
||||
}
|
||||
// Fragment (Anchor)
|
||||
if (strpos($options, 'r') === false) {
|
||||
$aOptions['r'] = '-';
|
||||
} else {
|
||||
$aOptions['r'] = substr($options, strpos($options, 'r') + 1, 1);
|
||||
}
|
||||
|
||||
// Generate options
|
||||
$newoptions = '';
|
||||
foreach ($aOptions as $key => $value) {
|
||||
$newoptions .= $key.$value;
|
||||
}
|
||||
|
||||
// Send to validateUrlSyntax() and return result
|
||||
return validateUrlSyntax($ftpaddr, $newoptions);
|
||||
} // END Function validateFtpSyntax()
|
||||
Reference in New Issue
Block a user