1 /*
2 * Copyright (c) 2002-2025, City of Paris
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice
10 * and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice
13 * and the following disclaimer in the documentation and/or other materials
14 * provided with the distribution.
15 *
16 * 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * License 1.0
33 */
34 package fr.paris.lutece.portal.service.mail;
35
36 import fr.paris.lutece.portal.service.daemon.AppDaemonService;
37 import fr.paris.lutece.portal.service.daemon.Daemon;
38 import fr.paris.lutece.portal.service.datastore.DatastoreService;
39 import fr.paris.lutece.portal.service.portal.PortalService;
40 import fr.paris.lutece.portal.service.spring.SpringContextService;
41 import fr.paris.lutece.portal.service.util.AppPathService;
42 import fr.paris.lutece.portal.service.util.AppPropertiesService;
43 import fr.paris.lutece.util.mail.FileAttachment;
44 import fr.paris.lutece.util.mail.UrlAttachment;
45
46 import java.util.List;
47
48 /**
49 * Application Mail Service
50 */
51 public final class MailService
52 {
53 private static final String KEY_NO_REPLY_EMAIL = "portal.site.site_property.noreply_email";
54 private static final String PROPERTY_MAIL_NOREPLY_EMAIL = "mail.noreply.email";
55 private static final String BEAN_MAIL_QUEUE = "mailQueue";
56
57 /** Creates a new instance of AppMailService */
58 private MailService( )
59 {
60 }
61
62 /**
63 * Enqueues a mail item to be sent
64 *
65 * @param item
66 * the mail item to enqueue
67 */
68 private static void enqueue( MailItem item )
69 {
70 getQueue( ).send( item );
71 AppDaemonService.signalDaemon( MailSenderDaemon.DAEMON_ID );
72 }
73
74 /**
75 * Send a HTML message asynchronously. The message is queued until a daemon thread send all awaiting messages
76 *
77 * @param strRecipientsTo
78 * The list of the main recipients email.Every recipient must be separated by the mail separator define in config.properties
79 * @param strSenderName
80 * The sender name.
81 * @param strSenderEmail
82 * The sender email address.
83 * @param strSubject
84 * The message subject.
85 * @param strMessage
86 * The message.
87 */
88 public static void sendMailHtml( String strRecipientsTo, String strSenderName, String strSenderEmail, String strSubject, String strMessage )
89 {
90 sendMailHtml( strRecipientsTo, null, null, strSenderName, strSenderEmail, strSubject, strMessage );
91 }
92
93 /**
94 * Send a HTML message asynchronously. The message is queued until a daemon thread send all awaiting messages
95 *
96 * @param strRecipientsTo
97 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
98 * @param strRecipientsCc
99 * The recipients list of the carbon copies .
100 * @param strRecipientsBcc
101 * The recipients list of the blind carbon copies .
102 * @param strSenderName
103 * The sender name.
104 * @param strSenderEmail
105 * The sender email address.
106 * @param strSubject
107 * The message subject.
108 * @param strMessage
109 * The message.
110 */
111 public static void sendMailHtml( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
112 String strSubject, String strMessage )
113 {
114 sendMailHtml( strRecipientsTo, strRecipientsCc, strRecipientsBcc, strSenderName, strSenderEmail, strSubject, strMessage, false );
115 }
116
117 /**
118 * Send a HTML message asynchronously. The message is queued until a daemon thread send all awaiting messages
119 *
120 * @param strRecipientsTo
121 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
122 * @param strRecipientsCc
123 * The recipients list of the carbon copies .
124 * @param strRecipientsBcc
125 * The recipients list of the blind carbon copies .
126 * @param strSenderName
127 * The sender name.
128 * @param strSenderEmail
129 * The sender email address.
130 * @param strSubject
131 * The message subject.
132 * @param strMessage
133 * The message.
134 * @param bUniqueRecipientTo
135 * true if the mail must be send unitarily for each recipient
136 */
137 public static void sendMailHtml( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
138 String strSubject, String strMessage, boolean bUniqueRecipientTo )
139 {
140 MailItem/service/mail/MailItem.html#MailItem">MailItem item = new MailItem( );
141 item.setRecipientsTo( strRecipientsTo );
142 item.setRecipientsCc( strRecipientsCc );
143 item.setRecipientsBcc( strRecipientsBcc );
144 item.setSenderName( strSenderName );
145 item.setSenderEmail( strSenderEmail );
146 item.setSubject( strSubject );
147 item.setMessage( strMessage );
148 item.setFormat( MailItem.FORMAT_HTML );
149 item.setUniqueRecipientTo( bUniqueRecipientTo );
150
151 enqueue( item );
152 }
153
154 /**
155 * Send a HTML message asynchronously with the attachments associated to the message . The message is queued until a daemon thread send all awaiting
156 * messages
157 *
158 * @param strRecipientsTo
159 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
160 * @param strSenderName
161 * The sender name.
162 * @param strSenderEmail
163 * The sender email address.
164 * @param strSubject
165 * The message subject.
166 * @param strMessage
167 * The message.
168 * @param urlsAttachement
169 * The List of UrlAttachement Object, containing the URL of attachments associated with their content-location.
170 */
171 public static void sendMailMultipartHtml( String strRecipientsTo, String strSenderName, String strSenderEmail, String strSubject, String strMessage,
172 List<UrlAttachment> urlsAttachement )
173 {
174 sendMailMultipartHtml( strRecipientsTo, null, null, strSenderName, strSenderEmail, strSubject, strMessage, urlsAttachement, null );
175 }
176
177 /**
178 * Send a HTML message asynchronously with the attachments associated to the message and attached files . The message is queued until a daemon thread send
179 * all awaiting messages
180 *
181 * @param strRecipientsTo
182 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
183 * @param strRecipientsCc
184 * The recipients list of the carbon copies .
185 * @param strRecipientsBcc
186 * The recipients list of the blind carbon copies .
187 * @param strSenderName
188 * The sender name.
189 * @param strSenderEmail
190 * The sender email address.
191 * @param strSubject
192 * The message subject.
193 * @param strMessage
194 * The message.
195 * @param urlsAttachement
196 * The List of UrlAttachement Object, containing the URL of attachments associated with their content-location
197 * @param filesAttachement
198 * The list of attached files.
199 */
200 public static void sendMailMultipartHtml( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName,
201 String strSenderEmail, String strSubject, String strMessage, List<UrlAttachment> urlsAttachement, List<FileAttachment> filesAttachement )
202 {
203 sendMailMultipartHtml( strRecipientsTo, strRecipientsCc, strRecipientsBcc, strSenderName, strSenderEmail, strSubject, strMessage, urlsAttachement,
204 filesAttachement, false );
205 }
206
207 /**
208 * Send a HTML message asynchronously with the attachments associated to the message and attached files . The message is queued until a daemon thread send
209 * all awaiting messages
210 *
211 * @param strRecipientsTo
212 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
213 * @param strRecipientsCc
214 * The recipients list of the carbon copies .
215 * @param strRecipientsBcc
216 * The recipients list of the blind carbon copies .
217 * @param strSenderName
218 * The sender name.
219 * @param strSenderEmail
220 * The sender email address.
221 * @param strSubject
222 * The message subject.
223 * @param strMessage
224 * The message.
225 * @param urlsAttachement
226 * The List of UrlAttachement Object, containing the URL of attachments associated with their content-location
227 * @param filesAttachement
228 * The list of attached files.
229 * @param bUniqueRecipientTo
230 * true if the mail must be send unitarily for each recipient
231 */
232 public static void sendMailMultipartHtml( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName,
233 String strSenderEmail, String strSubject, String strMessage, List<UrlAttachment> urlsAttachement, List<FileAttachment> filesAttachement,
234 boolean bUniqueRecipientTo )
235 {
236 MailItem/service/mail/MailItem.html#MailItem">MailItem item = new MailItem( );
237 item.setRecipientsTo( strRecipientsTo );
238 item.setRecipientsCc( strRecipientsCc );
239 item.setRecipientsBcc( strRecipientsBcc );
240 item.setSenderName( strSenderName );
241 item.setSenderEmail( strSenderEmail );
242 item.setSubject( strSubject );
243 item.setMessage( strMessage );
244 item.setFormat( MailItem.FORMAT_MULTIPART_HTML );
245 item.setUrlsAttachement( urlsAttachement );
246 item.setFilesAttachement( filesAttachement );
247 item.setUniqueRecipientTo( bUniqueRecipientTo );
248
249 enqueue( item );
250 }
251
252 /**
253 * Send a calendar message asynchronously. The message is queued until a daemon thread send all awaiting messages
254 *
255 * @param strRecipientsTo
256 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
257 * @param strSenderName
258 * The sender name.
259 * @param strSenderEmail
260 * The sender email address.
261 * @param strSubject
262 * The message subject.
263 * @param strMessage
264 * The message.
265 * @param strCalendarMessage
266 * The calendar message
267 * @param bCreateEvent
268 * True to create the calendar event, false to remove it
269 */
270 public static void sendMailCalendar( String strRecipientsTo, String strSenderName, String strSenderEmail, String strSubject, String strMessage,
271 String strCalendarMessage, boolean bCreateEvent )
272 {
273 sendMailCalendar( strRecipientsTo, null, null, strSenderName, strSenderEmail, strSubject, strMessage, strCalendarMessage, bCreateEvent );
274 }
275
276 /**
277 * Send a calendar message asynchronously. The message is queued until a daemon thread send all awaiting messages
278 *
279 * @param strRecipientsTo
280 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
281 * @param strRecipientsCc
282 * The recipients list of the carbon copies .
283 * @param strRecipientsBcc
284 * The recipients list of the blind carbon copies .
285 * @param strSenderName
286 * The sender name.
287 * @param strSenderEmail
288 * The sender email address.
289 * @param strSubject
290 * The message subject.
291 * @param strMessage
292 * The message.
293 * @param strCalendarMessage
294 * The calendar message
295 * @param bCreateEvent
296 * True to create the calendar event, false to remove it
297 */
298 public static void sendMailCalendar( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
299 String strSubject, String strMessage, String strCalendarMessage, boolean bCreateEvent )
300 {
301 sendMailCalendar( strRecipientsTo, strRecipientsCc, strRecipientsBcc, strSenderName, strSenderEmail, strSubject, strMessage, strCalendarMessage,
302 bCreateEvent, false );
303 }
304
305 /**
306 * Send a calendar message asynchronously. The message is queued until a daemon thread send all awaiting messages
307 *
308 * @param strRecipientsTo
309 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
310 * @param strRecipientsCc
311 * The recipients list of the carbon copies .
312 * @param strRecipientsBcc
313 * The recipients list of the blind carbon copies .
314 * @param strSenderName
315 * The sender name.
316 * @param strSenderEmail
317 * The sender email address.
318 * @param strSubject
319 * The message subject.
320 * @param strMessage
321 * The message.
322 * @param strCalendarMessage
323 * The calendar message
324 * @param bCreateEvent
325 * True to create the calendar event, false to remove it
326 * @param bUniqueRecipientTo
327 * true if the mail must be send unitarily for each recipient
328 */
329 public static void sendMailCalendar( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
330 String strSubject, String strMessage, String strCalendarMessage, boolean bCreateEvent, boolean bUniqueRecipientTo )
331 {
332 MailItem/service/mail/MailItem.html#MailItem">MailItem item = new MailItem( );
333 item.setRecipientsTo( strRecipientsTo );
334 item.setRecipientsCc( strRecipientsCc );
335 item.setRecipientsBcc( strRecipientsBcc );
336 item.setSenderName( strSenderName );
337 item.setSenderEmail( strSenderEmail );
338 item.setSubject( strSubject );
339 item.setMessage( strMessage );
340 item.setCalendarMessage( strCalendarMessage );
341 item.setCreateEvent( bCreateEvent );
342 item.setFormat( MailItem.FORMAT_CALENDAR );
343 item.setUniqueRecipientTo( bUniqueRecipientTo );
344
345 enqueue( item );
346 }
347
348 /**
349 * Send a text message asynchronously. The message is queued until a daemon thread send all awaiting messages
350 *
351 * @param strRecipientsTo
352 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
353 * @param strSenderName
354 * The sender name.
355 * @param strSenderEmail
356 * The sender email address.
357 * @param strSubject
358 * The message subject.
359 * @param strMessage
360 * The message.
361 */
362 public static void sendMailText( String strRecipientsTo, String strSenderName, String strSenderEmail, String strSubject, String strMessage )
363 {
364 sendMailText( strRecipientsTo, null, null, strSenderName, strSenderEmail, strSubject, strMessage );
365 }
366
367 /**
368 * Send a text message asynchronously. The message is queued until a daemon thread send all awaiting messages
369 *
370 * @param strRecipientsTo
371 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
372 * @param strRecipientsCc
373 * The recipients list of the carbon copies .
374 * @param strRecipientsBcc
375 * The recipients list of the blind carbon copies .
376 * @param strSenderName
377 * The sender name.
378 * @param strSenderEmail
379 * The sender email address.
380 * @param strSubject
381 * The message subject.
382 * @param strMessage
383 * The message.
384 */
385 public static void sendMailText( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
386 String strSubject, String strMessage )
387 {
388 sendMailText( strRecipientsTo, strRecipientsCc, strRecipientsBcc, strSenderName, strSenderEmail, strSubject, strMessage, false );
389 }
390
391 /**
392 * Send a text message asynchronously. The message is queued until a daemon thread send all awaiting messages
393 *
394 * @param strRecipientsTo
395 * The list of the main recipients email. Every recipient must be separated by the mail separator defined in config.properties
396 * @param strRecipientsCc
397 * The recipients list of the carbon copies .
398 * @param strRecipientsBcc
399 * The recipients list of the blind carbon copies .
400 * @param strSenderName
401 * The sender name.
402 * @param strSenderEmail
403 * The sender email address.
404 * @param strSubject
405 * The message subject.
406 * @param strMessage
407 * The message.
408 * @param bUniqueRecipientTo
409 * true if the mail must be send unitarily for each recipient
410 */
411 public static void sendMailText( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName, String strSenderEmail,
412 String strSubject, String strMessage, boolean bUniqueRecipientTo )
413 {
414 MailItem/service/mail/MailItem.html#MailItem">MailItem item = new MailItem( );
415 item.setRecipientsTo( strRecipientsTo );
416 item.setRecipientsCc( strRecipientsCc );
417 item.setRecipientsBcc( strRecipientsBcc );
418 item.setSenderName( strSenderName );
419 item.setSenderEmail( strSenderEmail );
420 item.setSubject( strSubject );
421 item.setMessage( strMessage );
422 item.setFormat( MailItem.FORMAT_TEXT );
423 item.setUniqueRecipientTo( bUniqueRecipientTo );
424
425 enqueue( item );
426 }
427
428 /**
429 * Send a text message asynchronously with attached files. The message is queued until a daemon thread send all awaiting messages
430 *
431 * @param strRecipientsTo
432 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
433 * @param strSenderName
434 * The sender name.
435 * @param strSenderEmail
436 * The sender email address.
437 * @param strSubject
438 * The message subject.
439 * @param strMessage
440 * The message.
441 * @param filesAttachement
442 * The list of attached files.
443 */
444 public static void sendMailMultipartText( String strRecipientsTo, String strSenderName, String strSenderEmail, String strSubject, String strMessage,
445 List<FileAttachment> filesAttachement )
446 {
447 sendMailMultipartText( strRecipientsTo, null, null, strSenderName, strSenderEmail, strSubject, strMessage, filesAttachement );
448 }
449
450 /**
451 * Send a text message asynchronously with attached files. The message is queued until a daemon thread send all awaiting messages
452 *
453 * @param strRecipientsTo
454 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
455 * @param strRecipientsCc
456 * The recipients list of the carbon copies .
457 * @param strRecipientsBcc
458 * The recipients list of the blind carbon copies .
459 * @param strSenderName
460 * The sender name.
461 * @param strSenderEmail
462 * The sender email address.
463 * @param strSubject
464 * The message subject.
465 * @param strMessage
466 * The message.
467 * @param filesAttachement
468 * The list of attached files.
469 */
470 public static void sendMailMultipartText( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName,
471 String strSenderEmail, String strSubject, String strMessage, List<FileAttachment> filesAttachement )
472 {
473 sendMailMultipartText( strRecipientsTo, strRecipientsCc, strRecipientsBcc, strSenderName, strSenderEmail, strSubject, strMessage, filesAttachement,
474 false );
475 }
476
477 /**
478 * Send a text message asynchronously with attached files. The message is queued until a daemon thread send all awaiting messages
479 *
480 * @param strRecipientsTo
481 * The list of the main recipients email.Every recipient must be separated by the mail separator defined in config.properties
482 * @param strRecipientsCc
483 * The recipients list of the carbon copies .
484 * @param strRecipientsBcc
485 * The recipients list of the blind carbon copies .
486 * @param strSenderName
487 * The sender name.
488 * @param strSenderEmail
489 * The sender email address.
490 * @param strSubject
491 * The message subject.
492 * @param strMessage
493 * The message.
494 * @param filesAttachement
495 * The list of attached files.
496 * @param bUniqueRecipientTo
497 * true if the mail must be send unitarily for each recipient
498 */
499 public static void sendMailMultipartText( String strRecipientsTo, String strRecipientsCc, String strRecipientsBcc, String strSenderName,
500 String strSenderEmail, String strSubject, String strMessage, List<FileAttachment> filesAttachement, boolean bUniqueRecipientTo )
501 {
502 MailItem/service/mail/MailItem.html#MailItem">MailItem item = new MailItem( );
503 item.setRecipientsTo( strRecipientsTo );
504 item.setRecipientsCc( strRecipientsCc );
505 item.setRecipientsBcc( strRecipientsBcc );
506 item.setSenderName( strSenderName );
507 item.setSenderEmail( strSenderEmail );
508 item.setSubject( strSubject );
509 item.setMessage( strMessage );
510 item.setFormat( MailItem.FORMAT_MULTIPART_TEXT );
511 item.setFilesAttachement( filesAttachement );
512 item.setUniqueRecipientTo( bUniqueRecipientTo );
513
514 enqueue( item );
515 }
516
517 /**
518 * Shutdown the service
519 */
520 public static void shutdown( )
521 {
522 // if there is mails that have not been sent call the daemon to flush the list
523 Daemon daemon = AppDaemonService.getDaemon( MailSenderDaemon.DAEMON_ID );
524
525 if ( daemon != null )
526 {
527 daemon.run( );
528 }
529 }
530
531 /**
532 * Returns a no reply email address defined in config.properties
533 *
534 * @return A no reply email
535 */
536 public static String getNoReplyEmail( )
537 {
538 String strDefault = AppPropertiesService.getProperty( PROPERTY_MAIL_NOREPLY_EMAIL );
539
540 return DatastoreService.getDataValue( KEY_NO_REPLY_EMAIL, strDefault );
541 }
542
543 /**
544 * Returns the mail queue
545 *
546 * @return the mail queue
547 */
548 public static IMailQueue getQueue( )
549 {
550 return SpringContextService.getBean( BEAN_MAIL_QUEUE );
551 }
552
553 /**
554 * Extract a collection of elements to be attached to a mail from an HTML string.
555 *
556 * The collection contains the Url used for created DataHandler for each url associated with an HTML tag img, script or link. Those urls must start with the
557 * url strBaseUrl.
558 *
559 * @param strHtml
560 * The HTML code.
561 * @param strBaseUrl
562 * The base url, can be null in order to extract all urls.
563 * @param useAbsoluteUrl
564 * Determine if we use absolute or relative url for attachement content-location
565 * @return a collection of UrlAttachment Object for created DataHandler associated with attachment urls.
566 */
567 public static List<UrlAttachment> getUrlAttachmentList( String strHtml, String strBaseUrl, boolean useAbsoluteUrl )
568 {
569 return MailUtil.getUrlAttachmentList( strHtml, strBaseUrl, useAbsoluteUrl );
570 }
571
572 /**
573 * Return a String that contains a list of recipients separated with mail separator
574 *
575 * @param listRecipients
576 * a list of string recipients
577 * @return a String that contains a list of recipients separated with mail separator
578 */
579 public static String getStrRecipients( List<String> listRecipients )
580 {
581 return MailUtil.getStrRecipients( listRecipients );
582 }
583
584 /**
585 * Get a string that contains an html link to the site back office or front office.
586 *
587 * @param strBaseUrl
588 * The base url of the site
589 * @param linkToFrontOffice
590 * True if the link should be directed to the front office, false if it should be directed to the back office.
591 * @return A string containing an html link.
592 */
593 public static String getSiteLink( String strBaseUrl, boolean linkToFrontOffice )
594 {
595 StringBuilder sb = new StringBuilder( );
596 String strSiteName = PortalService.getSiteName( );
597
598 if ( strSiteName != null )
599 {
600 sb.append( "<a title=\"" );
601 sb.append( strSiteName );
602 sb.append( "\" href=\"" );
603 sb.append( strBaseUrl );
604
605 String strUrl;
606
607 if ( linkToFrontOffice )
608 {
609 strUrl = AppPathService.getPortalUrl( );
610 }
611 else
612 {
613 strUrl = AppPathService.getAdminMenuUrl( );
614 }
615
616 if ( strUrl != null )
617 {
618 sb.append( strUrl );
619 }
620
621 sb.append( "\" >" );
622 sb.append( strSiteName );
623 sb.append( "</a>" );
624 }
625
626 return sb.toString( );
627 }
628 }