1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
|
16 |
|
|
17 |
|
package org.apache.jetspeed.request; |
18 |
|
|
19 |
|
import java.security.Principal; |
20 |
|
import java.util.Enumeration; |
21 |
|
import java.util.HashMap; |
22 |
|
import java.util.Iterator; |
23 |
|
import java.util.Locale; |
24 |
|
import java.util.Map; |
25 |
|
|
26 |
|
import javax.security.auth.Subject; |
27 |
|
import javax.servlet.ServletConfig; |
28 |
|
import javax.servlet.http.HttpServletRequest; |
29 |
|
import javax.servlet.http.HttpServletRequestWrapper; |
30 |
|
import javax.servlet.http.HttpServletResponse; |
31 |
|
import javax.servlet.http.HttpSession; |
32 |
|
|
33 |
|
import org.apache.jetspeed.Jetspeed; |
34 |
|
import org.apache.jetspeed.PortalReservedParameters; |
35 |
|
import org.apache.jetspeed.aggregator.ContentDispatcher; |
36 |
|
import org.apache.jetspeed.aggregator.ContentDispatcherCtrl; |
37 |
|
import org.apache.jetspeed.capabilities.CapabilityMap; |
38 |
|
import org.apache.jetspeed.container.url.PortalURL; |
39 |
|
import org.apache.jetspeed.engine.servlet.ServletRequestFactory; |
40 |
|
import org.apache.jetspeed.engine.servlet.ServletResponseFactory; |
41 |
|
import org.apache.jetspeed.om.common.MutableLanguage; |
42 |
|
import org.apache.jetspeed.om.impl.LanguageImpl; |
43 |
|
import org.apache.jetspeed.om.page.ContentPage; |
44 |
|
import org.apache.jetspeed.om.page.ContentPageImpl; |
45 |
|
import org.apache.jetspeed.pipeline.Pipeline; |
46 |
|
import org.apache.jetspeed.portalsite.PortalSiteRequestContext; |
47 |
|
import org.apache.jetspeed.portalsite.PortalSiteSessionContext; |
48 |
|
import org.apache.jetspeed.profiler.ProfileLocator; |
49 |
|
import org.apache.jetspeed.profiler.Profiler; |
50 |
|
import org.apache.jetspeed.profiler.impl.ProfilerValveImpl; |
51 |
|
import org.apache.jetspeed.security.SecurityHelper; |
52 |
|
import org.apache.jetspeed.security.UserPrincipal; |
53 |
|
import org.apache.jetspeed.userinfo.UserInfoManager; |
54 |
|
import org.apache.pluto.om.common.Language; |
55 |
|
import org.apache.pluto.om.common.LanguageSet; |
56 |
|
import org.apache.pluto.om.common.ObjectID; |
57 |
|
import org.apache.pluto.om.portlet.PortletDefinition; |
58 |
|
import org.apache.pluto.om.window.PortletWindow; |
59 |
|
|
60 |
|
|
61 |
|
|
62 |
|
|
63 |
|
|
64 |
|
|
65 |
|
|
66 |
|
|
67 |
|
|
68 |
|
|
69 |
|
public class JetspeedRequestContext implements RequestContext |
70 |
|
{ |
71 |
|
private static final String ACTION_ERROR_ATTR = "org.apache.jetspeed.action.error:"; |
72 |
|
private HttpServletRequest request; |
73 |
|
private HttpServletResponse response; |
74 |
|
private ServletConfig config; |
75 |
|
private HttpSession session; |
76 |
|
private Map locators; |
77 |
|
private ContentPage page; |
78 |
|
private PortletDefinition portletDefinition; |
79 |
|
private Subject subject; |
80 |
|
private Locale locale; |
81 |
|
private ContentDispatcher dispatcher; |
82 |
|
private Pipeline pipeline; |
83 |
|
|
84 |
|
private CapabilityMap capabilityMap; |
85 |
|
private String mimeType; |
86 |
|
private String mediaType; |
87 |
|
private PortalURL url; |
88 |
|
private PortletWindow actionWindow; |
89 |
|
private String encoding; |
90 |
0 |
private String requestPath = null; |
91 |
|
|
92 |
|
private UserInfoManager userInfoMgr; |
93 |
|
private Map requestsForWindows; |
94 |
|
private Map responsesForWindows; |
95 |
|
private final Map objects; |
96 |
|
|
97 |
|
|
98 |
|
|
99 |
|
|
100 |
|
|
101 |
|
|
102 |
|
|
103 |
|
|
104 |
|
|
105 |
|
public JetspeedRequestContext( HttpServletRequest request, HttpServletResponse response, ServletConfig config, |
106 |
|
UserInfoManager userInfoMgr ) |
107 |
|
{ |
108 |
0 |
this(request, response, config, userInfoMgr, new HashMap()); |
109 |
0 |
} |
110 |
|
|
111 |
|
public JetspeedRequestContext( HttpServletRequest request, HttpServletResponse response, ServletConfig config, |
112 |
|
UserInfoManager userInfoMgr, Map objects) |
113 |
0 |
{ |
114 |
0 |
this.request = request; |
115 |
0 |
this.response = response; |
116 |
0 |
this.config = config; |
117 |
0 |
this.session = request.getSession(); |
118 |
0 |
this.userInfoMgr = userInfoMgr; |
119 |
0 |
this.requestsForWindows = new HashMap(); |
120 |
0 |
this.responsesForWindows = new HashMap(); |
121 |
0 |
this.objects = objects; |
122 |
|
|
123 |
|
|
124 |
0 |
if (null != this.request) |
125 |
|
{ |
126 |
0 |
this.request.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE, class="keyword">this); |
127 |
0 |
PortalRequestFactory prf = null; |
128 |
|
try |
129 |
|
{ |
130 |
0 |
prf = (PortalRequestFactory)Jetspeed.getComponentManager().getComponent(PortalRequestFactory.class); |
131 |
|
} |
132 |
0 |
catch (Throwable t) |
133 |
|
{ |
134 |
|
|
135 |
0 |
} |
136 |
0 |
if ( prf != null ) |
137 |
|
{ |
138 |
0 |
this.request = prf.createPortalRequest(class="keyword">this.request); |
139 |
|
} |
140 |
|
else |
141 |
|
{ |
142 |
|
|
143 |
|
|
144 |
|
|
145 |
|
|
146 |
0 |
this.request = new HttpServletRequestWrapper(class="keyword">this.request); |
147 |
|
} |
148 |
|
} |
149 |
0 |
} |
150 |
|
|
151 |
|
public HttpServletRequest getRequest() |
152 |
|
{ |
153 |
0 |
return request; |
154 |
|
} |
155 |
|
|
156 |
|
public HttpServletResponse getResponse() |
157 |
|
{ |
158 |
0 |
return response; |
159 |
|
} |
160 |
|
|
161 |
|
public ServletConfig getConfig() |
162 |
|
{ |
163 |
0 |
return config; |
164 |
|
} |
165 |
|
|
166 |
|
public Map getProfileLocators() |
167 |
|
{ |
168 |
0 |
return locators; |
169 |
|
} |
170 |
|
|
171 |
|
public void setProfileLocators( Map locators ) |
172 |
|
{ |
173 |
0 |
this.locators = locators; |
174 |
0 |
} |
175 |
|
|
176 |
|
public ContentPage getPage() |
177 |
|
{ |
178 |
0 |
return this.page; |
179 |
|
} |
180 |
|
|
181 |
|
public void setPage( ContentPage page ) |
182 |
|
{ |
183 |
0 |
this.page = page; |
184 |
0 |
} |
185 |
|
|
186 |
|
public PortletDefinition getPortletDefinition() |
187 |
|
{ |
188 |
0 |
return portletDefinition; |
189 |
|
} |
190 |
|
|
191 |
|
public void setPortletDefinition( PortletDefinition portletDefinition ) |
192 |
|
{ |
193 |
0 |
this.portletDefinition = portletDefinition; |
194 |
0 |
} |
195 |
|
|
196 |
|
public ContentDispatcher getContentDispatcher() |
197 |
|
{ |
198 |
0 |
return dispatcher; |
199 |
|
} |
200 |
|
|
201 |
|
public void setContentDispatcher( ContentDispatcher dispatcher ) |
202 |
|
{ |
203 |
0 |
this.dispatcher = dispatcher; |
204 |
0 |
} |
205 |
|
|
206 |
|
|
207 |
|
|
208 |
|
|
209 |
|
|
210 |
|
public CapabilityMap getCapabilityMap() |
211 |
|
{ |
212 |
0 |
return this.capabilityMap; |
213 |
|
} |
214 |
|
|
215 |
|
|
216 |
|
|
217 |
|
|
218 |
|
|
219 |
|
|
220 |
|
public void setMimeType( String mimeType ) |
221 |
|
{ |
222 |
0 |
this.mimeType = mimeType; |
223 |
0 |
} |
224 |
|
|
225 |
|
|
226 |
|
|
227 |
|
|
228 |
|
|
229 |
|
public String getMimeType() |
230 |
|
{ |
231 |
0 |
return this.mimeType; |
232 |
|
} |
233 |
|
|
234 |
|
|
235 |
|
|
236 |
|
|
237 |
|
|
238 |
|
|
239 |
|
public void setMediaType( String mediaType ) |
240 |
|
{ |
241 |
0 |
this.mediaType = mediaType; |
242 |
0 |
} |
243 |
|
|
244 |
|
|
245 |
|
|
246 |
|
|
247 |
|
|
248 |
|
public String getMediaType() |
249 |
|
{ |
250 |
0 |
return this.mediaType; |
251 |
|
} |
252 |
|
|
253 |
|
|
254 |
|
|
255 |
|
|
256 |
|
|
257 |
|
|
258 |
|
public PortletWindow getActionWindow() |
259 |
|
{ |
260 |
0 |
return actionWindow; |
261 |
|
} |
262 |
|
|
263 |
|
|
264 |
|
|
265 |
|
|
266 |
|
|
267 |
|
|
268 |
|
public void setActionWindow( PortletWindow portletWindow ) |
269 |
|
{ |
270 |
0 |
this.actionWindow = portletWindow; |
271 |
0 |
} |
272 |
|
|
273 |
|
|
274 |
|
|
275 |
|
|
276 |
|
|
277 |
|
|
278 |
|
|
279 |
|
public void setCapabilityMap( CapabilityMap map ) |
280 |
|
{ |
281 |
0 |
this.capabilityMap = map; |
282 |
0 |
} |
283 |
|
|
284 |
|
|
285 |
|
|
286 |
|
|
287 |
|
|
288 |
|
|
289 |
|
public String getCharacterEncoding() |
290 |
|
{ |
291 |
0 |
return this.encoding; |
292 |
|
} |
293 |
|
|
294 |
|
|
295 |
|
|
296 |
|
|
297 |
|
|
298 |
|
|
299 |
|
public void setCharacterEncoding( String enc ) |
300 |
|
{ |
301 |
0 |
String preferedEnc = (String) session.getAttribute(PortalReservedParameters.PREFERED_CHARACTERENCODING_ATTRIBUTE); |
302 |
|
|
303 |
0 |
if (preferedEnc == null || !enc.equals(preferedEnc)) |
304 |
|
{ |
305 |
0 |
request.setAttribute(PortalReservedParameters.PREFERED_CHARACTERENCODING_ATTRIBUTE, enc); |
306 |
|
} |
307 |
|
|
308 |
0 |
this.encoding = enc; |
309 |
0 |
} |
310 |
|
|
311 |
|
|
312 |
|
|
313 |
|
|
314 |
|
|
315 |
|
|
316 |
|
|
317 |
|
|
318 |
|
|
319 |
|
|
320 |
|
public HttpServletRequest getRequestForWindow( PortletWindow window ) |
321 |
|
{ |
322 |
0 |
if (!requestsForWindows.containsKey(window.getId())) |
323 |
|
{ |
324 |
0 |
ServletRequestFactory reqFac = (ServletRequestFactory) Jetspeed.getEngine().getFactory( |
325 |
|
javax.servlet.http.HttpServletRequest.class); |
326 |
0 |
HttpServletRequest requestWrapper = reqFac.getServletRequest(request, window); |
327 |
0 |
requestsForWindows.put(window.getId(), requestWrapper); |
328 |
0 |
return requestWrapper; |
329 |
|
} |
330 |
|
else |
331 |
|
{ |
332 |
0 |
return (HttpServletRequest) requestsForWindows.get(window.getId()); |
333 |
|
} |
334 |
|
|
335 |
|
} |
336 |
|
|
337 |
|
|
338 |
|
|
339 |
|
|
340 |
|
|
341 |
|
|
342 |
|
|
343 |
|
|
344 |
|
|
345 |
|
|
346 |
|
public HttpServletResponse getResponseForWindow( PortletWindow window ) |
347 |
|
{ |
348 |
0 |
HttpServletResponse wrappedResponse = null; |
349 |
|
|
350 |
0 |
if (!responsesForWindows.containsKey(window.getId())) |
351 |
|
{ |
352 |
0 |
if (getContentDispatcher() != null) |
353 |
|
{ |
354 |
0 |
wrappedResponse = ((ContentDispatcherCtrl) getContentDispatcher()).getResponseForWindow(window, this); |
355 |
|
} |
356 |
|
else |
357 |
|
{ |
358 |
0 |
ServletResponseFactory rspFac = (ServletResponseFactory) Jetspeed.getEngine().getFactory( |
359 |
|
HttpServletResponse.class); |
360 |
0 |
wrappedResponse = rspFac.getServletResponse(this.response); |
361 |
|
|
362 |
|
} |
363 |
|
|
364 |
0 |
responsesForWindows.put(window.getId(), wrappedResponse); |
365 |
0 |
return wrappedResponse; |
366 |
|
|
367 |
|
} |
368 |
|
else |
369 |
|
{ |
370 |
0 |
return (HttpServletResponse) responsesForWindows.get(window.getId()); |
371 |
|
} |
372 |
|
} |
373 |
|
|
374 |
|
|
375 |
|
|
376 |
|
|
377 |
|
public Subject getSubject() |
378 |
|
{ |
379 |
0 |
return this.subject; |
380 |
|
} |
381 |
|
|
382 |
|
public Principal getUserPrincipal() |
383 |
|
{ |
384 |
0 |
return SecurityHelper.getBestPrincipal(getSubject(), UserPrincipal.class); |
385 |
|
} |
386 |
|
|
387 |
|
|
388 |
|
|
389 |
|
|
390 |
|
public void setSubject( Subject subject ) |
391 |
|
{ |
392 |
0 |
this.subject = subject; |
393 |
0 |
} |
394 |
|
|
395 |
|
|
396 |
|
|
397 |
|
|
398 |
|
public Locale getLocale() |
399 |
|
{ |
400 |
0 |
return this.locale; |
401 |
|
} |
402 |
|
|
403 |
|
|
404 |
|
|
405 |
|
|
406 |
|
public void setLocale( Locale locale ) |
407 |
|
{ |
408 |
0 |
Locale preferedLocale = (Locale) session.getAttribute(PortalReservedParameters.PREFERED_LOCALE_ATTRIBUTE); |
409 |
|
|
410 |
0 |
if (preferedLocale == null || !locale.equals(preferedLocale)) |
411 |
|
{ |
412 |
|
|
413 |
|
|
414 |
|
|
415 |
0 |
session.setAttribute(PortalReservedParameters.PREFERED_LOCALE_ATTRIBUTE, locale); |
416 |
0 |
request.setAttribute(PortalReservedParameters.PREFERED_LOCALE_ATTRIBUTE, locale); |
417 |
|
} |
418 |
|
|
419 |
0 |
this.locale = locale; |
420 |
0 |
} |
421 |
|
|
422 |
|
|
423 |
|
|
424 |
|
|
425 |
|
public String getRequestParameter( String key ) |
426 |
|
{ |
427 |
0 |
return request.getParameter(key); |
428 |
|
} |
429 |
|
|
430 |
|
public void setRequestParameter(String key, String value) |
431 |
|
{ |
432 |
0 |
request.getParameterMap().put(key, value); |
433 |
0 |
} |
434 |
|
|
435 |
|
|
436 |
|
|
437 |
|
|
438 |
|
public Map getParameterMap() |
439 |
|
{ |
440 |
0 |
return request.getParameterMap(); |
441 |
|
} |
442 |
|
|
443 |
|
|
444 |
|
|
445 |
|
|
446 |
|
public Object getRequestAttribute( String key ) |
447 |
|
{ |
448 |
0 |
return request.getAttribute(key); |
449 |
|
} |
450 |
|
|
451 |
|
|
452 |
|
|
453 |
|
|
454 |
|
public Object getSessionAttribute( String key ) |
455 |
|
{ |
456 |
0 |
return session.getAttribute(key); |
457 |
|
} |
458 |
|
|
459 |
|
|
460 |
|
|
461 |
|
|
462 |
|
|
463 |
|
public void setSessionAttribute( String key, Object value ) |
464 |
|
{ |
465 |
0 |
session.setAttribute(key, value); |
466 |
0 |
} |
467 |
|
|
468 |
|
|
469 |
|
|
470 |
|
|
471 |
|
|
472 |
|
public void setAttribute( String key, Object value ) |
473 |
|
{ |
474 |
0 |
request.setAttribute(key, value); |
475 |
0 |
} |
476 |
|
|
477 |
|
|
478 |
|
|
479 |
|
|
480 |
|
public Object getAttribute( String key ) |
481 |
|
{ |
482 |
0 |
return request.getAttribute(key); |
483 |
|
} |
484 |
|
|
485 |
|
|
486 |
|
|
487 |
|
|
488 |
|
public String getPath() |
489 |
|
{ |
490 |
0 |
if (this.requestPath == null) |
491 |
|
{ |
492 |
0 |
this.requestPath = getPortalURL().getPath(); |
493 |
|
} |
494 |
0 |
return this.requestPath; |
495 |
|
} |
496 |
|
|
497 |
|
public void setPortalURL(PortalURL url) |
498 |
|
{ |
499 |
0 |
if ( this.url != null ) |
500 |
0 |
throw new IllegalStateException("PortalURL already set"); |
501 |
0 |
if ( url == null ) |
502 |
0 |
throw new IllegalArgumentException("PortalURL may not be nullified"); |
503 |
0 |
this.url = url; |
504 |
0 |
} |
505 |
|
|
506 |
|
public PortalURL getPortalURL() |
507 |
|
{ |
508 |
0 |
return url; |
509 |
|
} |
510 |
|
|
511 |
|
|
512 |
|
|
513 |
|
|
514 |
|
public Map getUserInfoMap( ObjectID oid ) |
515 |
|
{ |
516 |
0 |
return userInfoMgr.getUserInfoMap(oid, this); |
517 |
|
} |
518 |
|
|
519 |
|
|
520 |
|
|
521 |
|
|
522 |
|
|
523 |
|
|
524 |
|
|
525 |
|
|
526 |
|
|
527 |
|
|
528 |
|
|
529 |
|
public Language getPreferedLanguage( PortletDefinition portlet ) |
530 |
|
{ |
531 |
|
|
532 |
|
|
533 |
|
|
534 |
|
|
535 |
|
|
536 |
|
|
537 |
|
|
538 |
|
|
539 |
|
|
540 |
|
|
541 |
0 |
LanguageSet languageSet = portlet.getLanguageSet(); |
542 |
0 |
Language language = languageSet.get(locale); |
543 |
|
|
544 |
0 |
Enumeration locales = request.getLocales(); |
545 |
0 |
while (locales.hasMoreElements() && language == null) |
546 |
|
{ |
547 |
0 |
Locale aLocale = (Locale) locales.nextElement(); |
548 |
0 |
language = languageSet.get(aLocale); |
549 |
0 |
} |
550 |
|
|
551 |
0 |
Iterator langItr = languageSet.iterator(); |
552 |
0 |
if (langItr.hasNext() && language == null) |
553 |
|
{ |
554 |
0 |
language = (Language) langItr.next(); |
555 |
|
} |
556 |
|
|
557 |
0 |
if (language == null) |
558 |
|
{ |
559 |
0 |
language = languageSet.get(languageSet.getDefaultLocale()); |
560 |
|
} |
561 |
|
|
562 |
0 |
if (language == null) |
563 |
|
{ |
564 |
0 |
MutableLanguage languageCtl = new LanguageImpl(); |
565 |
0 |
languageCtl.setLocale(locale); |
566 |
0 |
languageCtl.setShortTitle(portlet.getName()); |
567 |
0 |
languageCtl.setTitle(portlet.getName()); |
568 |
0 |
language = languageCtl; |
569 |
|
} |
570 |
|
|
571 |
|
|
572 |
0 |
return language; |
573 |
|
} |
574 |
|
|
575 |
|
|
576 |
|
|
577 |
|
|
578 |
|
|
579 |
|
|
580 |
|
|
581 |
|
|
582 |
|
|
583 |
|
public void setPath( String path ) |
584 |
|
{ |
585 |
0 |
this.requestPath = path; |
586 |
0 |
} |
587 |
|
|
588 |
|
|
589 |
|
|
590 |
|
|
591 |
|
|
592 |
|
public Throwable popActionFailure(PortletWindow window) |
593 |
|
{ |
594 |
|
|
595 |
0 |
String key = ACTION_ERROR_ATTR + window.getId(); |
596 |
0 |
Throwable t = (Throwable) session.getAttribute(key); |
597 |
0 |
session.removeAttribute(key); |
598 |
0 |
return t; |
599 |
|
|
600 |
|
} |
601 |
|
|
602 |
|
|
603 |
|
|
604 |
|
|
605 |
|
|
606 |
|
|
607 |
|
|
608 |
|
public void setActionFailure(PortletWindow window, Throwable actionFailure) |
609 |
|
{ |
610 |
0 |
setSessionAttribute(ACTION_ERROR_ATTR + window.getId(), |
611 |
|
actionFailure); |
612 |
0 |
} |
613 |
|
|
614 |
|
|
615 |
|
|
616 |
|
|
617 |
|
|
618 |
|
|
619 |
|
public Pipeline getPipeline() |
620 |
|
{ |
621 |
0 |
return pipeline; |
622 |
|
} |
623 |
|
|
624 |
|
|
625 |
|
|
626 |
|
|
627 |
|
|
628 |
|
|
629 |
|
public void setPipeline(Pipeline pipeline) |
630 |
|
{ |
631 |
0 |
this.pipeline = pipeline; |
632 |
0 |
} |
633 |
|
|
634 |
|
|
635 |
|
|
636 |
|
|
637 |
|
|
638 |
|
public void setRequest(HttpServletRequest request) |
639 |
|
{ |
640 |
0 |
this.request = request; |
641 |
0 |
} |
642 |
|
|
643 |
|
|
644 |
|
|
645 |
|
|
646 |
|
public void setResponse(HttpServletResponse response) |
647 |
|
{ |
648 |
0 |
this.response = response; |
649 |
0 |
} |
650 |
|
|
651 |
|
public ContentPage locatePage(Profiler profiler, String nonProfiledPath) |
652 |
|
{ |
653 |
|
try |
654 |
|
{ |
655 |
0 |
String pathSave = this.getPath(); |
656 |
0 |
this.setPath(nonProfiledPath); |
657 |
0 |
ContentPage realPage = this.getPage(); |
658 |
0 |
this.setPage(null); |
659 |
0 |
Map locators = null; |
660 |
0 |
ProfileLocator locator = profiler.getProfile(this, ProfileLocator.PAGE_LOCATOR); |
661 |
0 |
if ( locator != null ) |
662 |
|
{ |
663 |
0 |
locators = new HashMap(); |
664 |
0 |
locators.put(ProfileLocator.PAGE_LOCATOR, locator); |
665 |
|
} |
666 |
0 |
PortalSiteSessionContext sessionContext = (PortalSiteSessionContext)getSessionAttribute(ProfilerValveImpl.PORTAL_SITE_SESSION_CONTEXT_ATTR_KEY); |
667 |
0 |
PortalSiteRequestContext requestContext = sessionContext.newRequestContext(locators, true, class="keyword">true); |
668 |
0 |
ContentPage cpage = new ContentPageImpl(requestContext.getManagedPage()); |
669 |
|
|
670 |
0 |
this.setPage(realPage); |
671 |
0 |
this.setPath(pathSave); |
672 |
0 |
return cpage; |
673 |
|
} |
674 |
0 |
catch (Throwable t) |
675 |
|
{ |
676 |
0 |
t.printStackTrace(); |
677 |
|
} |
678 |
0 |
return null; |
679 |
|
} |
680 |
|
|
681 |
|
public Map getObjects() |
682 |
|
{ |
683 |
0 |
return objects; |
684 |
|
} |
685 |
|
} |