From e5da9444be3bd213cfcdb465e1ff449710ff5a28 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Wed, 26 Nov 2025 21:36:26 -0500 Subject: [PATCH] Tracking: Add multi-session selector and bulk certificate export in session filter - refs BT#23109 --- main/mySpace/session_filter.php | 427 ++++++++++++------ .../src/export_pdf_all_in_one.php | 313 +++++++++---- 2 files changed, 494 insertions(+), 246 deletions(-) diff --git a/main/mySpace/session_filter.php b/main/mySpace/session_filter.php index f7c3b53095a..0feed39ab96 100644 --- a/main/mySpace/session_filter.php +++ b/main/mySpace/session_filter.php @@ -9,12 +9,21 @@ require_once __DIR__.'/../inc/global.inc.php'; $this_section = SECTION_TRACKING; +// CSRF token for delete action. +$token = Security::get_token(); + if (!api_is_allowed_to_create_course() && !api_is_drh()) { api_not_allowed(true); } $allowCustomCertificate = 'true' === api_get_plugin_setting('customcertificate', 'enable_plugin_customcertificate'); -$plugin = CustomCertificatePlugin::create(); + +/** @var CustomCertificatePlugin|null $plugin */ +// Create plugin instance only when the plugin is enabled. +$plugin = null; +if ($allowCustomCertificate) { + $plugin = CustomCertificatePlugin::create(); +} $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE); $tblSession = Database::get_main_table(TABLE_MAIN_SESSION); @@ -39,7 +48,7 @@ $whereCondictionMultiUrl = " AND session_rel_user.user_id = ".api_get_user_id(); } -// Select of sessions. +// Select sessions. $sql = "SELECT s.id, name FROM $tblSession s $innerJoinSessionRelUser $whereCondictionDRH @@ -51,7 +60,7 @@ if ($accessUrlId != -1) { $sql = "SELECT s.id, name FROM $tblSession s INNER JOIN $tblSessionRelAccessUrl as session_rel_url - ON (s.id = session_rel_url.session_id) + ON (s.id = session_rel_url.session_id) $innerJoinSessionRelUser WHERE access_url_id = $accessUrlId $whereCondictionMultiUrl @@ -66,7 +75,18 @@ $options[$enreg['id']] = $enreg['name']; } -$form->addElement('select', 'session_id', get_lang('SessionList'), $options, ['id' => 'session-id']); +// Multi-select to allow selecting several sessions at once. +$form->addElement( + 'select', + 'session_id', + get_lang('SessionList'), + $options, + [ + 'id' => 'session-id', + 'multiple' => 'multiple', + 'size' => 10, + ] +); $form->addDatePicker('date_begin', get_lang('DateStart'), ['id' => 'date-begin']); $form->addDatePicker('date_end', get_lang('DateEnd'), ['id' => 'date-end']); @@ -87,14 +107,48 @@ $form->addElement('hidden', 'formSent', 1); $form->addButtonSearch(get_lang('Search')); -$form->addButtonExport(get_lang('ExportAsCSV'), 'export'); +// We keep all export actions (CSV, PDF, ZIP) near the result table, so no form-level export button. +// Decide how to get values: normal search submit or CSV export icon. +$values = null; +$exportToCsv = false; + +// Case 1: user clicked "Search" button (normal form submit). if ($form->validate()) { $values = $form->getSubmitValues(); - $exportToCsv = isset($values['export']); - $sessionId = (int) $_REQUEST['session_id']; - $dateBegin = isset($_REQUEST['date_begin']) ? strtotime($_REQUEST['date_begin']) : null; - $dateEnd = isset($_REQUEST['date_end']) ? strtotime($_REQUEST['date_end'].' 23:59:59') : null; + $exportToCsv = false; +} elseif (isset($_GET['export'])) { + // Case 2: CSV export icon was clicked. + // Use raw GET parameters as the form values so filters are preserved. + $values = $_GET; + $exportToCsv = true; +} + +// Process filters and build result list only when we have values (search or export). +if (!empty($values)) { + // Normalize session ids from the form to an integer array. + $sessionIds = []; + if (!empty($values['session_id'])) { + if (is_array($values['session_id'])) { + foreach ($values['session_id'] as $sessionIdValue) { + $sessionIdValue = (int) $sessionIdValue; + if ($sessionIdValue > 0) { + $sessionIds[] = $sessionIdValue; + } + } + } else { + $sessionIdValue = (int) $values['session_id']; + if ($sessionIdValue > 0) { + $sessionIds[] = $sessionIdValue; + } + } + } + + $dateBeginRaw = isset($values['date_begin']) ? $values['date_begin'] : null; + $dateEndRaw = isset($values['date_end']) ? $values['date_end'] : null; + + $dateBegin = !empty($dateBeginRaw) ? strtotime($dateBeginRaw) : null; + $dateEnd = !empty($dateEndRaw) ? strtotime($dateEndRaw.' 23:59:59') : null; $filterDate = 0; if (!empty($dateBegin)) { @@ -104,91 +158,117 @@ $filterDate += DATE_END_FILTER; } + // Build extra-field filter list. $filterCheckList = []; $extraField = new ExtraField('user'); $extraFieldsAll = $extraField->get_all(['filter = ?' => 1], 'option_order'); foreach ($extraFieldsAll as $field) { - if (!empty($_REQUEST['extra_'.$field['variable']])) { + $fieldName = 'extra_'.$field['variable']; + if (!empty($values[$fieldName])) { $filterCheckList[$field['id']] = $field; } } - $result = Database::select( - 'c.id, c.code', - "$tbl_course c INNER JOIN $tblSessionRelCourse r ON c.id = r.c_id", - [ - 'where' => [ - "r.session_id = ? " => [$sessionId], - ], - ] - ); + // Build certificate list for all selected sessions. + $certificateList = []; + if (!empty($sessionIds)) { + foreach ($sessionIds as $sessionId) { + $courseResult = Database::select( + 'c.id, c.code', + "$tbl_course c INNER JOIN $tblSessionRelCourse r ON c.id = r.c_id", + [ + 'where' => [ + "r.session_id = ? " => [$sessionId], + ], + ] + ); - foreach ($result as $value) { - $courseId = $value['id']; - $courseCode = $value['code']; - - $cats = Category::load( - null, - null, - $courseCode, - null, - null, - $sessionId, - 'ORDER BY id' - ); + if (empty($courseResult)) { + continue; + } - if (empty($cats)) { - // first time - $cats = Category::load( - 0, - null, - $courseCode, - null, - null, - $sessionId, - 'ORDER BY id' - ); - } + $sessionInfo = api_get_session_info($sessionId); - $selectCat = (int) $cats[0]->get_id(); - $certificateListAux = []; - if (!empty($selectCat)) { - $certificateListAux = GradebookUtils::get_list_users_certificates($selectCat); - } + foreach ($courseResult as $course) { + $courseId = $course['id']; + $courseCode = $course['code']; - foreach ($certificateListAux as $value) { - $createdAt = strtotime(api_get_local_time($value['created_at'])); - $value['category_id'] = $selectCat; - $value['c_id'] = $courseId; - $value['course_code'] = $courseCode; - switch ($filterDate) { - case NO_DATE_FILTER: - $certificateList[] = $value; - break; - case DATE_BEGIN_FILTER: - if ($createdAt >= $dateBegin) { - $certificateList[] = $value; - } - break; - case DATE_END_FILTER: - if ($createdAt <= $dateEnd) { - $certificateList[] = $value; + $cats = Category::load( + null, + null, + $courseCode, + null, + null, + $sessionId, + 'ORDER BY id' + ); + + if (empty($cats)) { + // First time load with default category id = 0. + $cats = Category::load( + 0, + null, + $courseCode, + null, + null, + $sessionId, + 'ORDER BY id' + ); + } + + if (empty($cats)) { + continue; + } + + $selectCat = (int) $cats[0]->get_id(); + if ($selectCat <= 0) { + continue; + } + + $certificateListAux = GradebookUtils::get_list_users_certificates($selectCat); + if (empty($certificateListAux)) { + continue; + } + + foreach ($certificateListAux as $certificate) { + $createdAt = strtotime(api_get_local_time($certificate['created_at'])); + $certificate['category_id'] = $selectCat; + $certificate['c_id'] = $courseId; + $certificate['course_code'] = $courseCode; + $certificate['session_id'] = $sessionId; + $certificate['session_name'] = isset($sessionInfo['name']) ? $sessionInfo['name'] : ''; + + $includeCertificate = false; + switch ($filterDate) { + case NO_DATE_FILTER: + $includeCertificate = true; + break; + case DATE_BEGIN_FILTER: + $includeCertificate = $createdAt >= $dateBegin; + break; + case DATE_END_FILTER: + $includeCertificate = $createdAt <= $dateEnd; + break; + case ALL_DATE_FILTER: + $includeCertificate = $createdAt >= $dateBegin && $createdAt <= $dateEnd; + break; } - break; - case ALL_DATE_FILTER: - if ($createdAt >= $dateBegin && $createdAt <= $dateEnd) { - $certificateList[] = $value; + + if ($includeCertificate) { + $certificateList[] = $certificate; } - break; + } } } + } - // Filter extra field - foreach ($certificateList as $key => $value) { + // Filter by extra fields after building the global list. + if (!empty($filterCheckList) && !empty($certificateList)) { + foreach ($certificateList as $key => $certificate) { foreach ($filterCheckList as $fieldId => $field) { $extraFieldValue = new ExtraFieldValue('user'); $extraFieldValueData = $extraFieldValue->get_values_by_handler_and_field_id( - $value['user_id'], + $certificate['user_id'], $fieldId ); @@ -197,43 +277,78 @@ break; } + $fieldName = 'extra_'.$field['variable']; + switch ($field['field_type']) { case ExtraField::FIELD_TYPE_TEXT: case ExtraField::FIELD_TYPE_ALPHANUMERIC: - $pos = stripos($extraFieldValueData['value'], $_REQUEST['extra_'.$field['variable']]); - if ($pos === false) { - unset($certificateList[$key]); + $filterValue = isset($values[$fieldName]) ? $values[$fieldName] : ''; + if ($filterValue !== '') { + $pos = stripos($extraFieldValueData['value'], (string) $filterValue); + if ($pos === false) { + unset($certificateList[$key]); + } } break; case ExtraField::FIELD_TYPE_RADIO: - $valueRadio = $_REQUEST['extra_'.$field['variable']]['extra_'.$field['variable']]; - if ($extraFieldValueData['value'] != $valueRadio) { + $filterValue = ''; + if (isset($values[$fieldName][$fieldName])) { + $filterValue = $values[$fieldName][$fieldName]; + } + if ($extraFieldValueData['value'] != $filterValue) { unset($certificateList[$key]); } break; case ExtraField::FIELD_TYPE_SELECT: - if ($extraFieldValueData['value'] != $_REQUEST['extra_'.$field['variable']]) { + $filterValue = isset($values[$fieldName]) ? $values[$fieldName] : null; + if ($filterValue !== null && $extraFieldValueData['value'] != $filterValue) { unset($certificateList[$key]); } break; - } + } } } + + // Reindex after unsetting items. + $certificateList = array_values($certificateList); + } + + // Build URL parameters used by the export (PDF / ZIP / CSV) buttons. + $params = []; + + if (!empty($sessionIds)) { + foreach ($sessionIds as $sessionId) { + $params['session_id'][] = $sessionId; + } } - $params = [ - 'session_id' => (int) $_REQUEST['session_id'], - 'date_begin' => Security::remove_XSS($_REQUEST['date_begin']), - 'date_end' => Security::remove_XSS($_REQUEST['date_end']), - ]; + // Mark that form has been submitted, so filters can be reused. + $params['formSent'] = 1; + + $params['date_begin'] = Security::remove_XSS((string) $dateBeginRaw); + $params['date_end'] = Security::remove_XSS((string) $dateEndRaw); foreach ($filterCheckList as $field) { - $params['extra_'.$field['variable']] = Security::remove_XSS($_REQUEST['extra_'.$field['variable']]); + $fieldName = 'extra_'.$field['variable']; + if (!isset($values[$fieldName])) { + continue; + } + + if (is_array($values[$fieldName])) { + $cleanArray = []; + foreach ($values[$fieldName] as $key => $val) { + $cleanArray[$key] = is_string($val) ? Security::remove_XSS($val) : $val; + } + $params[$fieldName] = $cleanArray; + } else { + $params[$fieldName] = Security::remove_XSS((string) $values[$fieldName]); + } } $urlParam = http_build_query($params); - $dataToExport = []; + // Build CSV data when export is requested. if ($exportToCsv) { + $dataToExport = []; $headers = [ get_lang('Session'), get_lang('Course'), @@ -249,7 +364,6 @@ } $dataToExport[] = $headers; - $sessionInfo = api_get_session_info($sessionId); foreach ($certificateList as $index => $value) { $categoryId = $value['category_id']; $courseCode = $value['course_code']; @@ -274,7 +388,7 @@ $list = GradebookUtils::get_list_gradebook_certificates_by_user_id($value['user_id'], $categoryId); foreach ($list as $valueCertificate) { $item = []; - $item[] = $sessionInfo['name']; + $item[] = !empty($value['session_name']) ? $value['session_name'] : ''; $item[] = $courseInfo['title']; $item[] = $value['firstname']; $item[] = $value['lastname']; @@ -285,79 +399,97 @@ } } Export::arrayToCsv($dataToExport, 'export'); + + // Stop further HTML output when exporting CSV. + exit; } } -$htmlHeadXtra[] = ""; +} $interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('MySpace')]; Display::display_header(get_lang('CertificatesSessions')); echo Display::page_header(get_lang('CertificatesSessions')); -$actions = ''; -$actions .= Display::url( + +// Top toolbar: only back button, always visible. +$topActions = ''; +$topActions .= Display::url( Display::return_icon('back.png', get_lang('Back'), [], 32), api_get_path(WEB_CODE_PATH).'mySpace' ); +echo Display::toolbarAction('actions', [$topActions]); -if ($allowCustomCertificate) { - $url = api_get_path(WEB_PLUGIN_PATH).'customcertificate/src/export_pdf_all_in_one.php'; - $actions .= Display::url( - Display::return_icon('pdf.png', get_lang('ExportAllCertificatesToPDF'), [], ICON_SIZE_MEDIUM), - $url, - ['id' => 'export_pdf'] - ); - - $actions .= Display::url( - Display::return_icon('file_zip.png', get_lang('ExportAllCertificatesToZIP'), [], ICON_SIZE_MEDIUM), - $url, - ['id' => 'export_zip'] - ); -} - -echo Display::toolbarAction('actions', [$actions]); +// Show search form. echo $form->returnForm(); if (0 == count($certificateList)) { echo Display::return_message(get_lang('NoResultsAvailable'), 'warning'); } else { - echo ''; + // Actions related to the result list (CSV, PDF, ZIP) shown just above the table. + $resultActions = ''; + + // CSV export uses the same page with current filters. + $csvUrl = api_get_self().'?'.$urlParam.'&export=1'; + $resultActions .= Display::url( + Display::return_icon('excel.png', get_lang('ExportAsCSV'), [], ICON_SIZE_MEDIUM), + $csvUrl + ); + + if ($allowCustomCertificate) { + $pluginUrl = api_get_path(WEB_PLUGIN_PATH).'customcertificate/src/export_pdf_all_in_one.php'; + $pluginUrlWithParams = $pluginUrl.'?'.$urlParam; + + // Open PDF export in a new tab so the filter form remains visible. + $resultActions .= Display::url( + Display::return_icon('pdf.png', get_lang('ExportAllCertificatesToPDF'), [], ICON_SIZE_MEDIUM), + $pluginUrlWithParams.'&export_pdf=1', + [ + 'id' => 'export_pdf', + 'target' => '_blank', + ] + ); + + // Open ZIP export in a new tab so the filter form remains visible. + $resultActions .= Display::url( + Display::return_icon('file_zip.png', get_lang('ExportAllCertificatesToZIP'), [], ICON_SIZE_MEDIUM), + $pluginUrlWithParams.'&export_zip=1', + [ + 'id' => 'export_zip', + 'target' => '_blank', + ] + ); + } + + // Render result actions toolbar right above the table. + echo Display::toolbarAction('result-actions', [$resultActions]); + + // Render table with certificates. + echo '
'; echo ''; foreach ($certificateList as $index => $value) { $categoryId = $value['category_id']; @@ -370,9 +502,9 @@ echo ''; echo ''; echo ''; - echo '
'.$courseInfo['title'].'
- - '; + echo '
'; + echo ''; + echo ''; $list = GradebookUtils::get_list_gradebook_certificates_by_user_id($value['user_id'], $categoryId); foreach ($list as $valueCertificate) { @@ -392,10 +524,10 @@ ); echo $certificateUrl.PHP_EOL; - $url .= '&action=export'; + $urlExport = $url.'&action=export'; $pdf = Display::url( Display::return_icon('pdf.png', get_lang('Download')), - $url, + $urlExport, ['target' => '_blank'] ); echo $pdf.PHP_EOL; @@ -405,9 +537,10 @@ '&'.api_get_cidreq(). '&action=delete'. '&cat_id='.$categoryId. - '&certificate_id='.$valueCertificate['id'].'"> - '.Display::return_icon('delete.png', get_lang('Delete')).' - '.PHP_EOL; + '&certificate_id='.$valueCertificate['id'].'">'. + Display::return_icon('delete.png', get_lang('Delete')). + ''.PHP_EOL; + echo ''; } echo ''; diff --git a/plugin/customcertificate/src/export_pdf_all_in_one.php b/plugin/customcertificate/src/export_pdf_all_in_one.php index 759cd0f3886..a7bb38617e5 100644 --- a/plugin/customcertificate/src/export_pdf_all_in_one.php +++ b/plugin/customcertificate/src/export_pdf_all_in_one.php @@ -8,6 +8,8 @@ require_once __DIR__.'/../config.php'; api_block_anonymous_users(); + +/** @var CustomCertificatePlugin $plugin */ $plugin = CustomCertificatePlugin::create(); $enable = $plugin->get('enable_plugin_customcertificate') === 'true'; $tblProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); @@ -27,18 +29,63 @@ $currentLocalTime = api_get_local_time(); $accessUrlId = api_get_current_access_url_id(); -$sessionId = isset($_GET['session_id']) ? (int) $_GET['session_id'] : null; -$dateBegin = isset($_GET['date_begin']) ? strtotime($_GET['date_begin']) : null; -$dateEnd = isset($_GET['date_end']) ? strtotime($_GET['date_end'].' 23:59:59') : null; +/* + * Support single or multiple session_id parameters: + * - session_id=3 + * - session_id[0]=3&session_id[1]=5 + */ +$sessionIds = []; +if (isset($_GET['session_id'])) { + if (is_array($_GET['session_id'])) { + foreach ($_GET['session_id'] as $rawId) { + $id = (int) $rawId; + if ($id > 0) { + $sessionIds[] = $id; + } + } + } else { + $id = (int) $_GET['session_id']; + if ($id > 0) { + $sessionIds[] = $id; + } + } +} + +// Remove duplicates just in case. +$sessionIds = array_values(array_unique($sessionIds)); + +if (empty($sessionIds)) { + // No session selected: nothing to export. + Display::display_header($plugin->get_lang('PrintCertificate')); + echo Display::return_message(get_lang('NoResultsAvailable'), 'warning'); + Display::display_footer(); + exit; +} + +// Date filters (same logic as session_filter.php) +$dateBeginRaw = isset($_GET['date_begin']) ? $_GET['date_begin'] : null; +$dateEndRaw = isset($_GET['date_end']) ? $_GET['date_end'] : null; + +$dateBegin = !empty($dateBeginRaw) ? strtotime($dateBeginRaw) : null; +$dateEnd = !empty($dateEndRaw) ? strtotime($dateEndRaw.' 23:59:59') : null; + +$filterDate = 0; +if (!empty($dateBegin)) { + $filterDate += DATE_BEGIN_FILTER; +} +if (!empty($dateEnd)) { + $filterDate += DATE_END_FILTER; +} -if (api_is_multiple_url_enabled()) { - if ($accessUrlId != -1) { +// Multi-URL protection: every session must belong to the current URL. +if (api_is_multiple_url_enabled() && $accessUrlId != -1) { + foreach ($sessionIds as $sessionId) { $result = Database::select( '*', - "$tblSessionRelAccessUrl", + $tblSessionRelAccessUrl, [ 'where' => [ - "access_url_id = ? AND session_id = ?" => [$accessUrlId, $sessionId], + 'access_url_id = ? AND session_id = ?' => [$accessUrlId, $sessionId], ], ] ); @@ -52,51 +99,42 @@ $exportAllInOne = isset($_GET['export_pdf']) ? (int) $_GET['export_pdf'] : false; $exportZip = isset($_GET['export_zip']) ? (int) $_GET['export_zip'] : false; -$filterDate = 0; -if (!empty($dateBegin)) { - $filterDate += DATE_BEGIN_FILTER; -} -if (!empty($dateEnd)) { - $filterDate += DATE_END_FILTER; -} - +// Build extra fields filter (same approach as session_filter.php). $filterCheckList = []; $extraField = new ExtraField('user'); $extraFieldsAll = $extraField->get_all(['filter = ?' => 1], 'option_order'); foreach ($extraFieldsAll as $field) { - if (!empty($_GET['extra_'.$field['variable']])) { + $paramName = 'extra_'.$field['variable']; + if (!empty($_GET[$paramName])) { $filterCheckList[$field['id']] = $field; } } -$result = Database::select( - 'c.id, c.code', - "$tblCourse c INNER JOIN $tblSessionRelCourse r ON c.id = r.c_id", - [ - 'where' => [ - "r.session_id = ? " => [$sessionId], - ], - ] -); - -foreach ($result as $value) { - $courseId = $value['id']; - $courseCode = $value['code']; - - $cats = Category::load( - null, - null, - $courseCode, - null, - null, - $sessionId, - 'ORDER BY id' +// Collect all certificates from all selected sessions and their courses (same logic as session_filter.php). +$certificateList = []; + +foreach ($sessionIds as $sessionId) { + $courseResult = Database::select( + 'c.id, c.code', + "$tblCourse c INNER JOIN $tblSessionRelCourse r ON c.id = r.c_id", + [ + 'where' => [ + 'r.session_id = ? ' => [$sessionId], + ], + ] ); - if (empty($cats)) { - // first time + if (empty($courseResult)) { + continue; + } + + foreach ($courseResult as $value) { + $courseId = (int) $value['id']; + $courseCode = $value['code']; + + // Load gradebook categories for this course + session. $cats = Category::load( - 0, + null, null, $courseCode, null, @@ -104,40 +142,72 @@ $sessionId, 'ORDER BY id' ); - } - $selectCat = (int) $cats[0]->get_id(); - $certificateList = []; - $certificateListAux = GradebookUtils::get_list_users_certificates($selectCat); - - foreach ($certificateListAux as $value) { - $created_at = strtotime(api_get_local_time($value['created_at'])); - $value['category_id'] = $selectCat; - $value['c_id'] = $courseId; - $value['course_code'] = $courseCode; - switch ($filterDate) { - case NO_DATE_FILTER: - $certificateList[] = $value; - break; - case DATE_BEGIN_FILTER: - if ($created_at >= $dateBegin) { - $certificateList[] = $value; - } - break; - case DATE_END_FILTER: - if ($created_at <= $dateEnd) { - $certificateList[] = $value; - } - break; - case ALL_DATE_FILTER: - if ($created_at >= $dateBegin && $created_at <= $dateEnd) { - $certificateList[] = $value; - } - break; + if (empty($cats)) { + // First time, try with default category id = 0 (same as session_filter.php). + $cats = Category::load( + 0, + null, + $courseCode, + null, + null, + $sessionId, + 'ORDER BY id' + ); + } + + if (empty($cats)) { + // No gradebook category for this course/session. + continue; + } + + $selectCat = (int) $cats[0]->get_id(); + if ($selectCat <= 0) { + continue; + } + + // Get all certificates for this category. + $certificateListAux = GradebookUtils::get_list_users_certificates($selectCat); + if (empty($certificateListAux)) { + continue; + } + + foreach ($certificateListAux as $certRow) { + $createdAt = strtotime(api_get_local_time($certRow['created_at'])); + + // Base row with extra metadata. + $row = $certRow; + $row['category_id'] = $selectCat; + $row['c_id'] = $courseId; + $row['course_code'] = $courseCode; + $row['session_id'] = $sessionId; + + // Apply date filter (same logic as session_filter.php). + $include = false; + switch ($filterDate) { + case NO_DATE_FILTER: + $include = true; + break; + case DATE_BEGIN_FILTER: + $include = $createdAt >= $dateBegin; + break; + case DATE_END_FILTER: + $include = $createdAt <= $dateEnd; + break; + case ALL_DATE_FILTER: + $include = $createdAt >= $dateBegin && $createdAt <= $dateEnd; + break; + } + + if ($include) { + $certificateList[] = $row; + } } } +} - // Filter extra field +// Apply extra fields filtering to the global certificate list (same idea as session_filter.php). +if (!empty($filterCheckList) && !empty($certificateList)) { foreach ($certificateList as $key => $value) { foreach ($filterCheckList as $fieldId => $field) { $extraFieldValue = new ExtraFieldValue('user'); @@ -151,42 +221,73 @@ break; } + $paramName = 'extra_'.$field['variable']; + switch ($field['field_type']) { case ExtraField::FIELD_TYPE_TEXT: case ExtraField::FIELD_TYPE_ALPHANUMERIC: - $pos = stripos($extraFieldValueData['value'], $_GET['extra_'.$field['variable']]); - if ($pos === false) { - unset($certificateList[$key]); + $filterValue = isset($_GET[$paramName]) ? (string) $_GET[$paramName] : ''; + if ($filterValue !== '') { + $pos = stripos($extraFieldValueData['value'], $filterValue); + if ($pos === false) { + unset($certificateList[$key]); + } } break; case ExtraField::FIELD_TYPE_RADIO: - $valueRadio = $_GET['extra_'.$field['variable']]['extra_'.$field['variable']]; - if ($extraFieldValueData['value'] != $valueRadio) { + $filterValue = ''; + if (isset($_GET[$paramName][$paramName])) { + $filterValue = $_GET[$paramName][$paramName]; + } + if ($extraFieldValueData['value'] != $filterValue) { unset($certificateList[$key]); } break; case ExtraField::FIELD_TYPE_SELECT: - if ($extraFieldValueData['value'] != $_GET['extra_'.$field['variable']]) { + $filterValue = isset($_GET[$paramName]) ? $_GET[$paramName] : null; + if ($filterValue !== null && $extraFieldValueData['value'] != $filterValue) { unset($certificateList[$key]); } break; } + + // Stop checking other fields if this one already disqualified the record. + if (!isset($certificateList[$key])) { + break; + } } } } +// Re-index the array after unsetting elements. +$certificateList = array_values($certificateList); + +// If no certificates pass the filters, show a friendly message. +if (empty($certificateList)) { + Display::display_header($plugin->get_lang('PrintCertificate')); + echo Display::return_message(get_lang('NoResultsAvailable'), 'warning'); + Display::display_footer(); + exit; +} + +// Build user list + session mapping. $userList = []; -foreach ($certificateList as $index => $value) { +$sessionInfoPerSession = []; + +foreach ($certificateList as $value) { $infoUser = api_get_user_info($value['user_id']); $infoUser['category_id'] = $value['category_id']; $infoUser['c_id'] = $value['c_id']; $infoUser['course_code'] = $value['course_code']; + $infoUser['session_id'] = $value['session_id']; $userList[] = $infoUser; } -$sessionInfo = []; -if ($sessionId > 0) { - $sessionInfo = SessionManager::fetch($sessionId); +// Preload session info for all involved sessions (used in dates). +foreach ($sessionIds as $sessionId) { + if ($sessionId > 0) { + $sessionInfoPerSession[$sessionId] = SessionManager::fetch($sessionId); + } } $path = api_get_path(WEB_UPLOAD_PATH).'certificates/'; @@ -196,14 +297,17 @@ $courseId = $userInfo['c_id']; $courseCode = $userInfo['course_code']; $studentId = $userInfo['user_id']; + $sessionId = !empty($userInfo['session_id']) ? (int) $userInfo['session_id'] : 0; $courseInfo = api_get_course_info($courseCode); - $allowCustomCertificate = api_get_course_setting('customcertificate_course_enable', $courseInfo); - if (!$allowCustomCertificate) { + $allowCustomCertificateCourse = api_get_course_setting('customcertificate_course_enable', $courseInfo); + + if (!$allowCustomCertificateCourse) { + // Skip courses where the custom certificate plugin is not enabled at course level. continue; } - // Get info certificate + // Get info certificate for this course + session. $infoCertificate = CustomCertificatePlugin::getInfoCertificate($courseId, $sessionId, $accessUrlId); if (!is_array($infoCertificate)) { @@ -221,8 +325,8 @@ } } - $workSpace = intval(297 - $infoCertificate['margin_left'] - $infoCertificate['margin_right']); - $widthCell = intval($workSpace / 6); + $workSpace = (int) (297 - $infoCertificate['margin_left'] - $infoCertificate['margin_right']); + $widthCell = (int) ($workSpace / 6); $htmlText = ''; if (!$exportAllInOne) { @@ -237,7 +341,6 @@ href="'.api_get_path(WEB_CSS_PATH).'document.css">'; $htmlText .= ''; } - $studentId = $userInfo['user_id']; if (empty($infoCertificate['background'])) { $htmlText .= '
'; @@ -262,7 +365,7 @@ class="caraA" if (!empty($infoCertificate['logo_center'])) { $logoCenter = ' '; } @@ -282,11 +385,11 @@ class="caraA" " border="0">'; $htmlText .= '
'; - $htmlText .= ''; - $htmlText .= ''; + $htmlText .= ''; - $htmlText .= ''; + $htmlText .= ''; $htmlText .= ''; $htmlText .= '
'; @@ -307,15 +410,18 @@ class="caraA" $myContentHtml ); + // Use session-specific dates when available. + $currentSessionInfo = isset($sessionInfoPerSession[$sessionId]) ? $sessionInfoPerSession[$sessionId] : []; + $startDate = ''; $endDate = ''; switch ($infoCertificate['date_change']) { case 0: - if (!empty($sessionInfo['access_start_date'])) { - $startDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_start_date']))); + if (!empty($currentSessionInfo['access_start_date'])) { + $startDate = date("d/m/Y", strtotime(api_get_local_time($currentSessionInfo['access_start_date']))); } - if (!empty($sessionInfo['access_end_date'])) { - $endDate = date("d/m/Y", strtotime(api_get_local_time($sessionInfo['access_end_date']))); + if (!empty($currentSessionInfo['access_end_date'])) { + $endDate = date("d/m/Y", strtotime(api_get_local_time($currentSessionInfo['access_end_date']))); } break; case 1: @@ -364,8 +470,8 @@ class="caraA" } elseif ($infoCertificate['type_date_expediction'] == 4) { $dateExpediction .= $plugin->get_lang('to').$infoToReplaceInContentHtml[9]; //date_certificate_no_time } else { - if (!empty($sessionInfo)) { - $dateInfo = api_get_local_time($sessionInfo['access_end_date']); + if (!empty($currentSessionInfo)) { + $dateInfo = api_get_local_time($currentSessionInfo['access_end_date']); $dateExpediction .= $plugin->get_lang('to').api_format_date($dateInfo, DATE_FORMAT_LONG); } } @@ -571,12 +677,12 @@ class="caraA" $htmlText .= ''; $htmlText .= ''; $htmlText .= '
'; - $myContentHtml = strip_tags( + $myContentHtmlBack = strip_tags( $infoCertificate['contents'], '

'; $htmlText .= '
  • '; @@ -591,6 +697,15 @@ class="caraA" $htmlList[$fileName] = $htmlText; } +// If for some reason we ended up with no HTML (e.g. all courses had the plugin disabled), +// show a message instead of a blank page. +if (empty($htmlList)) { + Display::display_header($plugin->get_lang('PrintCertificate')); + echo Display::return_message(get_lang('NoResultsAvailable'), 'warning'); + Display::display_footer(); + exit; +} + $fileList = []; $archivePath = api_get_path(SYS_ARCHIVE_PATH).'certificates/'; if (!is_dir($archivePath)) {