1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
4
|
|
|
|
5
|
|
|
use Chamilo\CoreBundle\Entity\GradebookCategory; |
6
|
|
|
use Chamilo\CoreBundle\Entity\PersonalFile; |
7
|
|
|
use Chamilo\CoreBundle\Entity\ResourceFile; |
8
|
|
|
use Chamilo\CoreBundle\Entity\ResourceNode; |
9
|
|
|
use Chamilo\CoreBundle\Framework\Container; |
10
|
|
|
use Endroid\QrCode\ErrorCorrectionLevel; |
11
|
|
|
use Endroid\QrCode\QrCode; |
12
|
|
|
use JetBrains\PhpStorm\NoReturn; |
13
|
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Certificate Class |
17
|
|
|
* Generate certificates based in the gradebook tool. |
18
|
|
|
*/ |
19
|
|
|
class Certificate extends Model |
20
|
|
|
{ |
21
|
|
|
public $table; |
22
|
|
|
public $columns = [ |
23
|
|
|
'id', |
24
|
|
|
'cat_id', |
25
|
|
|
'score_certificate', |
26
|
|
|
'created_at', |
27
|
|
|
'path_certificate', |
28
|
|
|
]; |
29
|
|
|
/** |
30
|
|
|
* Certification data. |
31
|
|
|
*/ |
32
|
|
|
public $certificate_data = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Student's certification path. |
36
|
|
|
*/ |
37
|
|
|
public $certification_user_path = null; |
38
|
|
|
public $certification_web_user_path = null; |
39
|
|
|
public $html_file = null; |
40
|
|
|
public $qr_file = null; |
41
|
|
|
public $user_id; |
42
|
|
|
|
43
|
|
|
/** If true every time we enter to the certificate URL |
44
|
|
|
* we would generate a new certificate (good thing because we can edit the |
45
|
|
|
* certificate and all users will have the latest certificate bad because we. |
46
|
|
|
* load the certificate every time */ |
47
|
|
|
public $force_certificate_generation = true; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Constructor. |
51
|
|
|
* |
52
|
|
|
* @param int $certificate_id ID of the certificate |
53
|
|
|
* @param int $userId |
54
|
|
|
* @param bool $sendNotification send message to student |
55
|
|
|
* @param bool $updateCertificateData |
56
|
|
|
* @param string $pathToCertificate |
57
|
|
|
* |
58
|
|
|
* If no ID given, take user_id and try to generate one |
59
|
|
|
*/ |
60
|
|
|
public function __construct( |
61
|
|
|
$certificate_id = 0, |
62
|
|
|
$userId = 0, |
63
|
|
|
$sendNotification = false, |
64
|
|
|
$updateCertificateData = true, |
65
|
|
|
$pathToCertificate = '' |
66
|
|
|
) { |
67
|
|
|
$this->table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE); |
68
|
|
|
$this->user_id = !empty($userId) ? $userId : api_get_user_id(); |
69
|
|
|
|
70
|
|
|
if (!empty($certificate_id)) { |
71
|
|
|
$certificate = $this->get($certificate_id); |
72
|
|
|
if (!empty($certificate) && is_array($certificate)) { |
73
|
|
|
$this->certificate_data = $certificate; |
74
|
|
|
$this->user_id = $this->certificate_data['user_id']; |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
if ($this->user_id) { |
79
|
|
|
// To force certification generation |
80
|
|
|
if ($this->force_certificate_generation) { |
81
|
|
|
$this->generate(['certificate_path' => ''], $sendNotification); |
82
|
|
|
} |
83
|
|
|
if ( |
84
|
|
|
isset($this->certificate_data) |
85
|
|
|
&& $this->certificate_data |
86
|
|
|
&& empty($this->certificate_data['path_certificate']) |
87
|
|
|
) { |
88
|
|
|
$this->generate(['certificate_path' => $pathToCertificate], $sendNotification); |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
// Setting the qr and html variables |
93
|
|
|
if (isset($certificate_id) && |
94
|
|
|
!empty($this->certification_user_path) && |
95
|
|
|
isset($this->certificate_data['path_certificate']) |
96
|
|
|
) { |
97
|
|
|
//$pathinfo = pathinfo($this->certificate_data['path_certificate']); |
98
|
|
|
$this->html_file = $this->certificate_data['path_certificate']; |
99
|
|
|
//$this->qr_file = $this->certification_user_path.$pathinfo['filename'].'_qr.png'; |
100
|
|
|
} else { |
101
|
|
|
//$this->checkCertificatePath(); |
102
|
|
|
if ('true' === api_get_setting('document.allow_general_certificate')) { |
103
|
|
|
// General certificate |
104
|
|
|
$categoryId = isset($this->certificate_data['cat_id']) ? (int) $this->certificate_data['cat_id'] : 0; |
105
|
|
|
$name = hash('sha256', $this->user_id . $categoryId); |
106
|
|
|
$fileName = $name . '.html'; |
107
|
|
|
$content = $this->generateCustomCertificate(); |
108
|
|
|
$gradebookCertificateRepo = Container::getGradeBookCertificateRepository(); |
109
|
|
|
$personalFile = $gradebookCertificateRepo->generateCertificatePersonalFile($this->user_id, $fileName, $content); |
110
|
|
|
|
111
|
|
|
if (null !== $personalFile) { |
112
|
|
|
// Updating the path |
113
|
|
|
self::updateUserCertificateInfo( |
114
|
|
|
0, |
115
|
|
|
$this->user_id, |
116
|
|
|
$fileName, |
117
|
|
|
$updateCertificateData |
118
|
|
|
); |
119
|
|
|
$this->certificate_data['path_certificate'] = $fileName; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Deletes the current certificate object. This is generally triggered by |
127
|
|
|
* the teacher from the gradebook tool to re-generate the certificate because |
128
|
|
|
* the original version wa flawed. |
129
|
|
|
* |
130
|
|
|
* @param bool $force_delete |
131
|
|
|
* |
132
|
|
|
* @return bool |
133
|
|
|
*/ |
134
|
|
|
public function deleteCertificate(): bool |
135
|
|
|
{ |
136
|
|
|
if (!empty($this->certificate_data)) { |
137
|
|
|
$categoryId = isset($this->certificate_data['cat_id']) ? (int) $this->certificate_data['cat_id'] : 0; |
138
|
|
|
$gradebookCertificateRepo = Container::getGradeBookCertificateRepository(); |
139
|
|
|
$gradebookCertificateRepo->deleteCertificateAndRelatedFiles($this->certificate_data['user_id'], $categoryId); |
140
|
|
|
|
141
|
|
|
return true; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return false; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Generates an HTML Certificate and fills the path_certificate field in the DB. |
149
|
|
|
* |
150
|
|
|
* @param array $params |
151
|
|
|
* @param bool $sendNotification |
152
|
|
|
* |
153
|
|
|
* @return bool|int |
154
|
|
|
*/ |
155
|
|
|
public function generate($params = [], $sendNotification = false) |
156
|
|
|
{ |
157
|
|
|
$result = false; |
158
|
|
|
$params['hide_print_button'] = isset($params['hide_print_button']) ? true : false; |
159
|
|
|
$categoryId = 0; |
160
|
|
|
$isCertificateAvailableInCategory = false; |
161
|
|
|
$category = null; |
162
|
|
|
if (isset($this->certificate_data['cat_id'])) { |
163
|
|
|
$categoryId = (int) $this->certificate_data['cat_id']; |
164
|
|
|
$myCategory = Category::load($categoryId); |
165
|
|
|
$repo = Container::getGradeBookCategoryRepository(); |
166
|
|
|
/** @var GradebookCategory $category */ |
167
|
|
|
$category = $repo->find($categoryId); |
168
|
|
|
$isCertificateAvailableInCategory = !empty($categoryId) && $myCategory[0]->is_certificate_available($this->user_id); |
169
|
|
|
} |
170
|
|
|
$container = Container::getResourceNodeRepository(); |
171
|
|
|
$filesystem = $container->getFileSystem(); |
172
|
|
|
|
173
|
|
|
if ($isCertificateAvailableInCategory && null !== $category) { |
174
|
|
|
$courseInfo = api_get_course_info($category->getCourse()->getCode()); |
175
|
|
|
$courseId = $courseInfo['real_id']; |
176
|
|
|
$sessionId = $category->getSession() ? $category->getSession()->getId() : 0; |
177
|
|
|
|
178
|
|
|
$skill = new SkillModel(); |
179
|
|
|
$skill->addSkillToUser( |
180
|
|
|
$this->user_id, |
181
|
|
|
$category, |
182
|
|
|
$courseId, |
183
|
|
|
$sessionId |
184
|
|
|
); |
185
|
|
|
|
186
|
|
|
if (!empty($this->certificate_data)) { |
187
|
|
|
$newContentHtml = GradebookUtils::get_user_certificate_content( |
188
|
|
|
$this->user_id, |
189
|
|
|
$category->getCourse()->getId(), |
190
|
|
|
$category->getSession() ? $category->getSession()->getId() : 0, |
191
|
|
|
false, |
192
|
|
|
$params['hide_print_button'] |
193
|
|
|
); |
194
|
|
|
|
195
|
|
|
if ($category->getId() == $categoryId) { |
196
|
|
|
$myPathCertificate = $this->certificate_data['path_certificate'] ?? ''; |
197
|
|
|
|
198
|
|
|
if ($filesystem->fileExists($myPathCertificate) && |
199
|
|
|
!$this->force_certificate_generation |
200
|
|
|
) { |
201
|
|
|
// Seems that the file was already generated |
202
|
|
|
return true; |
203
|
|
|
} else { |
204
|
|
|
// Creating new name |
205
|
|
|
$name = hash('sha256', $this->user_id . $categoryId); |
206
|
|
|
$fileName = $name . '.html'; |
207
|
|
|
$gradebookCertificateRepo = Container::getGradeBookCertificateRepository(); |
208
|
|
|
$personalFile = $gradebookCertificateRepo->generateCertificatePersonalFile($this->user_id, $fileName, $newContentHtml['content']); |
209
|
|
|
|
210
|
|
|
if (null !== $personalFile) { |
211
|
|
|
$result = true; |
212
|
|
|
// Updating the path |
213
|
|
|
$this->updateUserCertificateInfo( |
214
|
|
|
$this->certificate_data['cat_id'], |
215
|
|
|
$this->user_id, |
216
|
|
|
$fileName |
217
|
|
|
); |
218
|
|
|
$this->certification_user_path = $fileName; |
219
|
|
|
$this->certificate_data['path_certificate'] = $fileName; |
220
|
|
|
|
221
|
|
|
if ($this->isHtmlFileGenerated()) { |
222
|
|
|
if ($sendNotification) { |
223
|
|
|
$subject = get_lang('Certificate notification'); |
224
|
|
|
$message = nl2br(get_lang('((user_first_name)),')); |
225
|
|
|
$score = $this->certificate_data['score_certificate']; |
226
|
|
|
self::sendNotification( |
227
|
|
|
$subject, |
228
|
|
|
$message, |
229
|
|
|
api_get_user_info($this->user_id), |
230
|
|
|
$courseInfo, |
231
|
|
|
[ |
232
|
|
|
'score_certificate' => $score, |
233
|
|
|
] |
234
|
|
|
); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return $result; |
240
|
|
|
} |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
} else { |
244
|
|
|
$name = hash('sha256', $this->user_id . $categoryId); |
245
|
|
|
$fileName = $name . '.html'; |
246
|
|
|
$certificateContent = $this->generateCustomCertificate($fileName); |
247
|
|
|
|
248
|
|
|
$gradebookCertificateRepo = Container::getGradeBookCertificateRepository(); |
249
|
|
|
$personalFile = $gradebookCertificateRepo->generateCertificatePersonalFile($this->user_id, $fileName, $certificateContent); |
250
|
|
|
|
251
|
|
|
if ($personalFile !== null) { |
252
|
|
|
$personalRepo = Container::getPersonalFileRepository(); |
253
|
|
|
$this->certificate_data['file_content'] = $personalRepo->getResourceFileContent($personalFile); |
254
|
|
|
$this->certificate_data['path_certificate'] = $fileName; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
return true; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return false; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* @return array |
265
|
|
|
*/ |
266
|
|
|
public static function notificationTags() |
267
|
|
|
{ |
268
|
|
|
$tags = [ |
269
|
|
|
'((course_title))', |
270
|
|
|
'((user_first_name))', |
271
|
|
|
'((user_last_name))', |
272
|
|
|
'((author_first_name))', |
273
|
|
|
'((author_last_name))', |
274
|
|
|
'((score))', |
275
|
|
|
'((portal_name))', |
276
|
|
|
'((certificate_link))', |
277
|
|
|
]; |
278
|
|
|
|
279
|
|
|
return $tags; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @param string $subject |
284
|
|
|
* @param string $message |
285
|
|
|
* @param array $userInfo |
286
|
|
|
* @param array $courseInfo |
287
|
|
|
* @param array $certificateInfo |
288
|
|
|
* |
289
|
|
|
* @return bool |
290
|
|
|
*/ |
291
|
|
|
public static function sendNotification( |
292
|
|
|
$subject, |
293
|
|
|
$message, |
294
|
|
|
$userInfo, |
295
|
|
|
$courseInfo, |
296
|
|
|
$certificateInfo |
297
|
|
|
) { |
298
|
|
|
if (empty($userInfo) || empty($courseInfo)) { |
299
|
|
|
return false; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
$currentUserInfo = api_get_user_info(); |
303
|
|
|
$url = ''; |
304
|
|
|
if (!empty($certificateInfo['path_certificate'])) { |
305
|
|
|
$hash = pathinfo($certificateInfo['path_certificate'], PATHINFO_FILENAME); |
306
|
|
|
$url = api_get_path(WEB_PATH) . 'certificates/' . $hash . '.html'; |
307
|
|
|
} |
308
|
|
|
$link = Display::url($url, $url); |
309
|
|
|
|
310
|
|
|
$replace = [ |
311
|
|
|
$courseInfo['title'], |
312
|
|
|
$userInfo['firstname'], |
313
|
|
|
$userInfo['lastname'], |
314
|
|
|
$currentUserInfo['firstname'], |
315
|
|
|
$currentUserInfo['lastname'], |
316
|
|
|
$certificateInfo['score_certificate'], |
317
|
|
|
api_get_setting('Institution'), |
318
|
|
|
$link, |
319
|
|
|
]; |
320
|
|
|
|
321
|
|
|
$message = str_replace(self::notificationTags(), $replace, $message); |
322
|
|
|
MessageManager::send_message( |
323
|
|
|
$userInfo['id'], |
324
|
|
|
$subject, |
325
|
|
|
$message, |
326
|
|
|
[], |
327
|
|
|
[], |
328
|
|
|
0, |
329
|
|
|
0, |
330
|
|
|
0, |
331
|
|
|
0, |
332
|
|
|
$currentUserInfo['id'] |
333
|
|
|
); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Update user info about certificate. |
338
|
|
|
* |
339
|
|
|
* @param int $categoryId category id |
340
|
|
|
* @param int $user_id user id |
341
|
|
|
* @param string $path_certificate the path name of the certificate |
342
|
|
|
* @param bool $updateCertificateData |
343
|
|
|
*/ |
344
|
|
|
public function updateUserCertificateInfo( |
345
|
|
|
$categoryId, |
346
|
|
|
$user_id, |
347
|
|
|
$path_certificate, |
348
|
|
|
$updateCertificateData = true |
349
|
|
|
) { |
350
|
|
|
$categoryId = (int) $categoryId; |
351
|
|
|
$user_id = (int) $user_id; |
352
|
|
|
|
353
|
|
|
if ($updateCertificateData && |
354
|
|
|
!UserManager::is_user_certified($categoryId, $user_id) |
355
|
|
|
) { |
356
|
|
|
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE); |
357
|
|
|
$now = api_get_utc_datetime(); |
358
|
|
|
$sql = 'UPDATE '.$table.' SET |
359
|
|
|
path_certificate="'.Database::escape_string($path_certificate).'", |
360
|
|
|
created_at = "'.$now.'" |
361
|
|
|
WHERE cat_id = "'.$categoryId.'" AND user_id="'.$user_id.'" '; |
362
|
|
|
Database::query($sql); |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Check if the file was generated. |
368
|
|
|
* |
369
|
|
|
* @return bool |
370
|
|
|
*/ |
371
|
|
|
public function isHtmlFileGenerated() |
372
|
|
|
{ |
373
|
|
|
if (empty($this->certification_user_path)) { |
374
|
|
|
return false; |
375
|
|
|
} |
376
|
|
|
if (!empty($this->certificate_data) && |
377
|
|
|
isset($this->certificate_data['path_certificate']) && |
378
|
|
|
!empty($this->certificate_data['path_certificate']) |
379
|
|
|
) { |
380
|
|
|
return true; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
return false; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Generates a QR code for the certificate. The QR code embeds the text given. |
388
|
|
|
* |
389
|
|
|
* @param string $text Text to be added in the QR code |
390
|
|
|
* @param string $path file path of the image |
391
|
|
|
* |
392
|
|
|
* @return bool |
393
|
|
|
*/ |
394
|
|
|
public function generateQRImage($text, $path): bool |
395
|
|
|
{ |
396
|
|
|
throw new \Exception('generateQRImage'); |
397
|
|
|
if (!empty($text) && !empty($path)) { |
|
|
|
|
398
|
|
|
$qrCode = new QrCode($text); |
399
|
|
|
//$qrCode->setEncoding('UTF-8'); |
400
|
|
|
$qrCode->setSize(120); |
401
|
|
|
$qrCode->setMargin(5); |
402
|
|
|
/*$qrCode->setWriterByName('png'); |
403
|
|
|
$qrCode->setErrorCorrectionLevel(ErrorCorrectionLevel::MEDIUM()); |
404
|
|
|
$qrCode->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]); |
405
|
|
|
$qrCode->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]); |
406
|
|
|
$qrCode->setValidateResult(false); |
407
|
|
|
$qrCode->writeFile($path);*/ |
408
|
|
|
|
409
|
|
|
return true; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
return false; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Transforms certificate tags into text values. This function is very static |
417
|
|
|
* (it doesn't allow for much flexibility in terms of what tags are printed). |
418
|
|
|
* |
419
|
|
|
* @param array $array Contains two array entries: first are the headers, |
420
|
|
|
* second is an array of contents |
421
|
|
|
* |
422
|
|
|
* @return string The translated string |
423
|
|
|
*/ |
424
|
|
|
public function parseCertificateVariables($array) |
425
|
|
|
{ |
426
|
|
|
$headers = $array[0]; |
427
|
|
|
$content = $array[1]; |
428
|
|
|
$final_content = []; |
429
|
|
|
|
430
|
|
|
if (!empty($content)) { |
431
|
|
|
foreach ($content as $key => $value) { |
432
|
|
|
$my_header = str_replace(['((', '))'], '', $headers[$key]); |
433
|
|
|
$final_content[$my_header] = $value; |
434
|
|
|
} |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/* Certificate tags |
438
|
|
|
* |
439
|
|
|
0 => string '((user_firstname))' (length=18) |
440
|
|
|
1 => string '((user_lastname))' (length=17) |
441
|
|
|
2 => string '((gradebook_institution))' (length=25) |
442
|
|
|
3 => string '((gradebook_sitename))' (length=22) |
443
|
|
|
4 => string '((teacher_firstname))' (length=21) |
444
|
|
|
5 => string '((teacher_lastname))' (length=20) |
445
|
|
|
6 => string '((official_code))' (length=17) |
446
|
|
|
7 => string '((date_certificate))' (length=20) |
447
|
|
|
8 => string '((course_code))' (length=15) |
448
|
|
|
9 => string '((course_title))' (length=16) |
449
|
|
|
10 => string '((gradebook_grade))' (length=19) |
450
|
|
|
11 => string '((certificate_link))' (length=20) |
451
|
|
|
12 => string '((certificate_link_html))' (length=25) |
452
|
|
|
13 => string '((certificate_barcode))' (length=23) |
453
|
|
|
*/ |
454
|
|
|
|
455
|
|
|
$break_space = " \n\r "; |
456
|
|
|
$text = |
457
|
|
|
$final_content['gradebook_institution'].' - '. |
458
|
|
|
$final_content['gradebook_sitename'].' - '. |
459
|
|
|
get_lang('Certification').$break_space. |
460
|
|
|
get_lang('Learner').': '.$final_content['user_firstname'].' '.$final_content['user_lastname'].$break_space. |
461
|
|
|
get_lang('Trainer').': '.$final_content['teacher_firstname'].' '.$final_content['teacher_lastname'].$break_space. |
462
|
|
|
get_lang('Date').': '.$final_content['date_certificate'].$break_space. |
463
|
|
|
get_lang('Score').': '.$final_content['gradebook_grade'].$break_space. |
464
|
|
|
'URL'.': '.$final_content['certificate_link']; |
465
|
|
|
|
466
|
|
|
return $text; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Check if the certificate is visible for the current user |
471
|
|
|
* If the global setting allow_public_certificates is set to 'false', no certificate can be printed. |
472
|
|
|
* If the global allow_public_certificates is set to 'true' and the course setting allow_public_certificates |
473
|
|
|
* is set to 0, no certificate *in this course* can be printed (for anonymous users). |
474
|
|
|
* Connected users can always print them. |
475
|
|
|
* |
476
|
|
|
* @return bool |
477
|
|
|
*/ |
478
|
|
|
public function isVisible() |
479
|
|
|
{ |
480
|
|
|
if (!api_is_anonymous()) { |
481
|
|
|
return true; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
if ('true' != api_get_setting('allow_public_certificates')) { |
485
|
|
|
// The "non-public" setting is set, so do not print |
486
|
|
|
return false; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
if (!isset($this->certificate_data, $this->certificate_data['cat_id'])) { |
490
|
|
|
return false; |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
$gradeBook = new Gradebook(); |
494
|
|
|
$gradeBookInfo = $gradeBook->get($this->certificate_data['cat_id']); |
495
|
|
|
|
496
|
|
|
if (empty($gradeBookInfo['course_code'])) { |
497
|
|
|
return false; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
$setting = api_get_course_setting( |
501
|
|
|
'allow_public_certificates', |
502
|
|
|
api_get_course_info($gradeBookInfo['course_code']) |
503
|
|
|
); |
504
|
|
|
|
505
|
|
|
if (0 == $setting) { |
506
|
|
|
// Printing not allowed |
507
|
|
|
return false; |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
return true; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Check if the certificate is available. |
515
|
|
|
* |
516
|
|
|
* @return bool |
517
|
|
|
*/ |
518
|
|
|
public function isAvailable() |
519
|
|
|
{ |
520
|
|
|
if (empty($this->certificate_data['path_certificate'])) { |
521
|
|
|
return false; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
$container = Container::getResourceNodeRepository(); |
525
|
|
|
$filesystem = $container->getFileSystem(); |
526
|
|
|
if (!$filesystem->fileExists($this->certificate_data['path_certificate'])) { |
527
|
|
|
return false; |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
return true; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Shows the student's certificate (HTML file). |
535
|
|
|
*/ |
536
|
|
|
public function show() |
537
|
|
|
{ |
538
|
|
|
$container = Container::getResourceNodeRepository(); |
539
|
|
|
$filesystem = $container->getFileSystem(); |
540
|
|
|
if ($filesystem->fileExists($this->certificate_data['path_certificate'])) { |
541
|
|
|
// Needed in order to browsers don't add custom CSS |
542
|
|
|
$certificateContent = '<!DOCTYPE html>'; |
543
|
|
|
$certificateContent .= $filesystem->read($this->certificate_data['path_certificate']); |
544
|
|
|
|
545
|
|
|
// Remove media=screen to be available when printing a document |
546
|
|
|
$certificateContent = str_replace( |
547
|
|
|
' media="screen"', |
548
|
|
|
'', |
549
|
|
|
$certificateContent |
550
|
|
|
); |
551
|
|
|
|
552
|
|
|
if ($this->user_id == api_get_user_id() && |
553
|
|
|
!empty($this->certificate_data) && |
554
|
|
|
isset($this->certificate_data['id']) |
555
|
|
|
) { |
556
|
|
|
$certificateId = $this->certificate_data['id']; |
557
|
|
|
$extraFieldValue = new ExtraFieldValue('user_certificate'); |
558
|
|
|
$value = $extraFieldValue->get_values_by_handler_and_field_variable( |
559
|
|
|
$certificateId, |
560
|
|
|
'downloaded_at' |
561
|
|
|
); |
562
|
|
|
if (empty($value)) { |
563
|
|
|
$params = [ |
564
|
|
|
'item_id' => $this->certificate_data['id'], |
565
|
|
|
'extra_downloaded_at' => api_get_utc_datetime(), |
566
|
|
|
]; |
567
|
|
|
$extraFieldValue->saveFieldValues($params); |
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
header('Content-Type: text/html; charset='.api_get_system_encoding()); |
572
|
|
|
echo $certificateContent; |
573
|
|
|
|
574
|
|
|
return; |
575
|
|
|
} |
576
|
|
|
api_not_allowed(true); |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
/** |
580
|
|
|
* @return string |
581
|
|
|
*/ |
582
|
|
|
public function generateCustomCertificate(string $fileName = ''): string |
583
|
|
|
{ |
584
|
|
|
$certificateRepo = Container::getGradeBookCertificateRepository(); |
585
|
|
|
$certificateRepo->registerUserInfoAboutCertificate(0, $this->user_id, 100, $fileName); |
586
|
|
|
|
587
|
|
|
$userInfo = api_get_user_info($this->user_id); |
588
|
|
|
$extraFieldValue = new ExtraFieldValue('user'); |
589
|
|
|
$value = $extraFieldValue->get_values_by_handler_and_field_variable($this->user_id, 'legal_accept'); |
590
|
|
|
$termsValidationDate = ''; |
591
|
|
|
if (isset($value) && !empty($value['value'])) { |
592
|
|
|
[$id, $id2, $termsValidationDate] = explode(':', $value['value']); |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
$sessions = SessionManager::get_sessions_by_user($this->user_id, false, true); |
596
|
|
|
$totalTimeInLearningPaths = 0; |
597
|
|
|
$sessionsApproved = []; |
598
|
|
|
$coursesApproved = []; |
599
|
|
|
$courseList = []; |
600
|
|
|
|
601
|
|
|
$gradeBookRepo = Container::getGradeBookCategoryRepository(); |
602
|
|
|
if ($sessions) { |
603
|
|
|
foreach ($sessions as $session) { |
604
|
|
|
$allCoursesApproved = []; |
605
|
|
|
foreach ($session['courses'] as $course) { |
606
|
|
|
$course = api_get_course_entity($course['real_id']); |
607
|
|
|
$courseId = $course->getId(); |
608
|
|
|
/* @var GradebookCategory $category */ |
609
|
|
|
$category = $gradeBookRepo->findOneBy(['course' => $course, 'session' => $session['session_id']]); |
610
|
|
|
|
611
|
|
|
if (null !== $category) { |
612
|
|
|
$result = Category::userFinishedCourse( |
613
|
|
|
$this->user_id, |
614
|
|
|
$category, |
615
|
|
|
true, |
616
|
|
|
$courseId, |
617
|
|
|
$session['session_id'] |
618
|
|
|
); |
619
|
|
|
|
620
|
|
|
$lpList = new LearnpathList( |
621
|
|
|
$this->user_id, |
622
|
|
|
api_get_course_info_by_id($courseId), |
623
|
|
|
$session['session_id'] |
624
|
|
|
); |
625
|
|
|
$lpFlatList = $lpList->get_flat_list(); |
626
|
|
|
|
627
|
|
|
// Find time spent in LP |
628
|
|
|
$timeSpent = Tracking::get_time_spent_in_lp( |
629
|
|
|
$this->user_id, |
630
|
|
|
$course, |
631
|
|
|
!empty($lpFlatList) ? array_keys($lpFlatList) : [], |
632
|
|
|
$session['session_id'] |
633
|
|
|
); |
634
|
|
|
|
635
|
|
|
if (!isset($courseList[$courseId])) { |
636
|
|
|
$courseList[$courseId]['approved'] = false; |
637
|
|
|
$courseList[$courseId]['time_spent'] = 0; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
if ($result) { |
641
|
|
|
$courseList[$courseId]['approved'] = true; |
642
|
|
|
$coursesApproved[$courseId] = $course->getTitle(); |
643
|
|
|
$allCoursesApproved[] = true; |
644
|
|
|
} |
645
|
|
|
$courseList[$courseId]['time_spent'] += $timeSpent; |
646
|
|
|
} |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
if (count($allCoursesApproved) == count($session['courses'])) { |
650
|
|
|
$sessionsApproved[] = $session; |
651
|
|
|
} |
652
|
|
|
} |
653
|
|
|
} |
654
|
|
|
|
655
|
|
|
$totalTimeInLearningPaths = 0; |
656
|
|
|
foreach ($courseList as $courseId => $courseData) { |
657
|
|
|
if (true === $courseData['approved']) { |
658
|
|
|
$totalTimeInLearningPaths += $courseData['time_spent']; |
659
|
|
|
} |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
$skill = new SkillModel(); |
663
|
|
|
$skills = $skill->getStudentSkills($this->user_id, 2); |
664
|
|
|
$allowAll = ('true' === api_get_setting('skill.allow_teacher_access_student_skills')); |
665
|
|
|
$courseIdForSkills = $allowAll ? 0 : 0; |
666
|
|
|
$sessionIdForSkills = $allowAll ? 0 : 0; |
667
|
|
|
$skillsTable = $skill->getUserSkillsTable( |
668
|
|
|
$this->user_id, |
669
|
|
|
$courseIdForSkills, |
670
|
|
|
$sessionIdForSkills, |
671
|
|
|
false |
672
|
|
|
); |
673
|
|
|
|
674
|
|
|
$timeInSeconds = Tracking::get_time_spent_on_the_platform( |
675
|
|
|
$this->user_id, |
676
|
|
|
'ever' |
677
|
|
|
); |
678
|
|
|
$time = api_time_to_hms($timeInSeconds); |
679
|
|
|
|
680
|
|
|
$tplContent = new Template(null, false, false, false, false, false); |
681
|
|
|
|
682
|
|
|
// variables for the default template |
683
|
|
|
$tplContent->assign('complete_name', $userInfo['complete_name']); |
684
|
|
|
$tplContent->assign('time_in_platform', $time); |
685
|
|
|
$tplContent->assign('certificate_generated_date', isset($myCertificate['created_at']) ? api_get_local_time($myCertificate['created_at']) : ''); |
686
|
|
|
if (!empty($termsValidationDate)) { |
687
|
|
|
$termsValidationDate = api_get_local_time($termsValidationDate); |
688
|
|
|
} |
689
|
|
|
$tplContent->assign('terms_validation_date', $termsValidationDate); |
690
|
|
|
|
691
|
|
|
if (empty($totalTimeInLearningPaths)) { |
692
|
|
|
$totalTimeInLearningPaths = $timeInSeconds; |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
// Ofaj |
696
|
|
|
$tplContent->assign('time_in_platform_in_hours', round($timeInSeconds/3600, 1)); |
697
|
|
|
$tplContent->assign( |
698
|
|
|
'certificate_generated_date_no_time', |
699
|
|
|
api_get_local_time( |
700
|
|
|
$myCertificate['created_at'] ?? null, |
701
|
|
|
null, |
702
|
|
|
null, |
703
|
|
|
false, |
704
|
|
|
false, |
705
|
|
|
false, |
706
|
|
|
'd-m-Y' |
707
|
|
|
) |
708
|
|
|
); |
709
|
|
|
$tplContent->assign( |
710
|
|
|
'terms_validation_date_no_time', |
711
|
|
|
api_get_local_time( |
712
|
|
|
$termsValidationDate, |
713
|
|
|
null, |
714
|
|
|
null, |
715
|
|
|
false, |
716
|
|
|
false, |
717
|
|
|
false, |
718
|
|
|
'd-m-Y' |
719
|
|
|
) |
720
|
|
|
); |
721
|
|
|
$tplContent->assign('skills', $skills); |
722
|
|
|
$tplContent->assign('skills_table_html', $skillsTable['table']); |
723
|
|
|
$tplContent->assign('skills_rows', $skillsTable['skills']); |
724
|
|
|
$tplContent->assign('sessions', $sessionsApproved); |
725
|
|
|
$tplContent->assign('courses', $coursesApproved); |
726
|
|
|
$tplContent->assign('time_spent_in_lps', api_time_to_hms($totalTimeInLearningPaths)); |
727
|
|
|
$tplContent->assign('time_spent_in_lps_in_hours', round($totalTimeInLearningPaths/3600, 1)); |
728
|
|
|
|
729
|
|
|
$layoutContent = $tplContent->get_template('gradebook/custom_certificate.html.twig'); |
730
|
|
|
$content = $tplContent->fetch($layoutContent); |
731
|
|
|
|
732
|
|
|
return $content; |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
/** |
736
|
|
|
* Ofaj. |
737
|
|
|
*/ |
738
|
|
|
public function generatePdfFromCustomCertificate(): void |
739
|
|
|
{ |
740
|
|
|
$orientation = api_get_setting('document.certificate_pdf_orientation'); |
741
|
|
|
|
742
|
|
|
$params['orientation'] = 'landscape'; |
743
|
|
|
if (!empty($orientation)) { |
744
|
|
|
$params['orientation'] = $orientation; |
745
|
|
|
} |
746
|
|
|
|
747
|
|
|
$params['left'] = 0; |
748
|
|
|
$params['right'] = 0; |
749
|
|
|
$params['top'] = 0; |
750
|
|
|
$params['bottom'] = 0; |
751
|
|
|
$page_format = 'landscape' == $params['orientation'] ? 'A4-L' : 'A4'; |
752
|
|
|
$pdf = new PDF($page_format, $params['orientation'], $params); |
753
|
|
|
|
754
|
|
|
$pdf->content_to_pdf( |
755
|
|
|
$this->certificate_data['file_content'], |
756
|
|
|
null, |
757
|
|
|
get_lang('Certificates'), |
758
|
|
|
api_get_course_id(), |
759
|
|
|
'D', |
760
|
|
|
false, |
761
|
|
|
null, |
762
|
|
|
false, |
763
|
|
|
true, |
764
|
|
|
true, |
765
|
|
|
true, |
766
|
|
|
true |
767
|
|
|
); |
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
/** |
771
|
|
|
* @param int $userId |
772
|
|
|
* |
773
|
|
|
* @return array |
774
|
|
|
*/ |
775
|
|
|
public static function getCertificateByUser($userId) |
776
|
|
|
{ |
777
|
|
|
$userId = (int) $userId; |
778
|
|
|
if (empty($userId)) { |
779
|
|
|
return []; |
780
|
|
|
} |
781
|
|
|
|
782
|
|
|
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE); |
783
|
|
|
$sql = "SELECT * FROM $table |
784
|
|
|
WHERE user_id= $userId"; |
785
|
|
|
$rs = Database::query($sql); |
786
|
|
|
|
787
|
|
|
return Database::store_result($rs, 'ASSOC'); |
788
|
|
|
} |
789
|
|
|
|
790
|
|
|
/** |
791
|
|
|
* @param int $userId |
792
|
|
|
*/ |
793
|
|
|
public static function generateUserSkills($userId) |
794
|
|
|
{ |
795
|
|
|
$controller = new IndexManager(get_lang('My courses')); |
796
|
|
|
$courseAndSessions = $controller->returnCoursesAndSessions($userId, true, null, true, false); |
797
|
|
|
$repo = Container::getGradeBookCategoryRepository(); |
798
|
|
|
if (isset($courseAndSessions['courses']) && !empty($courseAndSessions['courses'])) { |
799
|
|
|
foreach ($courseAndSessions['courses'] as $course) { |
800
|
|
|
$category = $repo->findOneBy(['course' => $course['real_id']]); |
801
|
|
|
/*$cats = Category::load( |
802
|
|
|
null, |
803
|
|
|
null, |
804
|
|
|
$course['code'], |
805
|
|
|
null, |
806
|
|
|
null, |
807
|
|
|
null, |
808
|
|
|
false |
809
|
|
|
);*/ |
810
|
|
|
if (null !== $category) { |
811
|
|
|
Category::generateUserCertificate($category, $userId); |
812
|
|
|
} |
813
|
|
|
} |
814
|
|
|
} |
815
|
|
|
|
816
|
|
|
if (isset($courseAndSessions['sessions']) && !empty($courseAndSessions['sessions'])) { |
817
|
|
|
foreach ($courseAndSessions['sessions'] as $sessionCategory) { |
818
|
|
|
if (isset($sessionCategory['sessions'])) { |
819
|
|
|
foreach ($sessionCategory['sessions'] as $sessionData) { |
820
|
|
|
if (!empty($sessionData['courses'])) { |
821
|
|
|
$sessionId = $sessionData['session_id']; |
822
|
|
|
foreach ($sessionData['courses'] as $courseData) { |
823
|
|
|
/*$cats = Category:: load( |
824
|
|
|
null, |
825
|
|
|
null, |
826
|
|
|
$courseData['course_code'], |
827
|
|
|
null, |
828
|
|
|
null, |
829
|
|
|
$sessionId, |
830
|
|
|
false |
831
|
|
|
);*/ |
832
|
|
|
|
833
|
|
|
$category = $repo->findOneBy( |
834
|
|
|
['course' => $courseData['real_id'], 'session' => $sessionId] |
835
|
|
|
); |
836
|
|
|
if (null !== $category) { |
837
|
|
|
Category::generateUserCertificate($category, $userId); |
838
|
|
|
} |
839
|
|
|
} |
840
|
|
|
} |
841
|
|
|
} |
842
|
|
|
} |
843
|
|
|
} |
844
|
|
|
} |
845
|
|
|
} |
846
|
|
|
} |
847
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.