@@ -168,36 +168,56 @@ public function deleteCertificate(): bool
168168 }
169169
170170 /**
171- * Generates an HTML Certificate and fills the path_certificate field in the DB.
171+ * Generates (or updates) the user's certificate as a Resource.
172+ *
173+ * - Stores the HTML in a ResourceNode (resource_type = user_certificate or fallback handled at repository level).
174+ * - Fills $this->certificate_data['file_content'] with the HTML to avoid PDF errors.
175+ * - Keeps legacy DB info via registerUserInfoAboutCertificate() (no PersonalFile usage).
172176 *
173177 * @param array $params
174178 * @param bool $sendNotification
175179 *
176- * @return bool|int
180+ * @return bool
177181 */
178182 public function generate ($ params = [], $ sendNotification = false )
179183 {
180- $ result = false ;
181- $ params ['hide_print_button ' ] = isset ($ params ['hide_print_button ' ]) ? true : false ;
184+ // Safe defaults
185+ $ params = is_array ($ params ) ? $ params : [];
186+ $ params ['hide_print_button ' ] = isset ($ params ['hide_print_button ' ])
187+ ? (bool ) $ params ['hide_print_button ' ]
188+ : false ;
189+
190+ $ certRepo = Container::getGradeBookCertificateRepository ();
191+
182192 $ categoryId = 0 ;
193+ $ category = null ;
183194 $ isCertificateAvailableInCategory = false ;
184- $ category = null ;
195+
196+ // If the certificate is linked to a Gradebook category, check availability
185197 if (isset ($ this ->certificate_data ['cat_id ' ])) {
186198 $ categoryId = (int ) $ this ->certificate_data ['cat_id ' ];
199+
200+ // Category::load() returns an array
187201 $ myCategory = Category::load ($ categoryId );
202+
188203 $ repo = Container::getGradeBookCategoryRepository ();
189- /** @var GradebookCategory $category */
204+ /** @var \Chamilo\CoreBundle\Entity\ GradebookCategory|null $category */
190205 $ category = $ repo ->find ($ categoryId );
191- $ isCertificateAvailableInCategory = !empty ($ categoryId ) && $ myCategory [0 ]->is_certificate_available ($ this ->user_id );
192- }
193206
194- $ certRepo = Container::getGradeBookCertificateRepository ();
207+ if (!empty ($ categoryId ) && !empty ($ myCategory ) && isset ($ myCategory [0 ])) {
208+ $ isCertificateAvailableInCategory = $ myCategory [0 ]->is_certificate_available ($ this ->user_id );
209+ }
210+ }
195211
212+ // Path A: course/session-bound certificate
196213 if ($ isCertificateAvailableInCategory && null !== $ category ) {
197- $ courseInfo = api_get_course_info ($ category ->getCourse ()->getCode ());
214+ // Course/session info
215+ $ course = $ category ->getCourse ();
216+ $ courseInfo = api_get_course_info ($ course ->getCode ());
198217 $ courseId = $ courseInfo ['real_id ' ];
199- $ sessionId = $ category ->getSession () ? $ category ->getSession ()->getId () : 0 ;
218+ $ sessionId = $ category ->getSession () ? ( int ) $ category ->getSession ()->getId () : 0 ;
200219
220+ // Award related skill
201221 $ skill = new SkillModel ();
202222 $ skill ->addSkillToUser (
203223 $ this ->user_id ,
@@ -206,63 +226,88 @@ public function generate($params = [], $sendNotification = false)
206226 $ sessionId
207227 );
208228
209- if (! empty ( $ this -> certificate_data )) {
210- $ gb = GradebookUtils::get_user_certificate_content (
211- $ this ->user_id ,
212- $ category -> getCourse () ->getId (),
213- $ category -> getSession () ? $ category -> getSession ()-> getId () : 0 ,
214- false ,
215- $ params ['hide_print_button ' ]
216- );
229+ // Build certificate HTML and score
230+ $ gb = GradebookUtils::get_user_certificate_content (
231+ $ this ->user_id ,
232+ $ course ->getId (),
233+ $ sessionId ,
234+ false ,
235+ $ params ['hide_print_button ' ]
236+ );
217237
218- $ html = is_array ( $ gb ) && isset ( $ gb [ ' content ' ]) ? $ gb [ ' content ' ] : '' ;
219- $ score = isset ( $ gb [ ' score ' ]) ? ( float ) $ gb [ ' score ' ] : 100.0 ;
238+ $ html = '' ;
239+ $ score = 100.0 ;
220240
221- try {
222- // Upsert como Resource (resource_type = user_certificate)
223- $ certEntity = $ certRepo ->upsertCertificateResource ($ categoryId , $ this ->user_id , $ score , $ html );
224- $ certRepo ->registerUserInfoAboutCertificate ($ categoryId , $ this ->user_id , $ score );
225-
226- $ this ->certification_user_path = 'resource://user_certificate ' ;
227- $ this ->certificate_data ['path_certificate ' ] = '' ;
228-
229- if ($ this ->isHtmlFileGenerated () && $ sendNotification ) {
230- $ subject = get_lang ('Certificate notification ' );
231- $ message = nl2br (get_lang ('((user_first_name)), ' ));
232- $ htmlUrl = $ certRepo ->getResourceFileUrl ($ certEntity );
233-
234- self ::sendNotification (
235- $ subject ,
236- $ message ,
237- api_get_user_info ($ this ->user_id ),
238- $ courseInfo ,
239- [
240- 'score_certificate ' => $ score ,
241- 'html_url ' => $ htmlUrl ,
242- ]
243- );
244- }
241+ if (is_array ($ gb )) {
242+ $ html = isset ($ gb ['content ' ]) ? (string ) $ gb ['content ' ] : '' ;
243+ $ score = isset ($ gb ['score ' ]) ? (float ) $ gb ['score ' ] : 100.0 ;
244+ } elseif (is_string ($ gb ) && $ gb !== '' ) {
245+ // Some custom implementations might return a raw string
246+ $ html = $ gb ;
247+ }
245248
246- return true ;
247- } catch (\Throwable $ e ) {
248- error_log ('[CERTIFICATE::generate] upsert error: ' .$ e ->getMessage ());
249- return false ;
250- }
249+ if ($ html === '' ) {
250+ error_log ('[CERT::generate] Empty HTML content for category certificate (cat= ' .$ categoryId .', user= ' .$ this ->user_id .'). ' );
251+ return false ;
251252 }
252- } else {
253- $ gbHtml = $ this ->generateCustomCertificate ();
253+
254254 try {
255- $ certEntity = $ certRepo ->upsertCertificateResource (0 , $ this ->user_id , 100.0 , $ gbHtml );
256- $ this ->certificate_data ['file_content ' ] = $ certRepo ->getResourceFileContent ($ certEntity );
257- $ this ->certificate_data ['path_certificate ' ] = '' ;
255+ // Persist as Resource and register legacy info (no PersonalFile)
256+ $ entity = $ certRepo ->upsertCertificateResource ($ categoryId , $ this ->user_id , $ score , $ html );
257+ $ certRepo ->registerUserInfoAboutCertificate ($ categoryId , $ this ->user_id , $ score );
258+
259+ // Ensure PDF flow has the HTML in memory
260+ $ this ->certificate_data ['file_content ' ] = $ html ;
261+ $ this ->certificate_data ['path_certificate ' ] = '' ; // stored as resource, no legacy file path
262+
263+ // Send notification if required (we have course context here)
264+ if ($ sendNotification ) {
265+ $ subject = get_lang ('Certificate notification ' );
266+ $ message = nl2br (get_lang ('((user_first_name)), ' ));
267+ $ htmlUrl = $ certRepo ->getResourceFileUrl ($ entity );
268+
269+ self ::sendNotification (
270+ $ subject ,
271+ $ message ,
272+ api_get_user_info ($ this ->user_id ),
273+ $ courseInfo ,
274+ [
275+ 'score_certificate ' => $ score ,
276+ 'html_url ' => $ htmlUrl ,
277+ ]
278+ );
279+ }
280+
258281 return true ;
259282 } catch (\Throwable $ e ) {
260- error_log ('[CERTIFICATE ::generate] general certificate upsert error : ' .$ e ->getMessage ());
283+ error_log ('[CERT ::generate] Upsert failed for category certificate (cat= ' . $ categoryId . ' , user= ' . $ this -> user_id . ' ) : ' .$ e ->getMessage ());
261284 return false ;
262285 }
263286 }
264287
265- return false ;
288+ // Path B: general (portal-wide) certificate
289+ try {
290+ $ html = $ this ->generateCustomCertificate ('' );
291+ $ score = 100.0 ;
292+
293+ if ($ html === '' ) {
294+ error_log ('[CERT::generate] Empty HTML content for general certificate (user= ' .$ this ->user_id .'). ' );
295+ return false ;
296+ }
297+
298+ $ entity = $ certRepo ->upsertCertificateResource (0 , $ this ->user_id , $ score , $ html );
299+ $ certRepo ->registerUserInfoAboutCertificate (0 , $ this ->user_id , $ score );
300+
301+ // Ensure PDF flow has the HTML in memory
302+ $ this ->certificate_data ['file_content ' ] = $ html ;
303+ $ this ->certificate_data ['path_certificate ' ] = '' ; // stored as resource
304+
305+ // No course context here, so we skip notification (sendNotification would fail its own checks)
306+ return true ;
307+ } catch (\Throwable $ e ) {
308+ error_log ('[CERT::generate] General certificate upsert failed (user= ' .$ this ->user_id .'): ' .$ e ->getMessage ());
309+ return false ;
310+ }
266311 }
267312
268313 /**
0 commit comments