* @version $Id$ * * @ingroup ServicesCME * @extends ilObject * */ class ilObjCME extends ilObject { /** * Get the EIV-Fobi server URL from the cmes settings * * @return string */ function getEivFobiUrl() { global $ilSetting; $cmes_set = new ilSetting("cmes"); $url = $cmes_set->get("cme_eiv_fobi_transfer_url"); error_log("getEivFobiUrl: url: $url", 0); return $url; } /** * Get the EIV-Fobi upload response email address from the cmes settings * * @return string */ function getEivFobiResponseEmail() { global $ilSetting; $cmes_set = new ilSetting("cmes"); $email = $cmes_set->get("cme_eiv_fobi_ack_mail"); error_log("getEivFobiResponseEmail: email: $email", 0); return $email; } /** * Initialize the XML writer and add the default elements * for EIV-Fobi XML * * @return XMLWriter */ function _startEivFobiXMLWriter() { $writer = new XMLWriter(); $writer->openMemory(); $writer->startDocument('1.0', 'iso-8859-15'); $writer->setIndentString("\t"); $writer->setIndent(true); $writer->startElement('Veranstaltung'); $writer->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); $writer->writeAttribute('xsi:noNamespaceSchemaLocation', 'http://www.eiv-fobi.de/downloads/eiv-punktemeldung.xsd'); return $writer; } /** * Close the the XML writer and the closing element * * @param XMLWriter $writer */ function _endEivFobiXMLWriter($writer) { $writer->endElement(); $writer->endDocument(); } /** * Create the XML data as of http://www.eiv-fobi.de/downloads/eiv-punktemeldung.xsd for the CME Points transfer * * @param int $test_id the test the CME XML is created for * @param int $user_id the user the CME XML is created for * * @return string the xml content */ function createEivFobiXML($test_id, $user_id) { global $ilDB; $writer = ilObjCME::_startEivFobiXMLWriter(); $cme_data = ilObjCME::getCMEDataByTestIdFromDb($test_id); // VNR und Meldungsart $writer->writeElement('VNR', $cme_data["cme_vnr"]); $writer->writeElement('Meldungsart', 'N'); // Teilnehmerzahl $result = $ilDB->queryF("SELECT * from cme_points WHERE test_fi = %s and user_fi = %s", array( 'integer', 'integer' ), array( $test_id, $user_id )); if ($result->numRows() == 0) { return NULL; } $writer->writeElement('Teilnehmeranzahl', $result->numRows()); $writer->writeElement('EMAIL', ilObjCME::getEivFobiResponseEmail()); while ($row = $ilDB->fetchObject($result)) { $writer->startElement('Teilnehmer'); // EFN $writer->writeElement('EFN', $row->efn); // Wenn der Benutzer keine CME-Punkte erreicht hat, dann // erhält er nicht mal die Basispunkte // if ($row->cme_points > 0) { $writer->writeElement('Punkte_Basis_JN', 'J'); } else { $writer->writeElement('Punkte_Basis_JN', 'N'); } // Wenn mann eine CME-Punktezahl kleiner der maximalen CME-Punktezahl // erreicht, dann überträgt man im Feld Lernkontrolle = N. // if ($row->cme_points < $row->cme_max_points) { $writer->writeElement('Punkte_Zusatz_Lernkontrolle_JN', 'N'); } // Wenn die volle Punktezahl erreicht ist, dann gibt es die volle // CME-Punktezahl, also Lernkontrolle = J else { $writer->writeElement('Punkte_Zusatz_Lernkontrolle_JN', 'J'); } $writer->writeElement('Punkte_Interaktivitaet_JN', 'N'); $writer->writeElement('Punkte_Referent', 0); $writer->endElement(); } ilObjCME::_endEivFobiXMLWriter($writer); $xml = $writer->outputMemory(TRUE); return array( "data" => $xml, "vnr" => $cme_data["cme_vnr"], "vnr_password" => $cme_data["cme_vnr_password"] ); } /** * Gets cme related data of the test * * @param integer The test id * * @return array containing "cme_test", "cme_vnr", "cme_vnr_password" of the given test * @access public */ function getCMEDataByTestIdFromDb($test_id) { global $ilDB; $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s", array( 'integer' ), array( $test_id )); if ($result->numRows()) { $row = $ilDB->fetchAssoc($result); return $row; } return FALSE; } /** * Update CME Test Result Points * * @param int $user_id the user passing the test * @param int $test_id the test the user passed * @param int $active_id the active testrun * @param int $ref_id the test reference */ function updateTestCMEResults($user_id, $test_id, $active_id, $ref_id) { global $ilDB; error_log("updateTestCMEResults: user_id: $user_id, test_id: $test_id, active_id: $active_id, ref_id: $ref_id", 0); require_once './Modules/Test/classes/class.ilObjTest.php'; $cme_data = ilObjCME::getCMEDataByTestIdFromDb($test_id); error_log("updateTestCMEResults: cme_test: " . $cme_data["cme_test"], 0); if ($cme_data["cme_test"] != 1) { error_log("updateTestCMEResults: test " . $cme_data["cme_test"] . " IS NOT A CME TEST", 0); return; } $vnr = $cme_data["cme_vnr"]; if (empty($vnr)) { error_log("updateTestCMEResults: vnr IS_EMPTY", 0); return; } $vnr_password = $cme_data["cme_vnr_password"]; if (empty($vnr_password)) { error_log("updateTestCMEResults: vnr_password IS_EMPTY", 0); return; } require_once './Services/User/classes/class.ilObjUser.php'; $userObj = new ilObjUser($user_id); $matriculation = $userObj->getMatriculation(); error_log("updateTestCMEResults: matriculation: " . $matriculation, 0); if (empty($matriculation)) { error_log("updateTestCMEResults: matriculation IS_EMPTY", 0); return; } error_log("updateTestCMEResults: vgetResultsForActiveId", 0); $testResults = ilObjTest::getResultsForActiveId($active_id); error_log("updateTestCMEResults: testResults[\"mark_short\"]: " . $testResults["mark_short"], 0); $laek = ilUtil::prepareFormOutput(ilCMEUtil::laekValueToText($cme_data["cme_laek"])); $cme_max_points = ''; $result = $ilDB->queryF(" SELECT short_name, minimum_level " . " FROM tst_mark tm1 " . " WHERE tm1.test_fi = %s and " . " tm1.minimum_level = ( SELECT max(tm2.minimum_level) FROM tst_mark tm2 WHERE tm2.test_fi = tm1.test_fi )", array( 'integer' ), array( $test_id )); if ($result->numRows() > 0) { $data = $ilDB->fetchAssoc($result); $cme_max_points = $data["short_name"]; } $cme = array( $matriculation, // efn $vnr, // vnr $vnr_password, // vnr_password $testResults["max_points"], // max_points $testResults["reached_points"], // reached_points $cme_max_points, // cme_max_points $testResults["mark_short"], // cme_points $testResults["pass"], // anzahl durchlaufe $laek, // landesaerztekammer $active_id, // active_fi $ref_id, // ref_id $user_id, // user_if $test_id // test_fi ); error_log("updateTestCMEResults: ilDB->queryF", 0); // update test pass results $result = $ilDB->queryF("SELECT * FROM cme_points WHERE user_fi = %s AND test_fi = %s", array( 'integer', 'integer' ), array( $user_id, $test_id )); error_log("updateTestCMEResults: result->numRows(): " . $result->numRows(), 0); if ($result->numRows() > 0) { $affectedRows = $ilDB->manipulateF("UPDATE cme_points " . " SET efn = %s, vnr = %s, vnr_password = %s, max_points = %s, reached_points = %s, " . " cme_max_points = %s, cme_points = %s, pass = %s, laek = %s, active_fi = %s, ref_id = %s, " . " test_finished = NOW(), transfer_success = null WHERE user_fi = %s and test_fi = %s", array( 'text', 'text', 'text', 'float', 'float', 'integer', 'integer', 'integer', 'text', 'integer', 'integer', 'integer', 'integer' ), $cme); } else { $affectedRows = $ilDB->manipulateF("INSERT INTO cme_points " . "(efn, vnr, vnr_password, max_points, reached_points, cme_max_points, " . " cme_points, pass, laek, active_fi, ref_id, user_fi, test_fi, test_finished) " . " VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s, NOW())", array( 'text', 'text', 'text', 'float', 'float', 'integer', 'integer', 'integer', 'text', 'integer', 'integer', 'integer', 'integer' ), $cme); } // Transfer nach EIV-FOBI ilObjCME::transferEivFobi($test_id, $user_id); } /** * Count the yet successful done transfers * * @return integer the number of missing transfers * @access public */ function countAllUntransferedEivFobi() { global $ilDB; error_log("countAllUntransferedEivFobi", 0); $result = $ilDB->queryF(" SELECT * FROM cme_points WHERE transfer_success is null and efn != '000' and test_finished > (now() - interval 30 DAY) order by test_finished asc", array(), array()); if ($result->numRows() == 0) { error_log("countAllUntransferedEivFobi: no transfers found, nothing to do.", 0); return 0; } $count = $result->numRows(); error_log("countAllUntransferedEivFobi: found $count transfers not yet done (cme_points.transfer_success is null and efn != '000').", 0); return $count; } /** * Initiate the transfer of the not yet successful done * * @return integer the number of transfers retried * @access public */ function transferAllUntransferedEivFobi() { global $ilDB; error_log("transferAllUntransferedEivFobi", 0); $result = $ilDB->queryF( "SELECT * FROM cme_points WHERE transfer_success is null and efn != '000' and test_finished > (now() - interval 30 DAY) order by test_finished asc", array(), array()); if ($result->numRows() == 0) { error_log("transferAllUntransferedEivFobi: no transfers found, nothing to do.", 0); return 0; } $count = $result->numRows(); error_log("transferAllUntransferedEivFobi: found $count transfers not yet done (cme_points.transfer_success != 1).", 0); $transfers = array(); // push all found transfers into an array // while ($row = $ilDB->fetchObject($result)) { error_log("transferAllUntransferedEivFobi: adding test_fi: " . $row->test_fi . ", user_fi: " . $row->user_fi . ", vnr: " . $row->vnr . ", efn: " . $row->efn, 0); array_push($transfers, (object) array( 'user_fi' => $row->user_fi, 'test_fi' => $row->test_fi )); } $success = 0; foreach ($transfers as $t) { if (ilObjCME::transferEivFobi($t->test_fi, $t->user_fi)) { $success += 1; } } error_log("transferAllUntransferedEivFobi: successfully retried $success of $count missing transfers.", 0); return $count; } /** * Transfer the CME Points to the EIV-Fobi server * * @param int $test_id the id of the CME test * @param int $user_id the user who passed the CME test * * @return boolean true if the transfer succeeded */ function transferEivFobi($test_id, $user_id) { // XML-Datei erzeugen und einen Transferversuch starten $transfer_data = ilObjCME::createEivFobiXML($test_id, $user_id); $uploadpath = ilCMEUtil::createEivFobiTransferPath(); $uploadfilename = ilCMEUtil::createEivFobiTransferFilename($test_id, $user_id); $uploadfilepath = $uploadpath . $uploadfilename; // XML-Daten in Datei schreiben $fh = fopen($uploadfilepath, 'w'); fwrite($fh, $transfer_data["data"]); fclose($fh); $vnr = $transfer_data["vnr"]; $post_data = array( "vnr" => $vnr, "passwd" => $transfer_data["vnr_password"], // "myFile" => '@' . $uploadfilepath . ';type=text/xml' "myFile" => new CurlFile($uploadfilepath, 'text/xml', $uploadfilename) ); $ch = curl_init(ilObjCME::getEivFobiUrl()); curl_setopt($ch, CURLOPT_VERBOSE, TRUE); curl_setopt($ch, CURLOPT_HTTPHEADER, array( "Content-Type:multipart/form-data" )); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // der Testserver hat das gleiche Zertifikat wie der Produtkivserver curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)"); curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); $post_result = curl_exec($ch); $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); error_log("CURL: content_type: $content_type", 0); /* Get the MIME type and character set */ preg_match('@([\w/+]+)(;\s*+charset=(\S+))?@i', $content_type, $matches); if (isset($matches[1])) $mime = $matches[1]; if (isset($matches[3])) $charset = $matches[3]; error_log("CURL: charset: $charset, mimetype: $mime", 0); if (! empty($charset) and strtolower($charset) != 'utf-8') { if (strtolower(mb_detect_encoding($post_result, 'utf-8, ' . strtolower($charset))) != 'utf-8') { $post_result = mb_convert_encoding($post_result, 'utf-8', strtolower($charset)); } } $post_result = html_entity_decode($post_result); $success = FALSE; if (! empty($post_result) and strstr($post_result, $uploadfilename) and stristr($post_result, "wurde erfolgreich") and stristr($post_result, "veranstaltung") and strstr($post_result, $vnr) and stristr($post_result, "hochgeladen")) { $fh = fopen($uploadfilepath . ".transfer-success.log", 'w'); fwrite($fh, $post_result); fclose($fh); // erfolgreiche Übertragung ilObjCME::reportTransferSuccess($user_id, $test_id, $post_result, $uploadfilename, $vnr); $success = TRUE; } else { $fh = fopen($uploadfilepath . ".transfer-error.log", 'w'); fwrite($fh, $post_result); fclose($fh); // Misserfolg bei der Übertragung ilObjCME::reportTransferError($user_id, $test_id, $post_result, $uploadfilename, $vnr); } curl_close($ch); return $success; } /** * Report Transfer Success * * @param int $user_id the user whos CME points were transfered successfully * @param int $test_id the CME test id which the user passed * @param string $post_result the transfer post answer of the EIV-Fobi server * @param string $uploadfilename the filename of the XML file * @param string $vnr the BÄK Veranstaltungsnummer */ function reportTransferSuccess($user_id, $test_id, $post_result, $uploadfilename, $vnr) { global $ilDB; @include_once ("./Services/Visionet/classes/class.ilVisionetUtil.php"); $parser = ilVisionetUtil::createHtmlDomParser(); $parser->load($post_result); $message = 'Die Datei "' . $uploadfilename . '" wurde erfolgreich für Veranstaltung ' . $vnr . ' hochgeladen.'; error_log("reportTransferSuccess: message: " . $message, 0); // hier die Punkteübertragung als erfolgreich markieren // $affectedRows = $ilDB->manipulateF("UPDATE cme_points SET transfer_success = NOW() WHERE user_fi = %s and test_fi = %s", array( 'integer', 'integer' ), array( $user_id, $test_id )); // und einen Eintrag in die Transfertabelle schreiben // // `transfer_time` timestamp NOT NULL, // `transfer_message` varchar(512) NOT NULL, // `transfer_success` int(11) NOT NULL, $affectedRows = $ilDB->manipulateF(" INSERT INTO cme_points_transfer " . " ( user_fi, test_fi, transfer_time, transfer_message, transfer_success ) " . " VALUES ( %s, %s, NOW(), %s, 1 ) ", array( 'integer', 'integer', 'text' ), array( $user_id, $test_id, $message )); } /** * Report Transfer Error * * @param int $user_id the user whos CME points transfer failed * @param int $test_id the CME test id which the user passed * @param string $post_result the transfer post answer of the EIV-Fobi server * @param string $uploadfilename the filename of the XML file * @param string $vnr the BÄK Veranstaltungsnummer */ function reportTransferError($user_id, $test_id, $post_result, $uploadfilename, $vnr) { global $ilDB; include_once ("./Services/Visionet/classes/class.ilVisionetUtil.php"); $parser = ilVisionetUtil::createHtmlDomParser(); $parser->load($post_result); $message = $post_result; $nodes = $parser->find('font[color="red"]'); if (count($nodes) > 0) { $message = ''; foreach ($nodes as $node) { $message .= strip_tags($node->innertext) . "\n"; } } error_log("reportTransferError: message: " . $message, 0); // und einen Eintrag in die Transfertabelle schreiben // // `transfer_time` timestamp NOT NULL, // `transfer_message` varchar(512) NOT NULL, // `transfer_success` int(11) NOT NULL, $affectedRows = $ilDB->manipulateF(" INSERT INTO cme_points_transfer " . " ( user_fi, test_fi, transfer_time, transfer_message, transfer_success ) " . " VALUES ( %s, %s, NOW(), %s, 0 ) ", array( 'integer', 'integer', 'text' ), array( $user_id, $test_id, $message )); } /** * Retreive the CME points of a given userin a years hash * * @param int $a_user_id the user id * * @return object containing a years-hash and the corresponding CME points for this year and a total of all CME points */ static function getCMEMyPointsYearsOfUser($a_user_id) { global $ilDB, $lng; $lng->loadLanguageModule("cme"); include_once ("./Services/Calendar/classes/class.ilDateTime.php"); $query = "SELECT cme.test_finished, cme.cme_points from cme_points cme" . " WHERE cme.user_fi = %s ORDER BY cme.test_finished desc"; $years = array( "_all_years_" => $lng->txt("cme_filter_all_years") ); $cme_points = array(); $cme_points["_all_years_"] = 0; $set = $ilDB->queryF($query, array( 'integer' ), array( $a_user_id )); while ($rec = $ilDB->fetchAssoc($set)) { $test_finished_obj = new ilDateTime($rec["test_finished"], IL_CAL_DATETIME, 'UTC'); $year = $test_finished_obj->get(IL_CAL_FKT_DATE, 'Y'); if (empty($years[$year])) { $years[$year] = $year; $cme_points[$year] = 0; } $cme_points["_all_years_"] += $rec["cme_points"]; $cme_points[$year] += $rec["cme_points"]; // error_log("getCMEMyPointsYearsOfUser: year: $year", 0); // error_log("getCMEMyPointsYearsOfUser: cme_points: " . $cme_points[$year], 0); } return array( "years" => $years, "cme_points" => $cme_points ); } /** * Retreive a list of Tests and CME points of the given user for the given year * or for all years if second parameter is omitted * * @param int $a_user_id the user id * @param int $a_year the year * * @return array mostly the columns of the table cme_points and additionally the title and description of the test */ static function getCMEMyPointsOfUser($a_user_id, $a_year) { global $ilDB, $ilUser; error_log("getCMEMyPointsOfUser: year: " . $a_year, 0); include_once ("./Services/Calendar/classes/class.ilDateTime.php"); include_once ("./Services/Calendar/classes/class.ilDatePresentation.php"); $old = ilDatePresentation::useRelativeDates(); ilDatePresentation::setUseRelativeDates(false); $param_types = array( 'integer' ); $param_values = array( $a_user_id ); $query = "SELECT obj.obj_id, obj.title, obj.description, cme.test_fi, cme.test_finished, cme.active_fi, cme.ref_id, cme.vnr, cme.laek, cme.cme_points, cme.cme_max_points, ta.tries, tt.author " . " FROM cme_points cme, tst_tests tt, tst_active ta, object_data obj " . " WHERE " . " cme.active_fi = ta.active_id and " . " cme.user_fi = ta.user_fi and " . " cme.test_fi = ta.test_fi and " . " cme.test_fi = tt.test_id and " . " tt.cme_test = 1 and " . " tt.obj_fi = obj.obj_id and " . " cme.user_fi = %s "; if (! empty($a_year) and $a_year != "_all_years_") { error_log("getCMEMyPointsOfUser: NOT EMPTY: year: " . $a_year, 0); $query .= " and cme.test_finished between %s and %s "; array_push($param_types, 'timestamp'); array_push($param_types, 'timestamp'); array_push($param_values, "$a_year-01-01 00:00:00"); array_push($param_values, "$a_year-12-31 23:59:59"); } $query .= " ORDER BY cme.test_finished"; error_log("getCMEMyPointsOfUser: $query", 0); $set = $ilDB->queryF($query, $param_types, $param_values); $res = array(); while ($rec = $ilDB->fetchAssoc($set)) { $test_finished_obj = new ilDateTime($rec["test_finished"], IL_CAL_DATETIME, $ilUser->getTimeZone()); $rec["test_finished_obj"] = $test_finished_obj; $rec["test_finished_format"] = ilDatePresentation::formatDate($test_finished_obj); $res[] = $rec; } ilDatePresentation::setUseRelativeDates($old); return $res; } }