From df762d4ce6cea70c7142398ec216e063c29e233c Mon Sep 17 00:00:00 2001 From: DOHA Date: Fri, 28 Nov 2014 19:24:01 +0200 Subject: [PATCH] change input apache-fop --- .../java/ApacheFOPConvertHTMLTest.java | 18 +- apache-fop/src/test/resources/hello.fo | 31 - apache-fop/src/test/resources/hello.html | 11 - apache-fop/src/test/resources/hello.pdf | Bin 5445 -> 0 bytes apache-fop/src/test/resources/hello.xml | 10 - apache-fop/src/test/resources/hello1.pdf | Bin 5437 -> 0 bytes apache-fop/src/test/resources/hello2.pdf | Bin 6398 -> 0 bytes apache-fop/src/test/resources/input.fo | 479 ++++++++++++++ apache-fop/src/test/resources/input.html | 598 ++++++++++++++++++ apache-fop/src/test/resources/input.xml | 544 ++++++++++++++++ .../src/test/resources/output_herold.pdf | Bin 0 -> 34733 bytes .../src/test/resources/output_html2fo.pdf | Bin 0 -> 14642 bytes .../src/test/resources/output_jtidy.pdf | Bin 0 -> 25904 bytes 13 files changed, 1630 insertions(+), 61 deletions(-) delete mode 100644 apache-fop/src/test/resources/hello.fo delete mode 100644 apache-fop/src/test/resources/hello.html delete mode 100644 apache-fop/src/test/resources/hello.pdf delete mode 100644 apache-fop/src/test/resources/hello.xml delete mode 100644 apache-fop/src/test/resources/hello1.pdf delete mode 100644 apache-fop/src/test/resources/hello2.pdf create mode 100644 apache-fop/src/test/resources/input.fo create mode 100644 apache-fop/src/test/resources/input.html create mode 100644 apache-fop/src/test/resources/input.xml create mode 100644 apache-fop/src/test/resources/output_herold.pdf create mode 100644 apache-fop/src/test/resources/output_html2fo.pdf create mode 100644 apache-fop/src/test/resources/output_jtidy.pdf diff --git a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLTest.java b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLTest.java index 533825f39d..bde6868b39 100644 --- a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLTest.java +++ b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPConvertHTMLTest.java @@ -26,20 +26,20 @@ import org.w3c.dom.Document; import org.w3c.tidy.Tidy; public class ApacheFOPConvertHTMLTest { - private String inputFile = "src/test/resources/hello.html"; + private String inputFile = "src/test/resources/input.html"; private String style = "src/test/resources/xhtml2fo.xsl"; private String style1 = "src/test/resources/docbook-xsl/fo/docbook.xsl"; - private String output = "src/test/resources/hello.pdf"; - private String output1 = "src/test/resources/hello1.pdf"; - private String output2 = "src/test/resources/hello2.pdf"; - private String foFile = "src/test/resources/hello.fo"; - private String xmlFile = "src/test/resources/hello.xml"; + private String output_jtidy = "src/test/resources/output_jtidy.pdf"; + private String output_html2fo = "src/test/resources/output_html2fo.pdf"; + private String output_herold = "src/test/resources/output_herold.pdf"; + private String foFile = "src/test/resources/input.fo"; + private String xmlFile = "src/test/resources/input.xml"; @Test public void whenTransformHTMLToPDFUsingJTidy_thenCorrect() throws Exception { final Document xhtml = fromHTMLToXHTML(); final Document fo = fromXHTMLToFO(xhtml); - fromFODocumentToPDF(fo, output); + fromFODocumentToPDF(fo, output_jtidy); } @Test @@ -51,7 +51,7 @@ public class ApacheFOPConvertHTMLTest { public void whenTransformFromHeroldToPDF_thenCorrect() throws Exception { fromHTMLTOXMLUsingHerold(); final Document fo = fromXMLFileToFO(); - fromFODocumentToPDF(fo, output2); + fromFODocumentToPDF(fo, output_herold); } private Document fromHTMLToXHTML() throws FileNotFoundException { @@ -92,7 +92,7 @@ public class ApacheFOPConvertHTMLTest { private void fromFOFileToPDF() throws Exception { final FopFactory fopFactory = FopFactory.newInstance(); - final OutputStream outStream = new BufferedOutputStream(new FileOutputStream(new File(output1))); + final OutputStream outStream = new BufferedOutputStream(new FileOutputStream(new File(output_html2fo))); final Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, outStream); final TransformerFactory factory = TransformerFactory.newInstance(); diff --git a/apache-fop/src/test/resources/hello.fo b/apache-fop/src/test/resources/hello.fo deleted file mode 100644 index 860478de9e..0000000000 --- a/apache-fop/src/test/resources/hello.fo +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - -Hello World - - - - - ... the footer should be inserted here ... - - - - - My name is John Doe - Hello World! - - - - diff --git a/apache-fop/src/test/resources/hello.html b/apache-fop/src/test/resources/hello.html deleted file mode 100644 index 1b715a25ae..0000000000 --- a/apache-fop/src/test/resources/hello.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Hello World - - -

My name is John Doe

-

Hello World!

- - - diff --git a/apache-fop/src/test/resources/hello.pdf b/apache-fop/src/test/resources/hello.pdf deleted file mode 100644 index 4f5f1267971af1515c2487616efa8692337be99a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5445 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 diff --git a/apache-fop/src/test/resources/hello.xml b/apache-fop/src/test/resources/hello.xml deleted file mode 100644 index 9f7e7ad707..0000000000 --- a/apache-fop/src/test/resources/hello.xml +++ /dev/null @@ -1,10 +0,0 @@ - -
- - Hello World - -
- My name is John Doe - Hello World! -
-
diff --git a/apache-fop/src/test/resources/hello1.pdf b/apache-fop/src/test/resources/hello1.pdf deleted file mode 100644 index 946263df89302371fc2d614d0cb554ead251c42f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5437 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 M5OQ)xR>p|`099;h)&Kwi diff --git a/apache-fop/src/test/resources/hello2.pdf b/apache-fop/src/test/resources/hello2.pdf deleted file mode 100644 index 65d1caa56d71f5e7cb07cf4929089c5767151028..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6398 zcmb_hc|4Te+ozBwiIgSOZ73vWGh^SzlC0C1>|&T3lQE;25g}W&hZb6pM~aZ8XtC9! zLS-xR2%$WoBqgH7^4|A^>Z$klKEKcBoquM|bzSE=-*cUFuJgUltS6aSsADuV5$p5v zFBc#*0W`q!-ht59N1#mEAeGBv11cs#R3Cp3u&^fqjv$*uXE6bc21XTuBC%PtU?1>5 zned!IJW{x z$7bhpK2A*ZbalO3HyClQ_v_cH!Re!uyG8jfMB5air5yQeqO(mgI?uBWG zU#6WwJ#c)g+;3yuL_!}S7duB9>jbE12k$E|`r1&O6T6!zZr^l_-88UtH)14tY@fcW zXVU@XsXl#vBhRMxeny(=YQ)Pa3vsoVoR>O(SRyLonr%;%q^E7^yDxk_ZICZo@nU54 z1JB|(PhhK3P18e-Rm;e&N+O&>!Mi;*@ioHF2mL;*U+F8oKebwXOnP_|m9Uv2{HIM* zhWXYfDGI2oC#nvgkZ7&0ojFXLZm+qmYR>X|W&UaKoXPl%5hJSqR2v^(fYtU?^{A)E z^Z8!6FNv=0He%F<^N~^}3;zBf_4(22vEv(#(JDGS2L=bbgg?2e(4J* z9C{mxT5)%O#+_rnR?>&cca~sFE_hoo3H$5)(6jpYX0_iJHB;rQ?Oz=JjNENj8u3uz zJY{!tWtXV@tLyOO|N=G;_Fu>x|<9QYVR^r+N-vDu<^2L;dBl}+9me*Qg!DE2vCL#AEp*Ula57pS>HLLcLiv@@I+WrvLjP-ql5Z!ARB$6gH z_~-f{!DoDv(IQ8pbG%koE@~AMpekU^;x!fa+!weSD+fqwN_~ir%@kGm$-T!%MQ(~a8iyRUL?A63f%TSxEzzaO zZp(G-<;N5x?z^oLGB?MNY);B<=4Eu{BmGJR`4T7 ze3@3E z9PP^<-54rSsb;vU`L0x7{rjVzm{Z`CaOBxPtk8-#C6G&B$F;>-5!GI7dSUoN@R+5h zQn}dOgNyn$daoZ98`a-=;8Tj3g3Ixz7X*%xRD<4+&Bl;B|o=R4xyx*9;k+C}A zAtHHAa@NKhlx#|F-b7wz9hl`B8+QbU&XC@8>T#7 z`!V+m=cC}4u8;OZM$}7~O6fC4tSnGIEyi`gpT;{#EVLIo(BD@Yyjfe^_&!Fw`T%&+ zyR`dKoqvmSulBvrGdmBDdPr0*tygNKN<6rb6dCDG4d`6nx650xvaXwU{=Ie3O9EYlpAhFR%V0dE(x< z_W;K2v?+m`>XnKoq!XGMM+5&3ywN$)5n0M9tuGBIt?;wIlHqr|_Ey)7(-P|?g{DXE zesp>15?SKdG<5c3#Pv4D%y4%JOOGsXD%Y+zp&c2--nCNin(k3OSv_T)cHLg@$IjHt zwwK2={H0i@k~Qkwd&+gotlU>r%=FcA>x1;369{z-(I~sW)2Jb)AqnAC6$}j@X3H}J z4!Blmtz4o|;^uP}=k%L5G;c*ME8-UI-QHaJwz8(@ZBJ20;c)-3gW-Y1+x7>Wqfs?l z4{vl%W@co1=6N3Oy>L5gmOSgg|0?>A=mP#5i-H%I#Wcp87)u*e&uEu5L%51j zVx8rl$uHX9u&hIp9d+wL#N(+ZlGqyQa`kbO(=N_-PbXiTeqa3l?E5^^A=1#kQ$?I`^jq72Y)H-{L-AkplTZqavO7fOr%}e_=gR+k=J`r$2 zxr{MlH{x(knWe`{d)I^-Ow7KSb28`1#qu1cwY>e9Wxi#qSg~MR?BHwu*TJuga75e$ z81eAP1BOIj+?G38$U`coPf{TKSJeEU=f!itkR$!~jNJCL1h zPJZ2A)BSn<)(`jJh-bWc+Vph$)#?6(d-75neVv}XRT@4ya&nuH)2?kYPJy{$p6QNz zvT7@`3mkKH`MiDOQR(3n583T?8+t0M5@oYwzuxlQ z)BWs18>Mrcn&F?ph`Kk^<2yJouvmA=L)Yh$^!iWLaeJL^jegF~PwsH-wP2|k2#9W3 zVepyox+``6UeQT8syuzV>19Rdt{vf9mOR<|6w`6$FV#1P-mKBredlj~!!6|NB%$}6 zo^R0Qft#J9S0&;k%#{t(-P!CR%0%wL*4&)^P*xsEKWM9>bL{xw%=rS8h))saw~%Buz+HL(g-hypMe`JgDjU!ThyO_MT&9 zyGFvEf3(&b=#{RN2_Bm$h;zHzRoUeef_URHIPbb+E_2+VnbN3g;6=i##s(c(#GOPY3 zu(#rLNLK6TcURu2897J1nvotKol$;~V}G0fLEm^E%r)nz;vDNF%&|^4)D}}yB9#Nu z03O%$n5U&$KhaXk?jR6A*@9dujmo9Mgw!F}o68eA+uHodSYgNHAE>FmQ7FWw13)g| z4f@fU2FQ06w~+vyW`K0U+oElQOhJFTWf&WD2(zR3ga!EM(2z!k2!wv9ZfIaoAT%Hl z8pvRBbVCi0RERHwy3lV}j6xbB^w~6DU9y?QoC(x2K>BmJLAoeZNJxlA2u_2=_CsNG zbaYT?EDDQNhb+`NyO~^Ss5+CQIPbv>qg=N5SHBaRgmn?VyWB^E&WM5c((>!4K%?BJzV|Zaxr* zg$Xe^8oWSi_^<+DbkJmQzfJ~H=+0L=hQF_e^x@v$jS%|0rkTZD$z%u>S#u`eW&Z_c zX!(DSIg}a%VMhOo8S0@sSHn?QEXMx|`=55uW`lapZ*I77`%mHj#r9iw-oW6$>HLG` zZ^OY`RcKd3tBG1T1yEz*AVEji0LkVZlV1-u2EA}_0o1o+YvKd%LJ)?d)g6<`;zHi$6Mzk!8Gt~MK_4#Q z24Jvgo;OVm9OMm)!DwjV2>=0y-;Pz*u5q!xySsC=Z^5Sy%N=*-#4s%Rv0z(R=G5@ zXhMV&lH>Sy`r5?1#C-$B881Gy?|EOkJaWb`K`)~0!I?-+mSp3b6)|!2odyPjs-+&D zsn@thR)_KF>mG~>s@tVb9+sJ1)>|UuqczFYvKR^+ITcx~dhnVaI6%VYmY$Ws)oC+E zTG`h+TiV|?x3v{Fju6QiiMrAK&xs#n^3;xyt;Tnb_kHNvv85{@tfE_glh%sK#P-wO zuFaIoALYu+KH90qAo)BvBoL|Woqm|xEJ`)LZQw3#>El`h<-9yey{-E9MDqCFOCgFssI&`sS4}n zFLQvGmglW)sNNuh1HiNYY4N__EU+anwEFtc4;+A*yC*I10EX8MhR1WqX8AZkq}B}u zy(dFARUib0IQMM|fdxak-}Wbq!D2f=3>XApmu8?ZHJHIAZzcjfqReym!^(VL80aZD zI1ma8n%9p6s9;oKV_tmb0~E>zeGzB?i$}7=wAPa46BWWLin>CRvWta{~^=V(ShRmvmIVb2RhR~%W&H0 zU+i$&+P`4d)FD6z@#nspXzVZeG%**k8uQV>N#xBm63ZK!3-=WmD-4 zkPZK>0PQF;i^T;Nvd9*uFAMmVKp@=~h>Yn&@xu~{L>)78a|;s-0>M;E+YAc6jt + + + + + + + + + + + + + +Registration &#8211; Activate a New Account by Email | Technical Articles + + + + + ... the footer should be inserted here ... + + + + + + + + + + + + + + + Navigation + + Technical Articles + + + Home + + + + - +Home + + + + - + + series + + Return to Content + + + + + + + + + + + + + + Registration &#8211; Activate a New Account by Email + + Elena October 23, 2014 Spring Security + 1. Overview +This article continues our ongoing Registration with Spring Security by finishing the missing piece of the registration process &#8211; verifying the email to confirm the user registration. +Confirm Registration&#8221; email sent after successful registration to verify his email address and activate his account. The user does this by clicking a unique account activation link sent to him as part of the email message. +Following this logic, a newly registered user will not be able to log in until email/registration verification is completed. +2. A Verification Token + + Entity to Our Modelassociated to a . So, we need a one-to-one unidirectional association between the and the . Entity for the user and persisting it.valueas a parameter. +We will make use of a simple verification token as the key artifact through which a user is verified. +2.1. Adding a VerificationToken entity must meet the following criteria: +The VerificationToken + +- +There will be one VerificationToken User VerificationTokenUser +- +It will be created after the user registration data is persisted. +- +It will expire in 24 hours following initial registration. +- +Its value should be unique and randomly generated. + entity like the one in Example 2.1.: +Requirements 2 and 3 are part of the registration logic. The other two are implemented in a simple VerificationToken +Example 2.1. +@Entity +@Table +public class VerificationToken { + private static final int EXPIRATION = 60 * 24; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(name = "token") + private String token; + + @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER) + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "expiry_date") + private Date expiryDate; + + public VerificationToken() { + super(); + } + public VerificationToken(String token, User user) { + super(); + this.token = token; + this.user = user; + this.expiryDate = calculateExpiryDate(EXPIRATION); + this.verified = false; + } + private Date calculateExpiryDate(int expiryTimeInMinutes) { + Calendar cal = Calendar.getInstance(); + cal.setTime(new Timestamp(cal.getTime().getTime())); + cal.add(Calendar.MINUTE, expiryTimeInMinutes); + return new Date(cal.getTime().getTime()); + } + + // standard getters and setters +} +2.2. Add an Enabled Flag to the User entity for now: +We will set the value of this flag depending on the result of the registration confirmation use case. Lets jus add the following field to our User +@Column(name = "enabled") +private boolean enabled; +3. The Account Registration Phase +Lets add two additional pieces of business logic to the user registration use case: + +- +Generating a VerificationToken +- +Sending the account confirmation email message which includes a confirmation link with the VerificationToken&#8217;s + +3.1. Using Spring Event Handling to Create the Token and Send the Verification Email to trigger the execution of these tasks. This is as simple as injecting anr in the controller, and then using it to publish the registration completion. Example 3.1. shows this simple logic: +These two additional pieces of logic should not be performed by the controller directly because they are &#8220;collateral&#8221; back-end tasks. The controller will publish a Spring ApplicationEvent ApplicationEventPublishe +Example 3.1. +@Autowired +ApplicationEventPublisher +@RequestMapping(value = "/user/registration", method = RequestMethod.POST) +public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto accountDto, + BindingResult result, WebRequest request, Errors errors) { + User registered = new User(); + String appUrl = request.getContextPath(); + if (result.hasErrors()) { + return new ModelAndView("registration", "user", accountDto); + } + registered = createUserAccount(accountDto); + if (registered == null) { + result.rejectValue("email", "message.regError"); + } + eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, + request.getLocale(), appUrl)); + return new ModelAndView("successRegister", "user", accountDto); +} +3.2. Spring Event Handler Implementation to start the that will handle the verification token creation and confirmation email sending. So it needs to have access to the implementation of the following interfaces: +The controller is using an ApplicationEventPublisherRegistrationListener + +- +An AplicationEvent representing the completion of the user registration. +- +An ApplicationListener. For and access. and its implementation for new CRUD operations needed. + , and the shown Examples 3.2.1 &#8211; 3.2.2. +The beans we will create are the OnRegistrationCompleteEventRegistrationListener +OnRegistrationCompleteEvent Example 3.2.1. +@SuppressWarnings("serial") +public class OnRegistrationCompleteEvent extends ApplicationEvent { + private final String appUrl; + private final Locale locale; + private final User user; + + public OnRegistrationCompleteEvent(User user, Locale locale, String appUrl) { + super(user); + this.user = user; + this.locale = locale; + this.appUrl = appUrl; + } + + // standard getters and setters +} +OnRegistrationCompleteEvent Example 3.2.2. - The RegistrationListener method will receive the , extract all the necessary information from it, create the verification token, persist it, and then send it as a parameter in the &#8220;Confirm Registration&#8221; link sent to the user. +@Component +public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> { + @Autowired + private IUserService service; + + @Autowired + private MessageSource messages; + + @Autowired + private JavaMailSender mailSender; + + @Override + public void onApplicationEvent(OnRegistrationCompleteEvent event) { + this.confirmRegistration(event); + } + + private void confirmRegistration(OnRegistrationCompleteEvent event) { + User user = event.getUser(); + String token = UUID.randomUUID().toString(); + service.addVerificationToken(user, token); + String recipientAddress = user.getEmail(); + String subject = "Registration Confirmation"; + String confirmationUrl = event.getAppUrl() + "/regitrationConfirm.html?token=" + token; + String message = messages.getMessage("message.regSucc", null, event.getLocale()); + SimpleMailMessage email = new SimpleMailMessage(); + email.setTo(recipientAddress); + email.setSubject(subject); + email.setText(message + " \r\n" + "http://localhost:8080" + confirmationUrl); + mailSender.send(email); + } +} +Here, the confirmRegistrationOnRegistrationCompleteEventUser +3.3. Processing the Verification Token Parameter +When the user receives the &#8220;Confirm Registration&#8221; email, he will click on the attached link and fire a GET request. The controller will extract the value of the token parameter in the GET request and will use it to verify the user. Lets see this logic in Example 3.3.1. +Example 3.3.1. &#8211; RegistrationController or if the does not exist, the controller will return a page with the corresponding error message (See Example 3.3.2.). +private IUserService service; + +@Autowired +public RegistrationController(IUserService service){ + this.service = service +} +@RequestMapping(value = "/regitrationConfirm", method = RequestMethod.GET) +public String confirmRegistration(WebRequest request, Model model, + @RequestParam("token") String token) { + VerificationToken verificationToken = service.getVerificationToken(token); + if (verificationToken == null) { + model.addAttribute("message", messages.getMessage("auth.message.invalidToken", + null, request.getLocale())); + return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage(); + } + User user = verificationToken.getUser(); + Calendar cal = Calendar.getInstance(); + if (user == null) { + model.addAttribute("message", messages.getMessage("auth.message.invalidUser", + null, request.getLocale())); + return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage(); + } + if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) { + user.setEnabled(false); + } else { + user.setEnabled(true); + } + service.saveRegisteredUser(user); + return "redirect:/login.html?lang=" + request.getLocale().getLanguage(); +} +Notice that if there is no user associated with the VerificationTokenVerificationToken badUser.html +Example 3.3.2. &#8211; The badUser.html&#8216;s field after checking if the has expired. +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> +<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<fmt:setBundle basename="messages" /> +<%@ page session="true"%> +<html> +<head> + <link href="<c:url value="/resources/bootstrap.css" />&quot; rel="stylesheet"> + <title>Expired</title> +</head> +<body> + <h1>${message}</h1> + <br> + <a href="<c:url value="/user/registration" />&quot;> + <spring:message code="label.form.loginSignUp"></spring:message> + </a> +</body> +</html> +If the token and user exist, the controller then proceeds to set the UserenabledVerificationToken +4. Adding Account Activation Checking to the Login Process +ladUserByUsername method: + +- +Make sure that the user is enabled before letting him log in. + check. +Example 4.1. shows the simple isEnabled() +Example 4.1. &#8211; Checking the VerificationToken in MyUserDetailsService with the flag set to false. This will trigger a +private UserRepository userRepository; +@Autowired +private IUserService service; +@Autowired +private MessageSource messages; + +@Autowired +public MyUserDetailsService(UserRepository repository) { + this.userRepository = repository; +} + +public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + boolean enabled = true; + boolean accountNonExpired = true; + boolean credentialsNonExpired = true; + boolean accountNonLocked = true; + try { + User user = userRepository.findByEmail(email); + if (user == null) { + return new org.springframework.security.core.userdetails.User(" ", " ", enabled, + true, true, true, getAuthorities(new Integer(1))); + } + if (!user.isEnabled()) { + accountNonExpired = false; + service.deleteUser(user); + return new org.springframework.security.core.userdetails.User(" ", " ", enabled, + accountNonExpired, true, true, getAuthorities(new Integer(1))); + } + return new org.springframework.security.core.userdetails.User(user.getEmail(), + user.getPassword().toLowerCase(), + enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, + getAuthorities(user.getRole().getRole())); + } catch (Exception e) { + throw new RuntimeException(e); + } +} +Notice that if the user is not enabled, the account is deleted and the method returns an org.springframework.security.core.userdetails.UseraccountNonExpiredUser Account Has ExpiredSPRING_SECURITY_LAST_EXCEPTION +Now, we need to modify our login.htmllogin.html: +ogin.htmlExample 4.2. &#8211; Adding Account Activation Error Checking t +<c:if test="${param.error != null}"> + <c:choose> + <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == &#039;User is disabled&#039;}"> + <div class="alert alert-error"> + <spring:message code="auth.message.disabled"></spring:message> + </div> + </c:when> + <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == &#039;User account has expired&#039;}"> + <div class="alert alert-error"> + <spring:message code="auth.message.expired"></spring:message> + </div> + </c:when> + <c:otherwise> + <div class="alert alert-error"> + <spring:message code="message.badCredentials"></spring:message> + </div> + </c:otherwise> + </c:choose> +</c:if> +5. Adapting the Persistence Layer +We need to modify the API of the persistence layer by: + +- +Creating a VerificationTokenRepository UserVerificationToken +- +Adding methods to the IUserInterface + +Examples 5.1 &#8211; 5.3. show the new interfaces and implementation: +VerificationTokenRepositoryExample 5.1. +public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> { + + VerificationToken findByToken(String token); + + VerificationToken findByUser(User user); +} +IUserServiceExample 5.2. &#8211; The Interface +public interface IUserService { + + User registerNewUserAccount(UserDto accountDto) throws EmailExistsException; + + User getUser(String verificationToken); + + void saveRegisteredUser(User user); + + void addVerificationToken(User user, String token); + + VerificationToken getVerificationToken(String VerificationToken); + + void deleteUser(User user); +} +UserService Example 5.3. +@Service +public class UserService implements IUserService { + @Autowired + private UserRepository repository; + + @Autowired + private VerificationTokenRepository tokenRepository; + + @Transactional + @Override + public User registerNewUserAccount(UserDto accountDto) throws EmailExistsException { + if (emailExist(accountDto.getEmail())) { + throw new EmailExistsException("There is an account with that email adress: " + + accountDto.getEmail()); + } + User user = new User(); + user.setFirstName(accountDto.getFirstName()); + user.setLastName(accountDto.getLastName()); + user.setPassword(accountDto.getPassword()); + user.setEmail(accountDto.getEmail()); + user.setRole(new Role(Integer.valueOf(1), user)); + return repository.save(user); + } + + private boolean emailExist(String email) { + User user = repository.findByEmail(email); + if (user != null) { + return true; + } + return false; + } + + @Override + public User getUser(String verificationToken) { + User user = tokenRepository.findByToken(verificationToken).getUser(); + return user; + } + + @Override + public VerificationToken getVerificationToken(String VerificationToken) { + return tokenRepository.findByToken(VerificationToken); + } + + @Transactional + @Override + public void saveRegisteredUser(User user) { + repository.save(user); + } + + @Transactional + @Override + public void deleteUser(User user) { + repository.delete(user); + } + + @Transactional + @Override + public void addVerificationToken(User user, String token) { + VerificationToken myToken = new VerificationToken(token, user); + tokenRepository.save(myToken); + } +} +6. Conclusion +We have expanded our Spring registration process to include an email based account activation procedure. The account activation logic requires sending a verification token to the user via email, so that he can send it back to the controller to verify his identity. A Spring event handler layer + + + Subscribe + + + Subscribe to our e-mail newsletter to receive updates. + + + + + + + + + + + + (published) Handling Static Resources With Spring + Convert HTML to PDF using Apache FOP + + + + No comments yet. + Leave a Reply Click here to cancel reply. + Logged in as odeskAuthor8. Log out? + + + + + + + � 2014 Technical Articles. All Rights Reserved. + + + + + + + + + + diff --git a/apache-fop/src/test/resources/input.html b/apache-fop/src/test/resources/input.html new file mode 100644 index 0000000000..9c18571233 --- /dev/null +++ b/apache-fop/src/test/resources/input.html @@ -0,0 +1,598 @@ + + + + + +Registration – Activate a New Account by Email | Technical Articles + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ + +
+
+
+ +

Registration – Activate a New Account by Email

+
+ +
+

1. Overview

+

This article continues our ongoing Registration with Spring Security series by finishing the missing piece of the registration process – verifying the email to confirm the user registration.

+

The registration confirmation mechanism forces the user to respond to a “Confirm Registration” email sent after successful registration to verify his email address and activate his account. The user does this by clicking a unique account activation link sent to him as part of the email message.

+

Following this logic, a newly registered user will not be able to log in until email/registration verification is completed.

+

2. A Verification Token
+

+

We will make use of a simple verification token as the key artifact through which a user is verified.

+

2.1. Adding a VerificationToken Entity to Our Model

+

The VerificationToken entity must meet the following criteria:

+
    +
  1. There will be one VerificationToken associated to a User. So, we need a one-to-one unidirectional association between the VerificationToken and the User.
  2. +
  3. It will be created after the user registration data is persisted.
  4. +
  5. It will expire in 24 hours following initial registration.
  6. +
  7. Its value should be unique and randomly generated.
  8. +
+

Requirements 2 and 3 are part of the registration logic. The other two are implemented in a simple VerificationToken entity like the one in Example 2.1.:

+

Example 2.1.

+
@Entity
+@Table
+public class VerificationToken {
+    private static final int EXPIRATION = 60 * 24;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private Long id;
+    
+    @Column(name = "token")
+    private String token;
+  
+    @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
+    @JoinColumn(name = "user_id")
+    private User user;
+    
+    @Column(name = "expiry_date")
+    private Date expiryDate;
+
+    public VerificationToken() {
+        super();
+    }
+    public VerificationToken(String token, User user) {
+        super();
+        this.token = token;
+        this.user = user;
+        this.expiryDate = calculateExpiryDate(EXPIRATION);
+        this.verified = false;
+    } 
+    private Date calculateExpiryDate(int expiryTimeInMinutes) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Timestamp(cal.getTime().getTime()));
+        cal.add(Calendar.MINUTE, expiryTimeInMinutes);
+        return new Date(cal.getTime().getTime());
+    }
+    
+    // standard getters and setters
+}
+

2.2. Add an Enabled Flag to the User Entity

+

We will set the value of this flag depending on the result of the registration confirmation use case. Lets jus add the following field to our User entity for now:

+
@Column(name = "enabled")
+private boolean enabled;
+

3. The Account Registration Phase

+

Lets add two additional pieces of business logic to the user registration use case:

+
    +
  1. Generating a VerificationToken for the user and persisting it.
  2. +
  3. Sending the account confirmation email message which includes a confirmation link with the VerificationToken’s value as a parameter.
  4. +
+

3.1. Using Spring Event Handling to Create the Token and Send the Verification Email

+

These two additional pieces of logic should not be performed by the controller directly because they are “collateral” back-end tasks. The controller will publish a Spring ApplicationEvent to trigger the execution of these tasks. This is as simple as injecting an ApplicationEventPublisher in the controller, and then using it to publish the registration completion. Example 3.1. shows this simple logic:

+

Example 3.1.

+
@Autowired
+ApplicationEventPublisher
+@RequestMapping(value = "/user/registration", method = RequestMethod.POST)
+public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto accountDto,
+      BindingResult result, WebRequest request, Errors errors) {
+    User registered = new User();
+    String appUrl = request.getContextPath();
+    if (result.hasErrors()) {
+       return new ModelAndView("registration", "user", accountDto);
+    }
+    registered = createUserAccount(accountDto);
+    if (registered == null) {
+        result.rejectValue("email", "message.regError");
+    }
+    eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, 
+      request.getLocale(), appUrl));
+    return new ModelAndView("successRegister", "user", accountDto);
+}
+

3.2. Spring Event Handler Implementation

+

The controller is using an ApplicationEventPublisher to start the RegistrationListener that will handle the verification token creation and confirmation email sending. So it needs to have access to the implementation of the following interfaces:

+
    +
  1. An AplicationEvent representing the completion of the user registration.
  2. +
  3. An ApplicationListener bean which will listen to the published event and proceed to do all the work.
  4. +
+

The beans we will create are the OnRegistrationCompleteEvent , and the RegistrationListener shown Examples 3.2.1 – 3.2.2.

+

Example 3.2.1. – The OnRegistrationCompleteEvent

+
@SuppressWarnings("serial")
+public class OnRegistrationCompleteEvent extends ApplicationEvent {
+    private final String appUrl;
+    private final Locale locale;
+    private final User user;
+
+    public OnRegistrationCompleteEvent(User user, Locale locale, String appUrl) {
+        super(user);
+        this.user = user;
+        this.locale = locale;
+        this.appUrl = appUrl;
+    }
+    
+    // standard getters and setters
+}
+

Example 3.2.2. - The RegistrationListener Responds to the OnRegistrationCompleteEvent

+
@Component
+public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> {
+    @Autowired
+    private IUserService service;
+
+    @Autowired
+    private MessageSource messages;
+
+    @Autowired
+    private JavaMailSender mailSender;
+
+    @Override
+    public void onApplicationEvent(OnRegistrationCompleteEvent event) {
+        this.confirmRegistration(event);
+    }
+
+    private void confirmRegistration(OnRegistrationCompleteEvent event) {
+        User user = event.getUser();
+        String token = UUID.randomUUID().toString();
+        service.addVerificationToken(user, token);
+        String recipientAddress = user.getEmail();
+        String subject = "Registration Confirmation";
+        String confirmationUrl = event.getAppUrl() + "/regitrationConfirm.html?token=" + token;
+        String message = messages.getMessage("message.regSucc", null, event.getLocale());
+        SimpleMailMessage email = new SimpleMailMessage();
+        email.setTo(recipientAddress);
+        email.setSubject(subject);
+        email.setText(message + " \r\n" + "http://localhost:8080" + confirmationUrl);
+        mailSender.send(email);
+    }
+}
+

Here, the confirmRegistration method will receive the OnRegistrationCompleteEvent, extract all the necessary User information from it, create the verification token, persist it, and then send it as a parameter in the “Confirm Registration” link sent to the user.

+

3.3. Processing the Verification Token Parameter

+

When the user receives the “Confirm Registration” email, he will click on the attached link and fire a GET request. The controller will extract the value of the token parameter in the GET request and will use it to verify the user. Lets see this logic in Example 3.3.1.

+

Example 3.3.1. – RegistrationController Processing the Registration Confirmation Link

+
private IUserService service;
+
+@Autowired
+public RegistrationController(IUserService service){
+    this.service = service
+}
+@RequestMapping(value = "/regitrationConfirm", method = RequestMethod.GET)
+public String confirmRegistration(WebRequest request, Model model, 
+      @RequestParam("token") String token) {
+    VerificationToken verificationToken = service.getVerificationToken(token);
+    if (verificationToken == null) {
+        model.addAttribute("message", messages.getMessage("auth.message.invalidToken", 
+          null, request.getLocale()));
+        return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage();
+    }
+    User user = verificationToken.getUser();
+    Calendar cal = Calendar.getInstance();
+    if (user == null) {
+        model.addAttribute("message", messages.getMessage("auth.message.invalidUser",
+          null, request.getLocale()));
+        return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage();
+    }
+    if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
+        user.setEnabled(false);
+    } else {
+        user.setEnabled(true);
+    }
+    service.saveRegisteredUser(user);
+    return "redirect:/login.html?lang=" + request.getLocale().getLanguage();
+}
+

Notice that if there is no user associated with the VerificationToken or if the VerificationToken does not exist, the controller will return a badUser.html page with the corresponding error message (See Example 3.3.2.).

+

Example 3.3.2. – The badUser.html

+
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
+<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<fmt:setBundle basename="messages" />
+<%@ page session="true"%>
+<html>
+<head>
+    <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet">
+    <title>Expired</title>
+</head>
+<body>
+    <h1>${message}</h1>
+    <br>
+    <a href="<c:url value="/user/registration" />">
+        <spring:message code="label.form.loginSignUp"></spring:message>
+    </a>
+</body>
+</html>
+

If the token and user exist, the controller then proceeds to set the User‘s enabled field after checking if the VerificationToken has expired.

+

4. Adding Account Activation Checking to the Login Process

+

We need to add the following verification logic to MyUserDetailsService’s loadUserByUsername method:

+
    +
  • Make sure that the user is enabled before letting him log in.
  • +
+

Example 4.1. shows the simple isEnabled() check.

+

Example 4.1. – Checking the VerificationToken in MyUserDetailsService

+
private UserRepository userRepository;
+@Autowired
+private IUserService service;
+@Autowired
+private MessageSource messages;
+
+@Autowired
+public MyUserDetailsService(UserRepository repository) {
+    this.userRepository = repository;
+}
+
+public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
+    boolean enabled = true;
+    boolean accountNonExpired = true;
+    boolean credentialsNonExpired = true;
+    boolean accountNonLocked = true;
+    try {
+        User user = userRepository.findByEmail(email);
+        if (user == null) {
+            return new org.springframework.security.core.userdetails.User(" ", " ", enabled, 
+                    true, true, true, getAuthorities(new Integer(1)));
+        }
+        if (!user.isEnabled()) {
+            accountNonExpired = false;
+            service.deleteUser(user);
+            return new org.springframework.security.core.userdetails.User(" ", " ", enabled, 
+              accountNonExpired, true, true, getAuthorities(new Integer(1)));
+        }
+        return new org.springframework.security.core.userdetails.User(user.getEmail(), 
+          user.getPassword().toLowerCase(), 
+          enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, 
+          getAuthorities(user.getRole().getRole()));
+    } catch (Exception e) {
+        throw new RuntimeException(e);
+    }
+}
+

Notice that if the user is not enabled, the account is deleted and the method returns an org.springframework.security.core.userdetails.User with the accountNonExpired flag set to false. This will trigger a SPRING_SECURITY_LAST_EXCEPTION in the login process. This exception’s String value is: ‘User Account Has Expired‘.

+

Now, we need to modify our login.html page to show this and any other exception messages resulting from en email verification error. The error checking code we added to login.html is shown in Example 4.2.:

+

Example 4.2. – Adding Account Activation Error Checking to login.html

+
<c:if test="${param.error != null}">
+    <c:choose>
+        <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'User is disabled'}">
+            <div class="alert alert-error">
+                <spring:message code="auth.message.disabled"></spring:message>
+            </div>
+        </c:when>
+        <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'User account has expired'}">
+            <div class="alert alert-error">
+                <spring:message code="auth.message.expired"></spring:message>
+            </div>
+        </c:when>
+        <c:otherwise>
+            <div class="alert alert-error">
+	      <spring:message code="message.badCredentials"></spring:message>
+           </div>
+        </c:otherwise>
+    </c:choose>
+</c:if>
+

5. Adapting the Persistence Layer

+

We need to modify the API of the persistence layer by:

+
    +
  1. Creating a VerificationTokenRepository. For User and VerificationToken access.
  2. +
  3. Adding methods to the IUserInterface and its implementation for new CRUD operations needed.
  4. +
+

Examples 5.1 – 5.3. show the new interfaces and implementation:

+

Example 5.1. – The VerificationTokenRepository

+
public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> {
+
+    VerificationToken findByToken(String token);
+
+    VerificationToken findByUser(User user);
+}
+

Example 5.2. – The IUserService Interface

+
public interface IUserService {
+    
+    User registerNewUserAccount(UserDto accountDto) throws EmailExistsException;
+
+    User getUser(String verificationToken);
+
+    void saveRegisteredUser(User user);
+
+    void addVerificationToken(User user, String token);
+
+    VerificationToken getVerificationToken(String VerificationToken);
+
+    void deleteUser(User user);
+}
+

Example 5.3. The UserService

+
@Service
+public class UserService implements IUserService {
+    @Autowired
+    private UserRepository repository;
+
+    @Autowired
+    private VerificationTokenRepository tokenRepository;
+
+    @Transactional
+    @Override
+    public User registerNewUserAccount(UserDto accountDto) throws EmailExistsException {
+        if (emailExist(accountDto.getEmail())) {
+            throw new EmailExistsException("There is an account with that email adress: " + 
+              accountDto.getEmail());
+        }
+        User user = new User();
+        user.setFirstName(accountDto.getFirstName());
+        user.setLastName(accountDto.getLastName());
+        user.setPassword(accountDto.getPassword());
+        user.setEmail(accountDto.getEmail());
+        user.setRole(new Role(Integer.valueOf(1), user));
+        return repository.save(user);
+    }
+
+    private boolean emailExist(String email) {
+        User user = repository.findByEmail(email);
+        if (user != null) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public User getUser(String verificationToken) {
+        User user = tokenRepository.findByToken(verificationToken).getUser();
+        return user;
+    }
+
+    @Override
+    public VerificationToken getVerificationToken(String VerificationToken) {
+        return tokenRepository.findByToken(VerificationToken);
+    }
+
+    @Transactional
+    @Override
+    public void saveRegisteredUser(User user) {
+        repository.save(user);
+    }
+
+    @Transactional
+    @Override
+    public void deleteUser(User user) {
+        repository.delete(user);
+    }
+
+    @Transactional
+    @Override
+    public void addVerificationToken(User user, String token) {
+        VerificationToken myToken = new VerificationToken(token, user);
+        tokenRepository.save(myToken);
+    }
+}
+

6. Conclusion

+

We have expanded our Spring registration process to include an email based account activation procedure. The account activation logic requires sending a verification token to the user via email, so that he can send it back to the controller to verify his identity. A Spring event handler layer takes care of the back-end work needed to send the confirmation email after the controller persists a registered.

+
+
+ +
+
+
+ + +
+
+ +
No comments yet.
+

Leave a Reply

+
+

Logged in as odeskAuthor8. Log out?

+ + + +

+
+
+ +
+ + +
+ + +
+ +
+ + + + +
+
+ +
+ + +
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/apache-fop/src/test/resources/input.xml b/apache-fop/src/test/resources/input.xml new file mode 100644 index 0000000000..4760c7968f --- /dev/null +++ b/apache-fop/src/test/resources/input.xml @@ -0,0 +1,544 @@ + +
+ + Registration – Activate a New Account by Email | Technical Articles + + 1. Overview This article continues our ongoing Registration with Spring Security series by finishing the missing piece of the registration process - verifying + + + + + + + + + + + + + + + + + + + + + + +
+ <link linkend="navigation">Navigation</link> + + Technical Articles + Home + + + Home + + + + + + + + + + + Return to Content + + + + + Registration – Activate a New Account by Email + By + Elena + on + October 23, 2014 + in + Spring Security + +
+ <emphasis role="bold">1. Overview</emphasis> + This article continues our ongoing Registration with Spring Security series by finishing the missing piece of the registration process – verifying the email to confirm the user registration. + The registration confirmation mechanism forces the user to respond to a “Confirm Registration” email sent after successful registration to verify his email address and activate his account. The user does this by clicking a unique account activation link sent to him as part of the email message. + Following this logic, a newly registered user will not be able to log in until email/registration verification is completed. +
+
+ <emphasis role="bold">2. A Verification Token</emphasis> + We will make use of a simple verification token as the key artifact through which a user is verified. +
+ <emphasis role="bold">2.1. Adding a VerificationToken Entity to Our Model</emphasis> + The VerificationToken entity must meet the following criteria: + + There will be one VerificationToken associated to a User. So, we need a one-to-one unidirectional association between the VerificationToken and the User. + + + It will be created after the user registration data is persisted. + + + It will expire in 24 hours following initial registration. + + + Its value should be unique and randomly generated. + + + Requirements 2 and 3 are part of the registration logic. The other two are implemented in a simple VerificationToken entity like the one in Example 2.1.: + Example 2.1. + @Entity +@Table +public class VerificationToken { + private static final int EXPIRATION = 60 * 24; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(name = "token") + private String token; + + @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER) + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "expiry_date") + private Date expiryDate; + + public VerificationToken() { + super(); + } + public VerificationToken(String token, User user) { + super(); + this.token = token; + this.user = user; + this.expiryDate = calculateExpiryDate(EXPIRATION); + this.verified = false; + } + private Date calculateExpiryDate(int expiryTimeInMinutes) { + Calendar cal = Calendar.getInstance(); + cal.setTime(new Timestamp(cal.getTime().getTime())); + cal.add(Calendar.MINUTE, expiryTimeInMinutes); + return new Date(cal.getTime().getTime()); + } + + // standard getters and setters +} +
+
+ <emphasis role="bold">2.2. Add an Enabled Flag to the User Entity</emphasis> + We will set the value of this flag depending on the result of the registration confirmation use case. Lets jus add the following field to our User entity for now: + @Column(name = "enabled") +private boolean enabled; +
+
+
+ <emphasis role="bold">3. The Account Registration Phase</emphasis> + Lets add two additional pieces of business logic to the user registration use case: + + Generating a VerificationToken for the user and persisting it. + + + Sending the account confirmation email message which includes a confirmation link with the VerificationToken’s valueas a parameter. + + +
+ <emphasis role="bold">3.1. Using Spring Event Handling to Create the Token and Send the Verification Email</emphasis> + These two additional pieces of logic should not be performed by the controller directly because they are “collateral” back-end tasks. The controller will publish a Spring ApplicationEvent to trigger the execution of these tasks. This is as simple as injecting an ApplicationEventPublisher in the controller, and then using it to publish the registration completion. Example 3.1. shows this simple logic: + Example 3.1. + @Autowired +ApplicationEventPublisher +@RequestMapping(value = "/user/registration", method = RequestMethod.POST) +public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto accountDto, + BindingResult result, WebRequest request, Errors errors) { + User registered = new User(); + String appUrl = request.getContextPath(); + if (result.hasErrors()) { + return new ModelAndView("registration", "user", accountDto); + } + registered = createUserAccount(accountDto); + if (registered == null) { + result.rejectValue("email", "message.regError"); + } + eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, + request.getLocale(), appUrl)); + return new ModelAndView("successRegister", "user", accountDto); +} +
+
+ <emphasis role="bold">3.2. Spring Event Handler Implementation</emphasis> + The controller is using an ApplicationEventPublisher to start the RegistrationListener that will handle the verification token creation and confirmation email sending. So it needs to have access to the implementation of the following interfaces: + + An AplicationEvent representing the completion of the user registration. + + + An ApplicationListener bean which will listen to the published event and proceed to do all the work. + + + The beans we will create are the OnRegistrationCompleteEvent , and the RegistrationListener shown Examples 3.2.1 – 3.2.2. + Example 3.2.1. – The OnRegistrationCompleteEvent + @SuppressWarnings("serial") +public class OnRegistrationCompleteEvent extends ApplicationEvent { + private final String appUrl; + private final Locale locale; + private final User user; + + public OnRegistrationCompleteEvent(User user, Locale locale, String appUrl) { + super(user); + this.user = user; + this.locale = locale; + this.appUrl = appUrl; + } + + // standard getters and setters +} + Example 3.2.2. - The RegistrationListener Responds to the OnRegistrationCompleteEvent + @Component +public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> { + @Autowired + private IUserService service; + + @Autowired + private MessageSource messages; + + @Autowired + private JavaMailSender mailSender; + + @Override + public void onApplicationEvent(OnRegistrationCompleteEvent event) { + this.confirmRegistration(event); + } + + private void confirmRegistration(OnRegistrationCompleteEvent event) { + User user = event.getUser(); + String token = UUID.randomUUID().toString(); + service.addVerificationToken(user, token); + String recipientAddress = user.getEmail(); + String subject = "Registration Confirmation"; + String confirmationUrl = event.getAppUrl() + "/regitrationConfirm.html?token=" + token; + String message = messages.getMessage("message.regSucc", null, event.getLocale()); + SimpleMailMessage email = new SimpleMailMessage(); + email.setTo(recipientAddress); + email.setSubject(subject); + email.setText(message + " \r\n" + "http://localhost:8080" + confirmationUrl); + mailSender.send(email); + } +} + Here, the confirmRegistration method will receive the OnRegistrationCompleteEvent, extract all the necessary User information from it, create the verification token, persist it, and then send it as a parameter in the “Confirm Registration” link sent to the user. +
+
+ <emphasis role="bold">3.3. Processing the Verification Token Parameter</emphasis> + When the user receives the “Confirm Registration” email, he will click on the attached link and fire a GET request. The controller will extract the value of the token parameter in the GET request and will use it to verify the user. Lets see this logic in Example 3.3.1. + Example 3.3.1. – RegistrationController Processing the Registration Confirmation Link + private IUserService service; + +@Autowired +public RegistrationController(IUserService service){ + this.service = service +} +@RequestMapping(value = "/regitrationConfirm", method = RequestMethod.GET) +public String confirmRegistration(WebRequest request, Model model, + @RequestParam("token") String token) { + VerificationToken verificationToken = service.getVerificationToken(token); + if (verificationToken == null) { + model.addAttribute("message", messages.getMessage("auth.message.invalidToken", + null, request.getLocale())); + return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage(); + } + User user = verificationToken.getUser(); + Calendar cal = Calendar.getInstance(); + if (user == null) { + model.addAttribute("message", messages.getMessage("auth.message.invalidUser", + null, request.getLocale())); + return "redirect:/badUser.html?lang=" + request.getLocale().getLanguage(); + } + if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) { + user.setEnabled(false); + } else { + user.setEnabled(true); + } + service.saveRegisteredUser(user); + return "redirect:/login.html?lang=" + request.getLocale().getLanguage(); +} + Notice that if there is no user associated with the VerificationToken or if the VerificationToken does not exist, the controller will return a badUser.html page with the corresponding error message (See Example 3.3.2.). + Example 3.3.2. – The badUser.html + <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> +<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> +<fmt:setBundle basename="messages" /> +<%@ page session="true"%> +<html> +<head> + <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet"> + <title>Expired</title> +</head> +<body> + <h1>${message}</h1> + <br> + <a href="<c:url value="/user/registration" />"> + <spring:message code="label.form.loginSignUp"></spring:message> + </a> +</body> +</html> + If the token and user exist, the controller then proceeds to set the User‘s enabled field after checking if the VerificationToken has expired. +
+
+
+ <emphasis role="bold">4. Adding Account Activation Checking to the Login Process</emphasis> + We need to add the following verification logic to MyUserDetailsService’s loadUserByUsername method: + + + Make sure that the user is enabled before letting him log in. + + + Example 4.1. shows the simple isEnabled() check. + Example 4.1. – Checking the VerificationToken in MyUserDetailsService + private UserRepository userRepository; +@Autowired +private IUserService service; +@Autowired +private MessageSource messages; + +@Autowired +public MyUserDetailsService(UserRepository repository) { + this.userRepository = repository; +} + +public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + boolean enabled = true; + boolean accountNonExpired = true; + boolean credentialsNonExpired = true; + boolean accountNonLocked = true; + try { + User user = userRepository.findByEmail(email); + if (user == null) { + return new org.springframework.security.core.userdetails.User(" ", " ", enabled, + true, true, true, getAuthorities(new Integer(1))); + } + if (!user.isEnabled()) { + accountNonExpired = false; + service.deleteUser(user); + return new org.springframework.security.core.userdetails.User(" ", " ", enabled, + accountNonExpired, true, true, getAuthorities(new Integer(1))); + } + return new org.springframework.security.core.userdetails.User(user.getEmail(), + user.getPassword().toLowerCase(), + enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, + getAuthorities(user.getRole().getRole())); + } catch (Exception e) { + throw new RuntimeException(e); + } +} + Notice that if the user is not enabled, the account is deleted and the method returns an org.springframework.security.core.userdetails.User with the accountNonExpired flag set to false. This will trigger a SPRING_SECURITY_LAST_EXCEPTION in the login process. This exception’s String value is: ‘User Account Has Expired‘. + Now, we need to modify our login.html page to show this and any other exception messages resulting from en email verification error. The error checking code we added to login.html is shown in Example 4.2.: + Example 4.2. – Adding Account Activation Error Checking to login.html + <c:if test="${param.error != null}"> + <c:choose> + <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'User is disabled'}"> + <div class="alert alert-error"> + <spring:message code="auth.message.disabled"></spring:message> + </div> + </c:when> + <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'User account has expired'}"> + <div class="alert alert-error"> + <spring:message code="auth.message.expired"></spring:message> + </div> + </c:when> + <c:otherwise> + <div class="alert alert-error"> + <spring:message code="message.badCredentials"></spring:message> + </div> + </c:otherwise> + </c:choose> +</c:if> +
+
+ 5. Adapting the Persistence Layer + We need to modify the API of the persistence layer by: + + Creating a VerificationTokenRepository. For User and VerificationToken access. + + + Adding methods to the IUserInterface and its implementation for new CRUD operations needed. + + + Examples 5.1 – 5.3. show the new interfaces and implementation: + Example 5.1. – The VerificationTokenRepository + public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> { + + VerificationToken findByToken(String token); + + VerificationToken findByUser(User user); +} + Example 5.2. – The IUserService Interface + public interface IUserService { + + User registerNewUserAccount(UserDto accountDto) throws EmailExistsException; + + User getUser(String verificationToken); + + void saveRegisteredUser(User user); + + void addVerificationToken(User user, String token); + + VerificationToken getVerificationToken(String VerificationToken); + + void deleteUser(User user); +} + Example 5.3. The UserService + @Service +public class UserService implements IUserService { + @Autowired + private UserRepository repository; + + @Autowired + private VerificationTokenRepository tokenRepository; + + @Transactional + @Override + public User registerNewUserAccount(UserDto accountDto) throws EmailExistsException { + if (emailExist(accountDto.getEmail())) { + throw new EmailExistsException("There is an account with that email adress: " + + accountDto.getEmail()); + } + User user = new User(); + user.setFirstName(accountDto.getFirstName()); + user.setLastName(accountDto.getLastName()); + user.setPassword(accountDto.getPassword()); + user.setEmail(accountDto.getEmail()); + user.setRole(new Role(Integer.valueOf(1), user)); + return repository.save(user); + } + + private boolean emailExist(String email) { + User user = repository.findByEmail(email); + if (user != null) { + return true; + } + return false; + } + + @Override + public User getUser(String verificationToken) { + User user = tokenRepository.findByToken(verificationToken).getUser(); + return user; + } + + @Override + public VerificationToken getVerificationToken(String VerificationToken) { + return tokenRepository.findByToken(VerificationToken); + } + + @Transactional + @Override + public void saveRegisteredUser(User user) { + repository.save(user); + } + + @Transactional + @Override + public void deleteUser(User user) { + repository.delete(user); + } + + @Transactional + @Override + public void addVerificationToken(User user, String token) { + VerificationToken myToken = new VerificationToken(token, user); + tokenRepository.save(myToken); + } +} +
+
+ 6. Conclusion + We have expanded our Spring registration process to include an email based account activation procedure. The account activation logic requires sending a verification token to the user via email, so that he can send it back to the controller to verify his identity. A Spring event handler layer takes care of the back-end work needed to send the confirmation email after the controller persists a registered. + + + + +
+ Subscribe + Subscribe to our e-mail newsletter to receive updates. + + + + (published) Handling Static Resources With Spring + Convert HTML to PDF using Apache FOP + +
+ No comments yet. + +
+
+
+ Leave a Reply <link xl:href="/?p=1092#respond">Click here to cancel reply.</link> + + Logged in as odeskAuthor8. Log out? + Comment + + + + + + + © 2014 Technical Articles. All Rights Reserved. + + + + +
+
+
+
diff --git a/apache-fop/src/test/resources/output_herold.pdf b/apache-fop/src/test/resources/output_herold.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1d23de7b616c734d4c3e85d71b628c6ef6600575 GIT binary patch literal 34733 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 literal 0 HcmV?d00001 diff --git a/apache-fop/src/test/resources/output_html2fo.pdf b/apache-fop/src/test/resources/output_html2fo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7c2b4a0c51550a3033d6489ea16e79f32b11584d GIT binary patch literal 14642 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 literal 0 HcmV?d00001 diff --git a/apache-fop/src/test/resources/output_jtidy.pdf b/apache-fop/src/test/resources/output_jtidy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1d9456122cf14bda4aa8c1b8fb0a619bb8d86c07 GIT binary patch literal 25904 zcmdSBWk6Nk);3JHlyq*oVQ;#-yBm}S>27K129Xw|yE~=3yAkPB2W|+my{x~0)PNp!&eBrya-Gp4kiXpwhjPl zAv*&ja}$8Lf+9fO#KFA#1koTcDLU91I~$q&zpDIr$kQF722LgbYEf=xAcz$N zV&(*KftXo1>43~sKp+(j0zW^3iH-61H?#e+003Z;2e2SK{gE}XF>^8pFf%`0RQ_Hd zZeiv0^j;=$tEcCRni$y{n|yzUqtlbk)(CEC>2F;%Rd$H_)*R)pUXqho!Fj&P6$Fb# z%kxEA$VQC=14jptBV7xsR%93SD?`zQX;x^znGU5!4%=LPrVN#!U9$OcLNp`)^6F}J zY3YP`$17v)?yhy`A@bTC87#+FwhV}^4lYO?7eaR5id$*_1?dpX7o6y&FCQ3zjZK|~ zrY10kZaitEKat1JXQDV2Ou3~Tgz4$`s8FgUvpti$LwygR~i;`tk1nir!Xq)D5+-~um3 z=RRAd(O@EM-HW5qZ8>gny_ZC24jhws+U$p`OWOhSCCh!?Iq;$)@C87Lyk~Tf z5d%?qiX6$Y47zKzC#VN*eaGyK0^JlRIJFb)9OsbUfL%fru0wV-Q%q_4EeTUqd~108 z^Qq3>Z{flZb3N@eVzy?xVqbSMgf6}bSowTTngIi|l-5sW@R^S*2HVwz_3d2|u$ zQ+#_}`sob&W*qZ8jxyG`d2w-jXJ-lSt1iBnkk8zs(^vf4O;7f)qt{F*UBQ_jV@;)T z!kX->nX7XQ#ckMwhs=PFyxotSr{!Y?gq;c-;Wwo2qBUNFP}!>P&0@TMqA?VPaH5Ze zp^v~rg_LB=#)3#~v7rK`(<{`yxg7=D$R5ftW&t zG9Y0V4Pqtn?uV-KCjelvVxI;1XTeb;OZzL6ppgf?6|&4yE+8m+v!7+UtFue(gx3W2 zOxX23i8;Y7rUxt;5I(Q7se;A|vHB$wCtOq@c4wg-atjorKSx)h9i$iPv9Cf`(mH+x z(rW|-5-<`{r*1F`c{-gD{ejq zDlVZWxhC2v%qjT7UzRd8j&I1Qaz>3qqV|}8bnCMOZ-4vubQV?SaemCRU|wkR|KodR1l?9 zfuRz{6eK~DeVCh;g`|R3gqA4xS$R$wztF6Zj1_4l0!vaRKXI}x>)ANv4E3w(nd}*a z=~vTH#RI!=D+xrBV>w9^C{wnRYlR0zo`vT#97Xj-FVuWWvr0D#JLjk;%%>W%zR1UC z?G-0yHL6vsocm!(4=K~>(!H>(pVcabw@#@`W!GpHy{ov=Bh>6urV=(Q9Mw;Molc|2 zr{}X`wL-hX()8TJiBF-`srAT%>Z13u@YeAX`gZA3VPC+Y0Mvq$93d^vWHc$*2j~!d zrDzL$3A8ph#W88aTtHNT<0adCKfJMGkuxn$G1^$E#9)dO)7m! zM@8p1!&oe2SyLNc+uziAixuB}VYm&_c_+f|l=?E2jXj-x%qr6QlXd;#)xzr<$C{xU z%bI30h008`_TILoZyK;Nuw^2VU6)#0TCc0sNB2`Mz3OJHz8$Vq+wv$AiV)24Tyeg( zb8tcDspXF3!RMjkn&VzGoX|8Vkt;c8G{?40N@VQQU2WoSl-5OQ{|sHJE1 zXtjKy?C|rU3V%p+yF%!gFH;Z4VEy8CR%X_Vq8H(7Iqe@Fl^<2WcYWXcmV(zqI72r2 zjrhf%r|mFg&f$w9XrrnUUQo|7YZ5FILIe*ZE?_zMv<-Mo+>a`v;^8zgTnN3>(v+XS z-gtLfahh`4hjxWFM0$&*h)vGOS+TB`<7%w6)k=L%ZK0}A*&v3pTNWs{9r?k(4l1 z4>_Tv#~A!)R=d17$au?msz$40`D2v~Dq9}gw3AV$o#?!V{Dl07+@^dR8A63~$zsV= z)C%Ys|D8SXJ?Fi07Gahv6R*LD0juYmfl?nokcLr zjkRUeM^C0wxw7HwtVjK&Grl4|6g~|5=%Qxr7bE!cEyF@R3$eXBxn()*_NvFCvtor* z6?T*auEe8N{{_-T2gl;R*@w?XpJfJv>w~Ng=11rCs~$c_brWLKnrciRksl@;C#b<_ zxTxW3SQmP{NLTm%(A%6>s-Ev+babHCqNm~Nd2CZURZ3UtSuats}w}HrgkOU4U1o@o(Db`qvB83b#T~My($cyD$Ealipt07p?r?594)TvR^toj zJNKjMd*}N+?yC>?m5627QToPuhb>!9g&)=`+aF;2h!CIB#@0_E^7^;n-foX~A_qzbXyXsak4TGIC`;;cZWKb-i6Jy4$^@Kv5-9<(+n) zKmE`XZOu%z@P#BZ>zf23(F*=HvcLn)b^bM(SoA$5S?}qOeT-)8gO$y-haGmMi-%pjN>YKOCUprh4WfWEv2A2nx=e>11E#;gjB`Z(CJ5^rkw0Q87s>V#`=qz z@(Q>;v1v;aCxD@enS~7>=}B`tDZs**k5q$A4k%|QVq$I~>EU3a;vuhU`u}?|6DdCeuY<8Ex3Z}CUsaxd@sXN4 zIoWYDF}b?BGP<%b+B%pqfw;K1n1IYo%*+f=H5eS-ZJZ3;7;GHLem+6e#L>vX!p`YC z&;QrUjE(+&ke#!G)sM#*8!?$!nOK|HI5{#ssR1TZ!1uQs7}`2J@sWOil(7-FsjY*x zffK)hot>41k-_%|GT9lM@-qFp^rM=UfsL87ftd-v8-ufrrH!qt&5tU77yqdA)X*mU z-_-*H2*kk5sS4ua1~GI0kPzHJ;Ex-AR6*cn`fkWSocOC@{!#LmTTiz9?U#)s`Ha50SPgQ@$09gwg zO9Uom6C)>p4uFl75eQ&qXJ-Vm0a%z>7+INF0jwMxj373E-jm3Fs`1@6;P)TGwhqQm zs`eixLVzEZDk(l71*mrG1f-2eh|F_S%Yke#iK@o$a)8M}FT|Hya{2au5i z!~tLhu`_Z4IRPxJoQy1-T!5#xVFWRA{8^{`0Egdn$}gDsvrcifV=yqbws?w*4z{Ki zRwj&g=61hzlJg(E{Rce+VtyKLtWVByJ&nGnP67fMft;KGRv_CSkGt=#{BGQF{lk(! znMqGBTG>89-FMdn42-^`0N+!=6HffjQRaV6)IV^P8Td5(*#GvE?P-3oKMltJgP-61 z`Hi2<|D5JO*Pp)66XoBoJ^_9~yjxnXhMwaRJF3@Qd4q%*r8pQWx!<0XHb-RYUd~O$ z%r?m=q})gJzPN&z1ZBCU$5m*l&`fFO(#=o?xQ3ObRvyJnQ-98HdQC5~Vlm3iGELL4 zsx!B%&F1`(Mu->rNKfCHd5tC_NlEucZlvKyZ(H8l*&AV7C~Yexo8mXcGruoxkiAy2 zpvj)QVp;87uu++O@Y65(;3VcVwcI--hbKYTG&sbnzpyzVk@Cpx3Ce7RjK^yv-GPnL z4Nf^$iqA4${Mrk(tk_X=76IZeNVfhEl*?`B*&%5f2;{yHzk?>Wx@vftV#&yB&blnO z&;@LuLe+g`-ZdscytPYrJvFA(uOBzxn~W5b3oi#5H5YAr#XdIGjjU-G7X~c^7Tr>@ z(zdcvKKbrSD@8GZ+-IKG+ZTGY&i%@q!xg(7n?0ZKo2PWWNrnxUsyZ=mR=YTAhQYEB z+={`L{)dJd{^|Di{AWc55cR_D+9YdyH#Sh8J!RDfV7+tp=Ob54#k5Ut{4gh0SM^xU zRs8&8EU>9BMDh1BC)gP*D{Iv+>r98LEu#Ys(3>1U%y7=sn4Ea+{1`xw4C~FAGQsRV zFqPRsU);&%Xx3r zbz{}~Rcf&;sBZ5hu9p>)DxD?jPMqkO%TC4YowDT2n3D?&$0 zpWgAdF=5yv=t$-FFCh9B6z*7yp1Vm0=|o9?lmJaS01^ztM8A-+No-oFY%jbD3eYU< zK&g>M6jrestl_NydG#l+Q|lI{k~U<2;BnZE@|7;CTFT^{*EGED@Ha~B{-P-co0?gx z^VOdwDh52vogj8_7tNzr$!5jLo~Fr(NpC;1`X23>v7QiMK^oZ#hpGVcJiSe5>JD{} zWMrYGKHdljW!bqs0qb&Hr3w(J<97&O874j_8dip*Ag}Z&wGg3tw9L zin3>bEC^e%n4&f9@oMn%#nq0bYWoL=+DO9&hlg!zY@8@O*h3-90pZ-`Oor3FO36KE0>tF0WK>w2Q7~-QYU7q3&GKv3X+pMxMq@C zQ%r0z{E#_IaP>3K7g!^|6)JkAuQp!7XEbdWI=R7crHK-qTMOvL4>dKXKECi!6MQX> z%AYe6T)yX|_xW*V*pD;mdA6Nu1uM0J=uAV)3cbooL~*4mCMd)(BJj3hAlGEEM*hIN zbMZaq`SB8wV|jiiB=gq{W@smETkG`f2>}x)wt`xsah7ivw;FCmM!4Bjl(9>fjA!p` z?`ZD7+&161d#qfZUljRL`;rNwe64v+D`G2~e?Uu|e1nN4!2*4LcQ#EYV$cSh5PFLk z(J#264}@5o@e0^IavaASYizAtz^yj7kUL2gg%J0R4#f2%zr_nYfmK=0kGcxEtz`;A_34hoTgPVkZ-{?dq~`Q|e>9TGB*Cl-Y7(NZZz@A|IDvWG&#sb+v^}>-VwYJODJt6ynJAuRr>C2$ z^F~HoGU52TLgS4LH7370%;;@UfI1*d&AptvVXdcnr+Trw?ilMiFkTrv%zzt+B)QZj zN&pDQF{ZxcaG;RgLfw{n`(UZZdY$R$v@b#fG6^2qqw>IK7|#t?d$Wvf?4!5Yd25_% z=r{7JzEuqd#Tg{W7q_B7 zj7NMo0J-v;NYzlj+~Yj+g>{_guIulhnDL_DDNTNmQ&d`bp(#Yoo4~irzm3bV9x`x? zUT%i6Hku#(FoBPgH(_$yd4JSVJ2LT=ltD)U1dVn*elx@H06^x$;HS}MdcFU}C_$>S z%53H?d!J{?!!i8%F4#o*)TFxmVhjqBspn9|mozm_m7?KW7UHmVut)RI+Bz~-HwXKR zDa;h@(jAps>&6G`*EODUK13Xo93*u%lf0P<#K3j|o_1-|v4;{j+2Aw5PL8^RR0O!Y z{!0!6PkOo|sY1`RE=jk^&9I(hR&Y-KykrHx9bY%@r5fM*@u;~n%xVD}cHbxk$iowQ z=8Hugu7nURR4GyFPa`hf79`*? zOwKH8itVg5>$-)xB$xbr$a#&og8n)n9-S)mqn>8Z;3tzL{a~EXVqpxU@^RX$U4KH3 zSg{pBwKeY(7)&wGnkGoE5}dVDb@5}a<*SZ^CPs$0OX4@5<-KvQIPw} zEYpeWfCoVBE%OKQy6fK9#oMTCF<=u$q~<7koY%1ASV&Ij$z?(>biy<-TAAw5h90yr z&@R;}&dQ{Xq;`R1Yp4?a*T8aLXUg@&M1xp|d-XHXtrWDoi3*QkqH5!b7;!{U=s;X& z3wADH0jv|**l}6FNg@%E8D2#}Zv@vcEO&sLSf5Kq7{B~C=nB>9(C=~df38z9|B}f6 zC1~>tT>gck{{)wxYWe9rz%R`Br@s6PGg!VuB3C2>^mYYqDQEii=EA)eOY+|%BlhWhcYo4Z3TI5N}%Y!flLk=$6X)M(&_YR~uPe%rt zpkhbjE(emHkG`cU&!gJ0g$kc{s(eA_)#pGO?Kp@0&RT~zjB_WpE6?((Z=qH<&m?z~ zmcfC&+B#E@QtcL1Bd+JrwXvr!r{Fwn#oI{ZfOBMCz=}3rErZg&mr*T|GMPT}VIZ$P zT~|(l|6{zV*-_d~yd&Y<#|%rCbuD30r7if>8}(goH{#n!vs5=4Eh9G{kwAO`6ib4> zhE5Qc#9Qnp-39|=dea_5^JTPU1kPDU#)wP*=-%DNi?wL9N~eGedZsEr&Im)urR$e$ zA$Y}gY^Oqj+u8WZV^dxOMVOox3zA`8u|>`zsnabbCR*smOKK@4nkiITd`T3VhovKv z_ZikxuS{&Ln%|z>*-gWzL|Sy2F5KXI;=FyXt=lmIbQ3PgQvHjzy0R9J5i*nwG+L_%JC`G&3{;@kJVfXi)}1Q) zTMm4&q+(cjpik(zdmb9&yd{Oy9Y&K&+D1LNYMPQbmob;!6~>-tlOM;81JHa_nD~|) zj)cV8v?-3_Y88t89aL2Qk{M(FtIp$beo0ckvyG(`cjh(^@LPX=8cbJvFu!`Y&iYMBg8B6U`~PUHbO1%%ALlIhC5; zaJgig1?=+r4cAf7=S9z(-?Z*-rGtl_JQLp4=8c0RMS>yF*PT_X40=7XRo&=DdOFyG zU7Jn(*i(D2JXGa@ZOabkAvC9%S_N7ppz=AOL=e#wethU#!GqprF-+*AshWNjufEj>MsC%@Y4aFcy>lIz2a zK)~IVXvb1$qrh&IxzCa{BIZ=NMj^janTeJe=2iYsp!Zu>Yt^sPFQ^Ws3xyM9_Yv72 zmzKLeEUpj@M0>`#M*B%Y&`ac8935I+9p+k4*a;RzS9921tJ~1(WT07`0t4G^8DaiE9 zG9FpRsEs=&cVNoZF`Z1j;=Ym-z6=gRH-v^XOSX2lr5~(%B8%WTY9MIYnXi zsQM$Q75`0Eu6kp;&2 zJ^|dTuwBLmmXFW}uP&_ph_vFGC`;c+nVCkE!HH9ImQ+^BkE12bm3k_?O+J{1%x)9N z3O{T3EaATxWH%?iFe?ce6!@69uJb8%bdN>*R4>PKi`39{?Z(Lyo`8Gg25TpnM{#4k zFl(u|zvgRzh(R|44;WOS5KdqoAGg^MtvV*pDps>33`(m($m}3+5_3t1lu2MS`oIwG z%lnFs^WJ-{oN*CBXgZDbS+KgBfW%{Vw7*%LU}9v)pM;{MAVQrGtQ z)yY|lG@d43O9LC#SV8w1WBzF(=>4odrzL&u6SOXR+$F62*C9yVVUD>c&l3R-#l|`d zoHJ>;9t7Me=MX^wu&Nr3EQVkEaUD?wU}O0BcY$(MeM0N~ftv=h`yywVgVF14uL(X{ zQxR0=!UlSAup~stLr?GuW!Jm73KDy4PsP907rYbdo*2OvyO0A3@9=~S){8~!YVT9X1mbQBM+E}ri5gxU%)PU!^Zvt6 z1MLRr@fyEj3jcMw^cPe3*OKf%n8FVN@Lh(2I5;_fs!Nuidj0#t|4*jyLv{aWb(yZQ zbDZ(noK54TQl!YQa}S?ZXt8v{j0Gj7@<-ZTg_O}L1T$n! zqM@am&Bj(Y6rLkM`D7&5O9K1Z;}xDmM8{`p#NW^%K&Ycg;Nsn8FhK}&hWmn|;e_DO z_)FMN?+!nB(QB6~>rG>lM+8;$J5|cm24cKU%=`i>u1|b(*S_Hrk415hNS9n%Oe!Oz zNv&s`YgPWucP4e<=+zl`^2J*o2O;zR$bufKX-y2b1OyU@LBNIK@h2{1PgA*D>MU4( zS~%6G0rJIL#!u(tM?^z;>gXT}q{}X@28B>;v;?X^?Xv_Jrp>^)hA8=_P+D;{LNb}uFT3eaIkybT6-M5x-$kEictPDs~ zRYPvGwUmN%&=$fNe)}XYJ30gBwb(7}OBI!dd&&DM3Vmgp5dt)9~O%Ufe{{cA0Y5 zy5CW-L4&siS6*Hfsrwmjk#RQ|2XwH^Tb?<}`Nc!yM^q?svT9hue>(FDr_z#gj)gmK z#J2D5+IB&`pJHv8{)JpWk*@g=OnE82TD=|)zbX3sFOSJ%toShZ%5vxy!cgJ(I zlIu!Y&=qlr@I@^RCRnMx&Mr|DD!xrj)I>a&c;2FE<^0g7$Xt)<4^~X{@QJ>LYRZyD z@U8Mun5%ZbZQ#k_06t%|tVvMaaAH2bZ59Ru<=lU6lz=P7~OsKWm2HUqJawtg&wDkBeUa^SFn?j}>QRlaj8D7b5rSZj%kBa1PJnoR92upB| z98{dxjk?|vA#0r*FgrG3Pasy}&3-6*jKefsS!w;&qrzEm5Xptdsxa3&4!t{HOVRYO zP#>GLePgSPi6Szo(W~07Uhdf}ueq^EOV%rGDyH&&C!AMfp1Z4x&3^az=|C*_=cxP0 zmrF+vnaoom?0k)kZHLv1e6oZM^vAtu&xF@PQzI|#F1;j|vfEPtFTNt@IR=@cI8V~J zTei~p>@we2lc!Cxhwo5*oUbGKxT9p-z?yR^Y2?Up8I1z$>!W(kFJm z!lvt*!d(C~DZ&ofoTY}c;6N_umD#@k$WwoL6x1zd$`=$U_C?E=2Z(ZlSd*_QBNbi@{R_dG07Jfc)~Tn@8TT3M}4#@NmZg{@xwt-GwD zVH+16X1+BZr;2ylXY;l}`*r8ib>WwrsWAHe57iAmgb$9V24}>isfjNC9-Uc!?JoVd z>9`+Z_kS!Bviw{R1c6xo)^RMql@q#;GIaRT9RP)O;EBtGAxjRvGa21+jjMn@>Q4HyGC*KdzgTW{mwU!t?c?ONNW-wx z>-bmDG%bq6r=Viw+&2e04U4(%;67c#CQHm?lMsxS>1IK%Mjr!vnBt3jwlF;=wb@6Z zNw_%d82BAVq9L~3rQ)D6P)NdaJMK8G_?GhVp-VUKpy3%&!3W_C7~tqP0--CnG9sSK z;1peIHlA{Za}GKlYn>6f_Tot1@{PTEW-;B)*>e4bB)j|aiY6?nxk50hR7rPdvb?VPNql^?SH5dEHsdc)^NMg}QDn`MIaN>Z}+^M{h?;NICghvQ; zsd(G-=`9to8%iKaaqBaWcKHM(nRiEs7I9TaMrU?l8CtwZcg7@T3!|^C~tI=$B>~o;K1-{C$ zIrc#a$UM9YP+8dvRk^i-{Pi#j{&<^kAoh#*%5k(ppxvXB&K2z>?4?hG_&0wZ?;(~Xl%ex>tDgCjsA9xZ((DQw%;CGs zs5kgg%BRr`&mFo}Iit9fPhQk7duJS9hfJN7Fobc*GQEn}=Es4s;lFHKI zEMAPDCD)B;;G1LS8>78f&V?ZGIoMJl4bNgA$Dp9)G2Yp|yJz%TK5VPWiUvJXEYZy% zC&(E{+CC0ZJ{;}nUI;x%$gO-J4?WWPa}tSiOoSN(PsjLkvU+{hlsxEGIks5% zd?tcu`&Ig;yDQS2SpKk&$FkyVWW{QG%F4_u`V}ek zrUQ)}sc&lH%l;Ky6^`N{_Zzo)dQv|#)wr6=1u5qC-IkPl#6bmS%-=xj&mE|z+@=4H zjkv!B&kwBppOE@<=M2Qg!Sc7@`6cOoLFx~!^q(O${qOBGA-97V2z-h-bc$U0aVHsH zWSL6#<12#&SgmXZwyLc#CxQtD%Ivlrwdz=9Ln22b@gf0_KcyCByLzf*Q4h@xuK{J|Aw%;}34O_}nH* zIYw`Ed0e-&-8S)_dC5v*i1BBId>S`8_376cWB(%ke)d#|1Oj?H0LkbL1Vh#@vKY+$$eAX=z{OT5s$3ffq*=u@tp&41hkT+)zv1O@|U81Y;&C* z>?zs8k;YG6VZ=uC^%U|&wdYbc>l&Ey$}@&x?6sQP3fNP|Vfwnr1g>b8z#W3gK?umK zxdaHO=na=AiCZj3N1)9HJ5qxb$O1H6}VjAZ#-g z)8~JWG~e)UH16D5?|omxyzonT2IJb&=OpQ3YBVeG?3}T3?^RU8A<%QK>^`!POi%m6 z*4hmk1mruYg$y0!%jt(ZZP!HSRh@u4uG3%No~FlExN(xdQp)uVf7$O#ecJcozCSZU z_w{40%%%ny3DyX81*pD&A^%yG0X%mdOG{!lObUv1-Nvk8;u$!8!CgwN*;Wf87=;?b zn{YQ0gqD(hHpU_~{ngLKXP-dH;5ko_fc)lbW(k@q zpxf?0Azx#R9fVSz37}Te&69L|)BRlW%II85IkBiL3}Q8?4;|Sh5iV9*(N81~FBz+^ zF{=w^xCM0*+B^MA2gEnD3J<*x?$sS#~0w>B3+{&id z_W9wdk!wiS!s*{AV#)N|O#$p|13PKDRRiKX;Y=b7qzythRzlB#P0}iaitX>6Ji}Lw zULOYposD;X%Nq-piYrG+L)qJSZF%9Ur>-2;3&kynat?^8Sjzl{Q4lCSEoq;^ZZ1PT z{VIQhIC@_AHii;lhMP|Wgl4zs8##K-%G2EkMDYU@z>6qooG}S^#!;S#KD?uXKuCCv z*vAqf)VBeys<{r6SG!3~2gwJ+wh2>FC&Z4_`idYL`rTe`7|YmnxXRIEYU{V+{9|*h z0tFzVAd8*I6p}V#qS~H&`Ejb$1)PA~CdwM067cj@%e&?(CrF!DYrQDI{dzXm3VIt) zrKxrY>eH);4?({C42mQPlg_c9E%lJ8nJSf@5h21-J>1gh+rA|9lMjp?E=soo8n3qS z=+gpO3(MUFNdp(=>cjyu_w>0gkqdBPd1U&>SN23<`@nW|1|X5CegL2+5Kp>iF$3cZD1f`HX&(pL`|Tkz=;8rXHt zZ8x{B3x`N4V_>>AgN!?{eOzr9eIYwlQJi|0HRuqkFcrWUC7b-k?)_szYj>uc%myksfUcLXkHMljAXb}ZDQ zx~~sRu_f2c=(B|SKs5WcpIqeIiwBSO?Ox?KYPM(IBaRoSMgImEe{Qq=7XstIj@fA3y4-Dr$MFda1TNq% zKNfJ{6l%?J=0m|k^*$H&{6u4ayX(1qk`=u^R2C16cuDC6#1c*I*=SNKim?xklg_J} zcGsOwDk4~8aQq6EPO4YBHAWZ+1wuh25fmW0kn4$5eYu?|y((ZBB=HW1j&&J4r`YqN zl4E*jzIwjLC8u$9wn~`Jk*C&H!=Z3rw|Jju?2Osl-ZsFbiRoxbh&TYw&KwCgjlU*s zY}~26(kpCG4ZNDLL3q{<@6Br0-gZ%rz8VEBiI*B%-(}U{&`n7gwnJ&rb;H|%qQOf( zc~y!^E!sr9m$UP3Y%6Obp27XA#<*)3%zTG$5QJjw;3is;?t)Q{9Sg_Qxm7B>7!UT^ z{M?jYad8|>?)kZ2HfASqL$S=|f79F!Fa*B!92Po!{)ut)!2Z*M(^|>ydXjKN!zE~r zeX_S(K zJ_nl6XLV_`cxO@!y>!9cR@l}8TBEYW1~0kLbkk=E;Vr`nseyS zk$}N`q>m49Hk0Q&{46mS)+Ru0uhBkRNY~#so0V=>|5jEMnL!|nd{zo3)zK@dKex1d zvXKTC>b=g3bhTI3pIaeN&V%l)5R@yPOJlIZCsYxtJj84!_(3V!dpF_k;B>V{mC8VS z`$4HzvRg=)RTW~oM@r|`FD5gw7ri@4p^wRKkUi2sgm=nMT`%4EWV-huspN%mBesk0 zYtQ%f+n#J`(BpV^yQ;jMgmhWXWh~)tsDONuH!dN9&13{v9zwiuJw|z?uu3?nNhGXv zn_M|+D;iR#c_E_TD8JUciend#N1A`JBL-ebn<*mfzg;f2?u@30TowZeds)p{qTk-u z)NucKw=09- zu)hI#`O+T-*%KzbtCD&j=ukKtY|daX)fEL!gH1bPb}d<2v_YN2fAI)mb)(BEtLONBH^Hxz+rh_CJhhugCJ7V^D*{l~+y2Cnto zLWA6A3nT7hikx*V>Gdsp8>rj9F*axNK5{`}8=||<=XXf5?C_k2?@1TdjRbj=T;m8* zX~`_To35w&Opx}7I6$X4T*6+9o~zOI<1^F&h2$zlZ=>KR6lIX&XK_VM!q>lGgmBnf zk8&ui*$f({%+*oyI}2S})OQ@$rBhkF4v&M-E}m*_h)EQ``ZmnGUe%kzpK|NpJjO^R zx4>)Tm+}m%F6+jk@)Op{>UsQ!;17g|9&&30qR6KbzN?@f;(ZT=D7-2{M~JBhgpd9) zkDA_#SQO!MG?fc0r7F~~-^;H*Tp!f<-LWyl@B0+X`ZwY&_L-m)5x*V43UxkSeEreY zx#Tf<*_ThcEOqhiOe!beS_TMapjH5@754#g{s8OY-yxCpSC+wln^ydRb^k*v{y@4P zn`3Oizt3YnW#s%y5Bh6!jP*y>%72E$bd5EIB}vrRV=q9_V*3ntQ>&j9#TNZV)zY<7 zu85*M?T+Ls7%NqlpvG4oPSR_i*|(&2=m~yAW?A!MIUgx`F#71bImUFXV8xkJJP-{Z z)U$PD<>1xX(P7nAYY+?gNG(kf?dK>QnU#PCUX6MGWq*+Itf?@Lk_oY4o7dlBaVs^a zGN!}!>FDLuS)KX%9b{U0oEQ9K$8a8pSO%rcJ9W7XMr-d2s$qWFQM355{+#gTIF(p3 zj`w-41z$ALdLuw#_8}o}AM=YO^lNcoH%pvzwoNXv0%E;RXjpr zcb`%eCx1(6^F!70O9o%T7?fy{2YU`BO=9p0pYB*&V0vf6-&ZV z7u_N&S0#B`+a9e|BXY?z@?P? z#y+um(TDfof90{$KdrPm{RG~@%T!g5a&&oF%p^C%cyM*Rt3iwbZ9q`q!Oe!^%U%1T z{s7u$#RN{VSSj%v9{{yp!KKR<`r`{qRN~Ecgl-A*zT5Cp_8Y$})5=wwQ?T#}Z5-2) zl?L&KmJxo6XSh%`07b;tOI|FP(J33li{I3aM6yaxZ^l-xqkZ)p*FLGsHZh%^-NIoD zsjbeebZy-5-OlV>7wYl2e?>s$7Ea%U)Ge8&-u=j)9_XQE)60!WBh8H4@2s+oJ3Y{= ze;GP|2)&=698o_&rXFXKaR_T(zR{=@S1`Cz_C^9e92IW*!`&misB!}I;-YEnLay~8 zB5l??Jhjd7w9UpKG%{N1=srhUefE6*ZWc0Wdr`H_oRj-Fc(GOK=o-hmPpqtwJ=CHC z346?imN=uIroc4?IP#_Yz+2PhY*Wt|B?df@(T?8oPC1>bSClo)v0+=;<)|toz4jqP z(>@F)m0>#`1Ds{WmoBuT*oB$^3ahmeF`VPL`;EEWE^m2n;5~1~jeiree{M4U_o{A%QGRBv*S{>4N^Wb~asM-UTA zuMY5Zm4Dr6;?NSMKHARPb`as4Un0Zteiab2#lDT6c1iyMzBm(WuDC}cBIkM;no14M z237ltl=bakpmJ$#4w7mdeAIRFKG{-oX%Nv@R-sQ&j1zCp2cY;sv!TOKS)^0Xjm+7N z?Bu25&}*myx2eUxwhp5?;!=j1k{d{qY8FM0esO!)c$rQ&=YjzZPKln2@v3_& z{>H{fClUI7RvWf#YM48LikBF3Fll20cFlJW_LRLH!e=KXxNh$FmSxnkTmai;Ztvl` zn_~_hzRiLij*Z|m%vr8C%s>(q)xhj6GXINhjg5$P>5Pylh)Z;K0=+;*qtYJ#;iPhP z*I6xU9pI&k&8gxoPnrdLC3N74lv<}54&2Ttz|=w4=Q$CV%BIO~ zp%*9Js>LA@8@sH_!R;r_C0&lE9GBuW>9J(83fc#2iDj23Bdvsp%x-97fy|+C0CsHp zsE6uMk<4DRn97h%CjpX2!^NvC`vcIKg%^w#LJqN!I%y;sanBubm~!IC?nR*clnxpwy@(8o>=^-osLK6reVT}dHr0M0ns=97M$=k1ESButX-p+X`~ad?)6xJn@4 zaM;9@z5@~A^CG0JFVACmJ*ZS|a7ep&Se1U^9Erq={FD0|jKp}WH}@shCatZ3*ge~~ zpL~;vY%=gEg!5Gcb8!XwBBizHN8mVCG%osjd{%E1YJL;hey;WZ7e=-p$vpo|7<$S% z{ul4$Vg>#TY;3>S_Mc@%|1L3@?VoEre~=jbBki8!DGwOP1pqyzcmtoZte!HyL0o^7 z;Qhno-(&@|{X(HXa^<(4{F9phsVD4QKt^^h0OwO$H;@&;0R%B}{Glg5PQ?9tsyf?G z2>Qdcc@7pvW&rzBvOn`vdp{> zEWmHu_MaH~2W`v#G%%hVVdH%IbOs2(#mUJ2lqLU^>c;q#0r)3hXJGqrqUASk{hS8h zTlY`0ZBWv2d_5vN8X3iIt6w@&D-3zt&v;?duHe|D45tDN6S5 zlkyJ|GZHnIqLxKbz1Pak;o+fUk1j^)PlW};!Qe^z!B7HW%k$0hYKc4zJzc8&qjwUR zs_T|mV#o(qn~D_Dxt;eO?8uTk)^u)BDZ)(dQc*bkt!>-1R$6Z5s{%{e$IOfi8s#BK zBEsG&>yn3}l1^CFn)$SSow3ndQMLFq=-;YAYiwK9%a<2vLUXr}acZJY6#iyKpt9L} z-{A^HXTeINfqrBmwa8w}=78y&VKK4ou{uXPn3Ss{A|o99GMqNAUtYGaOftC?029&O zij_aQ3weQLMSGU(HM``?_mF85i!3p#8-cKuXp`NcuJ0048yy*-qoGC-NxXLqLA@R0 z?d;;WRr`=LFViL9ACjRWSl%3j2uI28eBHU2u-3U9!(LbIFfyj@4W@qSk55?f|5bDC z@lb7h{6wyjZl&@nY{@HRX3sk*nK7Q_{V0!8#v{W_Ly}UFPAN_4q=(Z>-K0~=qZC5A zqCAR-p)R2u@8jfp9Fn{C9z!$xe(pWz+`sPpGk#ycwSH^;)?UB0ervD!mS}&& zy`|nT|M>9eK5YxC;a-SjE3agv;C1cO?#a@D$3yr1_7snNYUX<-AJSEbE3>2fLXK(m z!M_aOJ@j;s+Dfm)U>ff8Zvh%f4&8TNO(A)ARgflRy7xeYPb??USnOOm1zt*g#{Z1wU! z&0W4B5N8Y-H`qHs!T1n?2vn$_qP@SpIat464I*%NOoVUi8mPCTaE#M=ah@ z@+(3T*Xf?(t6q|eJRMlB@=-muQrl;(%jr;MXCr1)QD9o$U>DS3PG5CQVDg0=T)xi} ze{(B*BnKuW%^I_;zC6Ni99Qz=89cxD#^AP3&%i_C)-yUS2U|%iUb5svNrzc38BWBm@f+bfzIdp&YHx+?p#-(G0-;Hl-vWE&R#9Gk5F-54pQ_+g$>{>R?F z8~4;Nn|eF)&m_ybY>VlBnA4P-eA9bmyGnP-QcKV5&mNgsf^$nMKD>@Rsp+QYuBY@O z`t=XV)Lj2E#oVC3UmAOx7^Ut}xWnyT`TTnk*2nei?X>KaJq$1GC=K;$?N)ml`8Lg^ zWbxrR9Xg3@QZbyKK6k9RvGR?>4USv;cMDuEHs&Wc9s>R-n zuOaz!u0?AaEpzXC$5CJOWI>Z&`pX{_3znA(Rx2$1UU{R-k&MsY+)345a8c!KseIeT z%xbN^LcxwTsUP;fCbMLCuSWG#%j=J3dDPz0PfRub$xhcQz)Yp&+=mULIr%kZs?U$f zy8Zf0E+R3}>YA*1^_HL(X@}%>`lQskwv_qXZ=X`pJ`h%U%LU3aL<+nD-|6WQiU7 ze9_;EdKSEM%(B;Ly5y|r?-F6NAdbyS9Z2)zXw8fChwKQf7E%4J)2L)X3g zT1kSs1SyUVzx2t*puCKbWeH)owaNF*77%^a3pU?3vF6+Ct2w;nfvZB5&K52?bzjLa ziy>zq<-gd};F-+9-oNhLdBw_ygYP)7s$sY(hZSg5HWA$Ue)uoapB_aLT~&QN4o26W z$Q<>gJZJJ3eu&lb&xeM~@-|pBrB+RJcyU#94Et@izNQ%8RWGQQPKlJd6%R*wtmvq4 zd%~4#buS<~-e&DOBCqc{E?8`1ymxGpt+$9v=RaHN#hu;P!0M-1>`@UJVu>G?!mqV^d*)n$^Lk={WW~!E{)qyXS#*(6Qxg*V^7KTMzr0 zho?aC3B8twd}2RdR)rc*$#%x1YSi=96hB9~Gz+#aJHN@DAM7!pzM$=jU|2Cb?zzbj z-8s9AShmhldPAYkmG5Pw*Q5<>%`MYwX?o_?C8Z%{Z%Uds;6C1Z?8R{BHj?u_t&{OA z-8-dm!x6hWBd#i}XGCmb1=qYX=fypVNvkU|og1JzG&q>GzrEjG5q6nZf3>=ZG;w7f zYuoj0#WmGMVMkjx&2rYTn6pQtR#9VPb%RpNqSzyKCv+b0jqWF8JLqPsj^_P%RDF+T zb55;6p-+uZhlYIn=J2EKrevqPHv@N6wkJ%Ojv2KWQzoTOEZH)S%?ZE)F5)6UH@;4C zBxKhJzR)vVIg)VWO+s(~%y1D9XZ8yJC+FHzWCUn`QFemqOHt^4L+sQ9-EE);-Bft` z-7i_>DPi^`-19L3qXFW8X+|yt#v=&A?T7pqGPYu|BUs^pmkxm~LIH(zP-p-IF8hUU z2n`Do0pI2}#LwCLebRGh}DO4h&xfY;8LnWcUH^K}x#WMNt9#uLZkOXvCNbAvN%zZ<-TPA5js}@j=4*KUK)icRPz61_^_~x0fYAJCOMzoEqq3KlLXbLg$T;OC98O z!Xyz?$0R_2KWmC9gb7kSVZ1^9LxRY{YzMJ`SmtqdI0GU}Ph-F#57_BKfEUfDzUg3k zup+~Sq$V6Thvy7fFIfP}5hh5s1xJLjHi&?$4^x%|j&>yLBO~jict3~~WUZ0FsDS@Z0waUN4GcyBe1VdB z6eKW+*GI);eL$ul;Y31@fKUsGVR#(iIF-O~R00xbB=m?lOj$e`oW@}E0H>h@hNmL0 zB*g0@kPx;baSTsIroT8wpkVxrClQdSD5-}8MR5$w0!*7Jc;x-Hq#kD6@Du_O(+?^c z<13&?mK+Z}m5TKhjfzBTal2G1##ewj5joNn(}Q6e=9vU~7`tF3G2;fxBDcN8>%+km zN&mq(0%k11ksC#_&4B+9^GpH^^X$SnY&-xM4bwL~4q4j7+ed&AvM5Om`Pr8QM!<|Y zOaLcP7(EhMaxB5jr+)Kng!6m>8zygR<#OV1!XaewMAs|pP=5|Iy+&CxAkVb~29AKI z;}~Qdg+{oX@&y0@ literal 0 HcmV?d00001