diff --git a/public/main/admin/statistics/index.php b/public/main/admin/statistics/index.php
index 4094b37bab6..a607c384858 100644
--- a/public/main/admin/statistics/index.php
+++ b/public/main/admin/statistics/index.php
@@ -334,6 +334,7 @@
get_lang('Courses') => [
'report=courses' => get_lang('Courses'),
'report=tools' => get_lang('Tools access'),
+ 'report=tool_usage' => get_lang('Tool-based resource count'),
'report=courselastvisit' => get_lang('Latest access'),
'report=coursebylanguage' => get_lang('Number of courses by language'),
],
@@ -709,6 +710,66 @@
$content .= '';
$content .= Statistics::printToolStats();
break;
+ case 'tool_usage':
+ $courseTools = Statistics::getAvailableTools();
+
+ if (empty($courseTools)) {
+ $content .= '
'.get_lang('No tools available for this report').'
';
+ break;
+ }
+
+ $form = new FormValidator('tool_usage', 'get');
+ $form->addHeader(get_lang('Tool-based resource count'));
+ $form->addSelect(
+ 'tool_ids',
+ get_lang('Select Tools'),
+ $courseTools,
+ ['multiple' => true, 'required' => true]
+ );
+ $form->addButtonSearch(get_lang('Generate report'));
+ $form->addHidden('report', 'tool_usage');
+
+ $content .= $form->returnForm();
+
+ if ($form->validate()) {
+ $values = $form->getSubmitValues();
+ $toolIds = $values['tool_ids'];
+ $reportData = Statistics::getToolUsageReportByTools($toolIds);
+
+ $table = new HTML_Table(['class' => 'table table-hover table-striped data_table stats_table']);
+ $headers = [
+ get_lang('Tool'),
+ get_lang('Session'),
+ get_lang('Course'),
+ get_lang('Resource count'),
+ get_lang('Last updated'),
+ ];
+ $row = 0;
+
+ foreach ($headers as $index => $header) {
+ $table->setHeaderContents($row, $index, $header);
+ }
+ $row++;
+
+ foreach ($reportData as $data) {
+ $linkHtml = $data['link'] !== '-'
+ ? sprintf(
+ '%s',
+ $data['link'],
+ htmlspecialchars($data['tool_name'])
+ )
+ : htmlspecialchars($data['tool_name']);
+
+ $table->setCellContents($row, 0, $linkHtml);
+ $table->setCellContents($row, 1, htmlspecialchars($data['session_name']));
+ $table->setCellContents($row, 2, htmlspecialchars($data['course_name']));
+ $table->setCellContents($row, 3, (int) $data['resource_count']);
+ $table->setCellContents($row, 4, htmlspecialchars($data['last_updated']));
+ $row++;
+ }
+ $content .= $table->toHtml();
+ }
+ break;
case 'coursebylanguage':
$content .= '';
$result = Statistics::printCourseByLanguageStats();
diff --git a/public/main/inc/lib/statistics.lib.php b/public/main/inc/lib/statistics.lib.php
index 02244026171..f9b8beab900 100644
--- a/public/main/inc/lib/statistics.lib.php
+++ b/public/main/inc/lib/statistics.lib.php
@@ -3,6 +3,7 @@
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
use Chamilo\CoreBundle\Entity\MessageRelUser;
+use Chamilo\CoreBundle\Entity\ResourceLink;
use Chamilo\CoreBundle\Entity\UserRelUser;
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
use Chamilo\CoreBundle\Framework\Container;
@@ -1784,6 +1785,28 @@ public static function groupByMonth(array $registrations): array
return $groupedData;
}
+ /**
+ * Retrieves the available tools using the repository.
+ */
+ public static function getAvailableTools(): array
+ {
+ $em = Database::getManager();
+ $repo = $em->getRepository(ResourceLink::class);
+
+ return $repo->getAvailableTools();
+ }
+
+ /**
+ * Generates a report of tool usage based on the provided tool IDs.
+ */
+ public static function getToolUsageReportByTools(array $toolIds): array
+ {
+ $em = Database::getManager();
+ $repo = $em->getRepository(ResourceLink::class);
+
+ return $repo->getToolUsageReportByTools($toolIds);
+ }
+
/**
* Return de number of certificates generated.
* This function is resource intensive.
diff --git a/src/CoreBundle/Repository/ResourceLinkRepository.php b/src/CoreBundle/Repository/ResourceLinkRepository.php
index aa69577d77b..050344e5311 100644
--- a/src/CoreBundle/Repository/ResourceLinkRepository.php
+++ b/src/CoreBundle/Repository/ResourceLinkRepository.php
@@ -21,6 +21,23 @@
*/
class ResourceLinkRepository extends SortableRepository
{
+ private array $toolList = [
+ 'course_description' => '/main/course_description/index.php',
+ 'document' => '/resources/document/%resource_node_id%/',
+ 'learnpath' => '/main/lp/lp_controller.php',
+ 'link' => '/resources/links/%resource_node_id%/',
+ 'quiz' => '/main/exercise/exercise.php',
+ 'announcement' => '/main/announcements/announcements.php',
+ 'glossary' => '/resources/glossary/%resource_node_id%/',
+ 'attendance' => '/main/attendance/index.php',
+ 'course_progress' => '/main/course_progress/index.php',
+ 'agenda' => '/resources/ccalendarevent',
+ 'forum' => '/main/forum/index.php',
+ 'student_publication' => '/resources/assignment/%resource_node_id%',
+ 'survey' => '/main/survey/survey_list.php',
+ 'notebook' => '/main/notebook/index.php',
+ ];
+
public function __construct(EntityManagerInterface $em)
{
parent::__construct($em, $em->getClassMetadata(ResourceLink::class));
@@ -53,4 +70,100 @@ public function removeByResourceInContext(
$this->remove($link);
}
}
+
+ /**
+ * Retrieves the list of available tools filtered by a predefined tool list.
+ *
+ * @return array The list of tools with their IDs and titles.
+ */
+ public function getAvailableTools(): array
+ {
+ $queryBuilder = $this->_em->createQueryBuilder();
+ $queryBuilder
+ ->select('DISTINCT t.id, t.title')
+ ->from('ChamiloCoreBundle:ResourceLink', 'rl')
+ ->innerJoin('ChamiloCoreBundle:ResourceType', 'rt', 'WITH', 'rt.id = rl.resourceTypeGroup')
+ ->innerJoin('ChamiloCoreBundle:Tool', 't', 'WITH', 't.id = rt.tool')
+ ->where('rl.course IS NOT NULL')
+ ->andWhere('t.title IN (:toolList)')
+ ->setParameter('toolList', array_keys($this->toolList));
+
+ $result = $queryBuilder->getQuery()->getArrayResult();
+
+ $tools = [];
+ foreach ($result as $row) {
+ $tools[$row['id']] = ucfirst(str_replace('_', ' ', $row['title']));
+ }
+
+ return $tools;
+ }
+
+ /**
+ * Retrieves a usage report of tools with dynamic links.
+ *
+ * @return array The tool usage data including counts, last update timestamps, and dynamic links.
+ */
+ public function getToolUsageReportByTools(array $toolIds): array
+ {
+ $queryBuilder = $this->_em->createQueryBuilder();
+
+ $queryBuilder
+ ->select(
+ 'COUNT(rl.id) AS resource_count',
+ 'IDENTITY(rl.course) AS course_id',
+ 'IDENTITY(rl.session) AS session_id',
+ 'IDENTITY(c.resourceNode) AS course_resource_node_id',
+ 't.title AS tool_name',
+ 'c.title AS course_name',
+ 's.title AS session_name',
+ 'MAX(rl.updatedAt) AS last_updated'
+ )
+ ->from('ChamiloCoreBundle:ResourceLink', 'rl')
+ ->innerJoin('ChamiloCoreBundle:ResourceType', 'rt', 'WITH', 'rt.id = rl.resourceTypeGroup')
+ ->innerJoin('ChamiloCoreBundle:Tool', 't', 'WITH', 't.id = rt.tool')
+ ->innerJoin('ChamiloCoreBundle:Course', 'c', 'WITH', 'c.id = rl.course')
+ ->leftJoin('ChamiloCoreBundle:Session', 's', 'WITH', 's.id = rl.session')
+ ->where($queryBuilder->expr()->in('t.id', ':toolIds'))
+ ->groupBy('rl.course, rl.session, t.title')
+ ->orderBy('t.title', 'ASC')
+ ->addOrderBy('c.title', 'ASC')
+ ->addOrderBy('s.title', 'ASC')
+ ->setParameter('toolIds', $toolIds);
+
+ $result = $queryBuilder->getQuery()->getArrayResult();
+
+ return array_map(function ($row) {
+ $toolName = $row['tool_name'];
+ $baseLink = $this->toolList[$toolName] ?? null;
+ $link = '-';
+ if ($baseLink) {
+ $link = str_replace(
+ ['%resource_node_id%'],
+ [$row['course_resource_node_id']],
+ $baseLink
+ );
+
+ $queryParams = [
+ 'cid' => $row['course_id'],
+ ];
+
+ if (!empty($row['session_id'])) {
+ $queryParams['sid'] = $row['session_id'];
+ }
+
+ $link .= '?' . http_build_query($queryParams);
+ }
+
+ return [
+ 'tool_name' => $toolName,
+ 'session_id' => $row['session_id'],
+ 'session_name' => $row['session_name'] ?: '-',
+ 'course_id' => $row['course_id'],
+ 'course_name' => $row['course_name'],
+ 'resource_count' => (int) $row['resource_count'],
+ 'last_updated' => $row['last_updated'] ?: '-',
+ 'link' => $link,
+ ];
+ }, $result);
+ }
}