Add hotspot/delineation question image using resources.

pull/3064/head
Julio Montoya 5 years ago
parent b729e696ee
commit af0fd1a49e
  1. 2
      assets/js/annotation.js
  2. 2
      assets/js/vendor.js
  3. 5
      main/exercise/Annotation.php
  4. 26
      main/exercise/annotation_user.php
  5. 2
      main/exercise/exercise_report.php
  6. 2
      main/exercise/exercise_result.php
  7. 2
      main/exercise/exercise_show.php
  8. 2
      main/exercise/exercise_submit.php
  9. 5
      main/exercise/hotspot.class.php
  10. 28
      main/exercise/hotspot.inc.php
  11. 24
      main/exercise/hotspot_actionscript.as.php
  12. 19
      main/exercise/hotspot_actionscript_admin.as.php
  13. 4
      main/exercise/hotspot_admin.inc.php
  14. 27
      main/exercise/hotspot_answers.as.php
  15. 181
      main/exercise/question.class.php
  16. 2
      main/exercise/result.php
  17. 19
      main/inc/lib/exercise.lib.php
  18. 5
      src/CoreBundle/Entity/Resource/ResourceFile.php
  19. 26
      src/CoreBundle/Repository/ResourceRepository.php
  20. 9
      src/CourseBundle/Repository/CQuizQuestionRepository.php

@ -333,7 +333,7 @@
},
userSettings
),
xhrUrl = 'exercise/annotation_user.php?' + _p.web_cid_query,
xhrUrl = 'exercise/annotation_user.php?',
$container = $('#annotation-canvas-' + settings.questionId);
$.getJSON(settings.relPath + xhrUrl, {
question_id: parseInt(settings.questionId),

@ -34,6 +34,8 @@ require('jquery-ui-timepicker-addon');
require('@fancyapps/fancybox/dist/jquery.fancybox.js');
require('@fancyapps/fancybox/src/js/media.js');
require('jquery-contextmenu/dist/jquery.contextMenu.js');
require('./annotation.js');
// require('video.js');
// doesn't work with webpack added directly in /public/libs folder

@ -92,8 +92,6 @@ class Annotation extends Question
parent::processCreation($form, $exercise);
if (!empty($fileInfo['tmp_name'])) {
$result = $this->uploadPicture($fileInfo['tmp_name']);
if ($result) {
$this->weighting = $form->getSubmitValue('weighting');
$this->save($exercise);
@ -103,9 +101,6 @@ class Annotation extends Question
return false;
}
return false;
}
/**
* @param FormValidator $form
*/

@ -1,6 +1,9 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
session_cache_limiter('none');
require_once __DIR__.'/../inc/global.inc.php';
@ -15,18 +18,25 @@ if (empty($courseInfo)) {
}
$objQuestion = Question::read($questionId, $courseInfo);
$documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$picturePath = $documentPath.'/images';
$pictureSize = getimagesize($picturePath.'/'.$objQuestion->getPictureFilename());
$pictureWidth = $pictureSize[0];
$pictureHeight = $pictureSize[1];
$questionRepo = Container::getQuestionRepository();
/** @var CQuizQuestion $objQuestion */
$objQuestion = $questionRepo->find($questionId);
$answer_type = $objQuestion->getType(); //very important
$resourceFile = $objQuestion->getResourceNode()->getResourceFile();
$pictureWidth = $resourceFile->getWidth();
$pictureHeight = $resourceFile->getHeight();
$imagePath = $questionRepo->getHotSpotImageUrl($objQuestion);
$data = [
'use' => 'user',
'image' => [
'path' => $objQuestion->selectPicturePath(),
'width' => $pictureSize[0],
'height' => $pictureSize[1],
'path' => $imagePath,
'width' => $pictureWidth,
'height' => $pictureHeight,
],
'answers' => [
'paths' => [],

@ -53,7 +53,7 @@ $TBL_TRACK_ATTEMPT_RECORDING = Database::get_main_table(TABLE_STATISTIC_TRACK_E_
$TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
$allowCoachFeedbackExercises = api_get_setting('allow_coach_feedback_exercises') === 'true';
$course_id = api_get_course_int_id();
$exercise_id = isset($_REQUEST['exerciseId']) ? (int) $_REQUEST['exerciseId'] : 0;
$exercise_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : 0;
$locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
$sessionId = api_get_session_id();

@ -70,7 +70,7 @@ $interbreadcrumb[] = [
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
$htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
//$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
if (api_get_configuration_value('quiz_prevent_copy_paste')) {
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'jquery.nocopypaste.js"></script>';
}

@ -161,7 +161,7 @@ $this_section = SECTION_COURSES;
$htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
//$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
if ($allowRecordAudio && $allowTeacherCommentAudio) {
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'rtc/RecordRTC.js"></script>';

@ -67,7 +67,7 @@ $htmlHeadXtra[] = $js;
$htmlHeadXtra[] = api_get_build_js('exercise.js');
$htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
//$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
if (api_get_configuration_value('quiz_prevent_copy_paste')) {
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'jquery.nocopypaste.js"></script>';
}

@ -81,12 +81,7 @@ class HotSpot extends Question
parent::processCreation($form, $exercise);
if (!empty($fileInfo['tmp_name'])) {
$result = $this->uploadPicture($fileInfo);
if ($result) {
$this->save($exercise);
return true;
}
}
return false;

@ -1,28 +0,0 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Hotspot languae conversion.
*
* @package chamilo.exercise
*/
/**
* Code.
*/
session_cache_limiter('none');
$language_file = 'hotspot';
require_once __DIR__.'/../inc/global.inc.php';
header('Content-Type: text/html; charset=UTF-8');
$file = file(api_get_path(SYS_LANG_PATH).'english/hotspot.inc.php');
foreach ($file as &$value) {
$variable = explode('=', $value, 2);
if (count($variable) > 1) {
$variable = substr(trim($variable[0]), 1);
$variable = '&'.$variable.'='.api_utf8_encode(get_lang($variable)).' ';
echo $variable;
}
}

@ -1,6 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
use ChamiloSession as Session;
/**
@ -22,15 +24,19 @@ $_course = api_get_course_info();
require api_get_path(LIBRARY_PATH).'geometry.lib.php';
// set vars
$questionId = intval($_GET['modifyAnswers']);
$exerciseId = isset($_GET['exe_id']) ? intval($_GET['exe_id']) : 0;
$objQuestion = Question::read($questionId);
$answer_type = $objQuestion->selectType(); //very important
$questionId = (int)$_GET['modifyAnswers'];
$exerciseId = isset($_GET['exe_id']) ? (int)$_GET['exe_id'] : 0;
$questionRepo = Container::getQuestionRepository();
/** @var CQuizQuestion $objQuestion */
$objQuestion = $questionRepo->find($questionId);
$answer_type = $objQuestion->getType(); //very important
$TBL_ANSWERS = Database::get_course_table(TABLE_QUIZ_ANSWER);
$picture = $objQuestion->getPicture();
$pictureName = $objQuestion->getPictureFilename();
$pictureWidth = $picture->getResourceNode()->getResourceFile()->getWidth();
$pictureHeight = $picture->getResourceNode()->getResourceFile()->getHeight();
$resourceFile = $objQuestion->getResourceNode()->getResourceFile();
$pictureWidth = $resourceFile->getWidth();
$pictureHeight = $resourceFile->getHeight();
$imagePath = $questionRepo->getHotSpotImageUrl($objQuestion);
$course_id = api_get_course_int_id();
@ -72,7 +78,7 @@ $data['lang'] = [
'ClosePolygon' => get_lang('ClosePolygon'),
'DelineationStatus1' => get_lang('DelineationStatus1'),
];
$data['image'] = $objQuestion->selectPicturePath();
$data['image'] = $imagePath;
$data['image_width'] = $pictureWidth;
$data['image_height'] = $pictureHeight;
$data['courseCode'] = $_course['path'];

@ -1,6 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
use ChamiloSession as Session;
/**
@ -23,11 +25,14 @@ if (!$isAllowedToEdit) {
$_course = api_get_course_info();
$questionId = isset($_GET['modifyAnswers']) ? (int) $_GET['modifyAnswers'] : 0;
$objQuestion = Question::read($questionId);
$picture = $objQuestion->getPicture();
$pictureName = $objQuestion->getPictureFilename();
$pictureWidth = $picture->getResourceNode()->getResourceFile()->getWidth();
$pictureHeight = $picture->getResourceNode()->getResourceFile()->getHeight();
$questionRepo = Container::getQuestionRepository();
/** @var CQuizQuestion $objQuestion */
$objQuestion = $questionRepo->find($questionId);
$resourceFile = $objQuestion->getResourceNode()->getResourceFile();
$pictureWidth = $resourceFile->getWidth();
$pictureHeight = $resourceFile->getHeight();
$imagePath = $questionRepo->getHotSpotImageUrl($objQuestion);
$data = [];
$data['type'] = 'admin';
@ -50,7 +55,7 @@ $data['lang'] = [
'ClosePolygon' => get_lang('ClosePolygon'),
'DelineationStatus1' => get_lang('DelineationStatus1'),
];
$data['image'] = $objQuestion->selectPicturePath();
$data['image'] = $imagePath;
$data['image_width'] = $pictureWidth;
$data['image_height'] = $pictureHeight;
$data['courseCode'] = $_course['path'];
@ -58,7 +63,7 @@ $data['hotspots'] = [];
$i = 0;
$nmbrTries = 0;
$answer_type = $objQuestion->type;
$answer_type = $objQuestion->getType();
$answers = Session::read('tmp_answers');
$nbrAnswers = count($answers['answer']);

@ -18,10 +18,8 @@ if (!is_object($objQuestion)) {
$questionName = $objQuestion->selectTitle();
$answerType = $objQuestion->selectType();
$pictureName = $objQuestion->getPictureFilename();
$debug = 0; // debug variable to get where we are
$okPicture = empty($pictureName) ? false : true;
$debug = 0;
// if we come from the warning box "this question is used in several exercises"
if ($modifyIn) {

@ -1,6 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
use Chamilo\CoreBundle\Entity\TrackEHotspot;
use Chamilo\CourseBundle\Entity\CQuizAnswer;
@ -23,11 +25,6 @@ $userId = api_get_user_id();
$courseId = api_get_course_int_id();
$objExercise = new Exercise($courseId);
$debug = false;
if ($debug) {
error_log("Call to hotspot_answers.as.php");
}
$trackExerciseInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
// Check if student has access to the hotspot answers
@ -51,7 +48,18 @@ if (!api_is_allowed_to_edit(null, true)) {
}
}
$objQuestion = Question::read($questionId, $objExercise->course);
$questionRepo = Container::getQuestionRepository();
/** @var CQuizQuestion $objQuestion */
$objQuestion = $questionRepo->find($questionId);
$answer_type = $objQuestion->getType(); //very important
$TBL_ANSWERS = Database::get_course_table(TABLE_QUIZ_ANSWER);
$resourceFile = $objQuestion->getResourceNode()->getResourceFile();
$pictureWidth = $resourceFile->getWidth();
$pictureHeight = $resourceFile->getHeight();
$imagePath = $questionRepo->getHotSpotImageUrl($objQuestion);
$objExercise->read($exerciseId);
if (empty($objQuestion) || empty($objExercise)) {
@ -59,9 +67,6 @@ if (empty($objQuestion) || empty($objExercise)) {
}
$em = Database::getManager();
$picture = $objQuestion->getPicture();
$pictureWidth = $picture->getResourceNode()->getResourceFile()->getWidth();
$pictureHeight = $picture->getResourceNode()->getResourceFile()->getHeight();
$data = [];
$data['type'] = 'solution';
@ -84,7 +89,7 @@ $data['lang'] = [
'ClosePolygon' => get_lang('ClosePolygon'),
'DelineationStatus1' => get_lang('DelineationStatus1'),
];
$data['image'] = $objQuestion->selectPicturePath();
$data['image'] = $imagePath;
$data['image_width'] = $pictureWidth;
$data['image_height'] = $pictureHeight;
$data['courseCode'] = $_course['path'];
@ -146,7 +151,7 @@ if (!$hideExpectedAnswer) {
->select('a')
->from('ChamiloCourseBundle:CQuizAnswer', 'a');
if ($objQuestion->selectType() == HOT_SPOT_DELINEATION) {
if ($objQuestion->getType() == HOT_SPOT_DELINEATION) {
$qb
->where($qb->expr()->eq('a.cId', $courseId))
->andWhere($qb->expr()->eq('a.questionId', $questionId))

@ -325,31 +325,6 @@ abstract class Question
return $this->picture;
}
/**
* @return string
*/
public function selectPicturePath()
{
if (!empty($this->picture)) {
$router = Container::getRouter();
// this "filter" param is used to resize the image to width 800px see config/packages/liip_imagine.yaml
$params = [
'file' => 'images/'.$this->getPictureFilename(),
'mode' => 'show',
'filter' => 'hotspot_question',
'course' => api_get_course_id(),
];
$url = $router->generate('resources_document_get_file', $params);
return $url;
/*return api_get_path(WEB_COURSE_PATH).
$this->course['directory'].'/document/images/'.$this->getPictureFilename().'?'.api_get_cidreq().'&type=show&filter=hotspot_question';*/
}
return '';
}
/**
* @return int|string
*/
@ -368,46 +343,6 @@ abstract class Question
return $this->picture;
}
/**
* @param int $courseId
* @param int $sessionId
*
* @return string
*/
public function getPictureFilename($courseId = 0, $sessionId = 0)
{
$courseId = empty($courseId) ? api_get_course_int_id() : (int) $courseId;
$sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
if (empty($courseId)) {
return '';
}
// for backward compatibility
// when in field picture we had the filename not the document id
if (preg_match("/quiz-.*/", $this->picture)) {
return $this->picture;
}
$pictureId = $this->getPictureId();
$courseInfo = $this->course;
$documentInfo = DocumentManager::get_document_data_by_id(
$pictureId,
$courseInfo['code'],
false,
$sessionId
);
$documentFilename = '';
if ($documentInfo) {
// document in document/images folder
$documentFilename = pathinfo(
$documentInfo['path'],
PATHINFO_BASENAME
);
}
return $documentFilename;
}
/**
* @param int $courseId
* @param int $sessionId
@ -682,6 +617,7 @@ abstract class Question
*/
public function getHotSpotFolderInCourse($courseInfo = [])
{
return null;
$courseInfo = empty($courseInfo) ? $this->course : $courseInfo;
if (empty($courseInfo) || empty($courseInfo['directory'])) {
@ -709,43 +645,6 @@ abstract class Question
return $folder;
}
/**
* adds a picture to the question.
*
* @param array $picture - picture to upload
*
* @return bool - true if uploaded, otherwise false
*/
public function uploadPicture($picture)
{
$folder = $this->getHotSpotFolderInCourse();
// if the question has got an ID
if ($folder && $this->id) {
$document = DocumentManager::upload_document(
['imageUpload' => $picture],
'/images',
'',
'',
false,
'overwrite',
false,
true,
'imageUpload',
true,
$folder->getId()
);
if ($document) {
$this->picture = $document->getIid();
return true;
}
}
return false;
}
/**
* return the name for image use in hotspot question
* to be unique, name is quiz-[utc unix timestamp].jpg.
@ -795,70 +694,8 @@ abstract class Question
*/
public function exportPicture($questionId, $courseInfo)
{
if (empty($questionId) || empty($courseInfo)) {
return false;
}
$course_id = $courseInfo['real_id'];
$destination_path = $this->getHotSpotFolderInCourse($courseInfo);
if (empty($destination_path)) {
return false;
}
$source_path = $this->getHotSpotFolderInCourse();
// if the question has got an ID and if the picture exists
if (!$this->id || empty($this->picture)) {
return false;
}
$sourcePictureName = $this->getPictureFilename($course_id);
$picture = $this->generatePictureName();
$result = false;
if (file_exists($source_path.'/'.$sourcePictureName)) {
// for backward compatibility
$result = copy(
$source_path.'/'.$sourcePictureName,
$destination_path.'/'.$picture
);
} else {
$imageInfo = DocumentManager::get_document_data_by_id(
$this->picture,
$courseInfo['code']
);
if (file_exists($imageInfo['absolute_path'])) {
$result = @copy(
$imageInfo['absolute_path'],
$destination_path.'/'.$picture
);
}
}
// If copy was correct then add to the database
if (!$result) {
return false;
}
$table = Database::get_course_table(TABLE_QUIZ_QUESTION);
$sql = "UPDATE $table SET
picture = '".Database::escape_string($picture)."'
WHERE c_id = $course_id AND id='".intval($questionId)."'";
Database::query($sql);
$documentId = DocumentManager::addDocument(
$courseInfo,
'/images/'.$picture,
'file',
filesize($destination_path.'/'.$picture),
$picture
);
if (!$documentId) {
return false;
}
return true;
// @todo Create a resource node duplication function.
throw new Exception('exportPicture not available yet');
}
/**
@ -983,6 +820,14 @@ abstract class Question
api_get_group_entity()
);
$request = Container::getRequest();
if ($request->files->has('imageUpload')) {
$file = $request->files->get('imageUpload');
$questionRepo->addFile($question, $file);
$em->flush();
}
// If hotspot, create first answer
if ($type == HOT_SPOT || $type == HOT_SPOT_ORDER) {
$quizAnswer = new CQuizAnswer();
@ -1005,7 +850,7 @@ abstract class Question
->setId($id)
->setIdAuto($id);
$em->merge($quizAnswer);
$em->persist($quizAnswer);
$em->flush();
}
}
@ -1031,7 +876,7 @@ abstract class Question
->setId($id)
->setIdAuto($id);
$em->merge($quizAnswer);
$em->persist($quizAnswer);
$em->flush();
}
}

@ -56,7 +56,7 @@ if (!$is_allowedToEdit) {
$htmlHeadXtra[] = '<link rel="stylesheet" href="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/css/hotspot.css">';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'hotspot/js/hotspot.js"></script>';
$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
//$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'annotation/js/annotation.js"></script>';
if (!empty($objExercise->getResultAccess())) {
/*$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/epiclock/renderers/minute/epiclock.minute.css');

@ -69,7 +69,6 @@ class ExerciseLib
}
$answerType = $objQuestionTmp->selectType();
$pictureName = $objQuestionTmp->getPictureFilename();
$s = '';
if ($answerType != HOT_SPOT &&
$answerType != HOT_SPOT_DELINEATION &&
@ -1527,23 +1526,11 @@ HOTSPOT;
global $exe_id;
$relPath = api_get_path(WEB_CODE_PATH);
if (api_is_platform_admin() || api_is_course_admin()) {
$docId = DocumentManager::get_document_id($course, '/images/'.$pictureName);
if ($docId) {
$images_folder_visibility = api_get_item_visibility(
$course,
'document',
$docId,
api_get_session_id()
);
if (!$images_folder_visibility) {
echo Display::return_message(get_lang('Change the visibility of the current image'), 'warning');
}
}
$questionRepo = Container::getQuestionRepository();
$questionEntity = $questionRepo->find($questionId);
if ($freeze) {
echo Display::img(
api_get_path(WEB_COURSE_PATH).$course['path'].'/document/images/'.$pictureName,
$questionRepo->getHotSpotImageUrl($questionEntity),
$objQuestionTmp->selectTitle(),
['width' => '600px']
);

@ -433,9 +433,8 @@ class ResourceFile
public function getWidth(): int
{
$data = $this->getDimensions();
if ($data) {
$data = explode(',', $data);
//$data = explode(',', $data);
return (int) $data[0];
}
@ -451,7 +450,7 @@ class ResourceFile
$data = $this->getDimensions();
if ($data) {
$data = explode(',', $data);
//$data = explode(',', $data);
return (int) $data[1];
}

@ -769,7 +769,31 @@ class ResourceRepository extends EntityRepository
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($id);
throw new FileNotFoundException($resource);
}
}
public function getResourceFileUrl(AbstractResource $resource, array $extraParams = []): string
{
try {
$resourceNode = $resource->getResourceNode();
if ($resourceNode->hasResourceFile()) {
$params = [
'tool' => $resourceNode->getResourceType()->getTool(),
'type' => $resourceNode->getResourceType(),
'id' => $resourceNode->getId(),
];
if (!empty($extraParams)) {
$params = array_merge($params, $extraParams);
}
return $this->router->generate('chamilo_core_resource_file', $params);
}
return '';
} catch (\Throwable $exception) {
throw new FileNotFoundException($resource);
}
}

@ -4,10 +4,19 @@
namespace Chamilo\CourseBundle\Repository;
use Chamilo\CoreBundle\Repository\ResourceRepository;
use Chamilo\CourseBundle\Entity\CQuizQuestion;
/**
* Class CQuizQuestionRepository.
*/
final class CQuizQuestionRepository extends ResourceRepository
{
public function getHotSpotImageUrl(CQuizQuestion $resource): string
{
$params = [
'mode' => 'show',
'filter' => 'hotspot_question',
];
return $this->getResourceFileUrl($resource, $params);
}
}

Loading…
Cancel
Save