|
4 | 4 | use Chamilo\CoreBundle\Framework\Container;
|
5 | 5 | use Michelf\MarkdownExtra;
|
6 | 6 | use Chamilo\CoreBundle\Entity\Plugin as PluginEntity;
|
| 7 | +use Chamilo\CoreBundle\Entity\ResourceNode; |
| 8 | +use Chamilo\CourseBundle\Entity\CDocument; |
7 | 9 |
|
8 | 10 | /**
|
9 | 11 | * Responses to AJAX calls.
|
|
49 | 51 | exit;
|
50 | 52 | }
|
51 | 53 |
|
| 54 | +/** |
| 55 | + * From here on, everything returns JSON. |
| 56 | + */ |
52 | 57 | header('Content-Type: application/json; charset=utf-8');
|
53 | 58 | api_block_anonymous_users();
|
54 | 59 |
|
| 60 | +if ($action === 'list_documents') { |
| 61 | + try { |
| 62 | + header('Content-Type: application/json; charset=utf-8'); |
| 63 | + |
| 64 | + $courseId = api_get_course_int_id(); |
| 65 | + $isAdmin = api_is_platform_admin(); |
| 66 | + |
| 67 | + // Require edit rights inside a course; otherwise only admins can list globally |
| 68 | + if ($courseId > 0) { |
| 69 | + if (!api_is_allowed_to_edit()) { |
| 70 | + http_response_code(403); |
| 71 | + echo json_encode(['error' => 'Forbidden']); |
| 72 | + exit; |
| 73 | + } |
| 74 | + } else { |
| 75 | + if (!$isAdmin) { |
| 76 | + http_response_code(403); |
| 77 | + echo json_encode(['error' => 'Forbidden (admin required for global listing)']); |
| 78 | + exit; |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + $em = Database::getManager(); |
| 83 | + $repo = $em->getRepository(ResourceNode::class); |
| 84 | + |
| 85 | + $qb = $em->createQueryBuilder() |
| 86 | + ->select('DISTINCT d') |
| 87 | + ->from(CDocument::class, 'd') |
| 88 | + ->innerJoin('d.resourceNode', 'rn') |
| 89 | + ->innerJoin('rn.resourceFiles', 'rf') |
| 90 | + ->innerJoin('rn.resourceLinks', 'rl') |
| 91 | + ->where('d.filetype = :type') |
| 92 | + ->setParameter('type', 'file'); |
| 93 | + |
| 94 | + if ($courseId > 0) { |
| 95 | + $qb->andWhere('IDENTITY(rl.course) = :cId') |
| 96 | + ->setParameter('cId', (int)$courseId); |
| 97 | + } |
| 98 | + |
| 99 | + $limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 500; |
| 100 | + $limit = max(1, min($limit, 2000)); |
| 101 | + $qb->setMaxResults($limit) |
| 102 | + ->orderBy('d.iid', 'DESC'); |
| 103 | + |
| 104 | + $docs = $qb->getQuery()->getResult(); |
| 105 | + $out = []; |
| 106 | + |
| 107 | + $sysBase = rtrim(str_replace('/public/', '', api_get_path(SYS_PATH)), '/'); |
| 108 | + foreach ($docs as $doc) { |
| 109 | + $files = $doc->getResourceNode()->getResourceFiles(); |
| 110 | + if ($files->isEmpty()) { |
| 111 | + continue; |
| 112 | + } |
| 113 | + |
| 114 | + $file = $files->first(); |
| 115 | + $orig = $file->getOriginalName(); |
| 116 | + $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION)); |
| 117 | + if (!in_array($ext, ['pdf','ppt','pptx','odp'], true)) { |
| 118 | + continue; |
| 119 | + } |
| 120 | + |
| 121 | + $relPath = $repo->getFilename($file); // e.g. "/a1/b2/file.pdf" |
| 122 | + $diskPath = $sysBase . '/var/upload/resource' . $relPath; |
| 123 | + |
| 124 | + // Only list entries that truly exist on disk |
| 125 | + if (!is_file($diskPath) || !is_readable($diskPath)) { |
| 126 | + continue; |
| 127 | + } |
| 128 | + |
| 129 | + $out[] = [ |
| 130 | + 'id' => $doc->getIid(), |
| 131 | + 'url' => $diskPath, |
| 132 | + 'filename' => $orig, |
| 133 | + 'size' => @filesize($diskPath) ?: null, |
| 134 | + ]; |
| 135 | + } |
| 136 | + |
| 137 | + echo json_encode($out); |
| 138 | + } catch (\Throwable $e) { |
| 139 | + error_log('[plugin.ajax list_documents] '.$e->getMessage()); |
| 140 | + http_response_code(500); |
| 141 | + echo json_encode(['error' => 'Internal error']); |
| 142 | + } |
| 143 | + exit; |
| 144 | +} |
| 145 | + |
55 | 146 | try {
|
56 | 147 | if (!api_is_platform_admin()) {
|
57 | 148 | http_response_code(403);
|
|
165 | 256 | }
|
166 | 257 | }
|
167 | 258 |
|
| 259 | + // If it is a course plugin, propagate enable/disable to all courses |
168 | 260 | if ($instance && !empty($instance->isCoursePlugin)) {
|
169 | 261 | if ($action === 'enable') {
|
170 | 262 | $instance->install_course_fields_in_all_courses(true);
|
|
0 commit comments