1 /*
2 * Copyright 2006 :torweg free software group
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */17 package org.torweg.pulse.site.content;
18 19 import java.io.IOException;
20 import java.io.StringReader;
21 import java.util.HashSet;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.Set;
25 26 import javax.persistence.Basic;
27 import javax.persistence.Column;
28 import javax.persistence.Lob;
29 import javax.persistence.MappedSuperclass;
30 import javax.persistence.Transient;
31 import javax.xml.bind.JAXBException;
32 import javax.xml.bind.annotation.XmlAccessOrder;
33 import javax.xml.bind.annotation.XmlAccessType;
34 import javax.xml.bind.annotation.XmlAccessorOrder;
35 import javax.xml.bind.annotation.XmlAccessorType;
36 import javax.xml.bind.annotation.XmlElement;
37 import javax.xml.bind.annotation.XmlRootElement;
38 import javax.xml.bind.annotation.XmlTransient;
39 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
40 41 import org.hibernate.LazyInitializationException;
42 import org.jdom.Element;
43 import org.jdom.JDOMException;
44 import org.jdom.input.SAXBuilder;
45 import org.jdom.input.SAXHandler;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 importorg.torweg.pulse.accesscontrol.User;
49 importorg.torweg.pulse.bundle.ExtendedJDOMable;
50 importorg.torweg.pulse.configuration.PoorMansCache;
51 importorg.torweg.pulse.invocation.lifecycle.Lifecycle;
52 importorg.torweg.pulse.service.PulseException;
53 importorg.torweg.pulse.service.request.ServiceRequest;
54 importorg.torweg.pulse.site.View;
55 importorg.torweg.pulse.site.content.util.ILinkCorrectableElement;
56 importorg.torweg.pulse.util.xml.XMLConverter;
57 importorg.torweg.pulse.util.xml.bind.ElementXmlAdapter;
58 59 /**
60 * @author Christian Schatt, Daniel Dietz
61 * @version $Revision: 1984 $
62 */63 @MappedSuperclass
64 @XmlRootElement(name = "abstract-basic-content")
65 @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
66 @XmlAccessorType(XmlAccessType.FIELD)
67 publicabstractclass AbstractBasicContent extendsContentimplements68 ExtendedJDOMable {
69 70 /**
71 * serialVersionUID.
72 */73 privatestaticfinallong serialVersionUID = 6376391145823134097L;
74 75 /**
76 * The {@code Logger} of the {@code AbstractBasicContent}.
77 */78 privatestaticfinal Logger LOGGER = LoggerFactory
79 .getLogger(AbstractBasicContent.class);
80 81 /**
82 * The default suffix of the {@code AbstractBasicContent}.
83 */84 publicstaticfinal String DEFAULT_SUFFIX = "index.html";
85 86 /**
87 * The summary of the {@code AbstractBasicContent}.
88 */89 @Lob
90 @Column(length = 1048576, nullable = false)
91 @XmlTransient
92 private String summary = "<body/>";
93 94 /**
95 * The summary of the {@code AbstractBasicContent} as a JDOM {@code Element}
96 * .
97 */98 @Transient
99 // getter JAXB-annotated
100 privatetransient Element summaryElement = null;
101 102 /**
103 * The keywords of the {@code AbstractBasicContent}.
104 */105 @Basic
106 @XmlElement(name = "keywords")
107 private String keywords = "";
108 109 /**
110 * the title to be used for mark-up languages with a support for document
111 * titles, e.g. HTML.
112 */113 @Basic
114 @XmlElement(name = "title")
115 private String title = null;
116 117 /**
118 * the keywords to be used for mark-up languages with a support for document
119 * keywords, e.g. HTML through meta-keywords.
120 */121 @Basic
122 @XmlElement(name = "meta-keywords")
123 private String metaKeywords = null;
124 125 /**
126 * the description to be used for mark-up languages with a support for
127 * document descriptions, e.g. HTML through meta-descriptions.
128 */129 @Basic
130 @XmlElement(name = "meta-description")
131 private String metaDescription = null;
132 133 /**
134 * The suffix used for {@code Command}s accessing the
135 * {@code AbstractBasicContent}.
136 *
137 * <p>
138 * The default value is {@code DEFAULT_SUFFIX}.
139 * </p>
140 */141 @Basic
142 @XmlElement(name = "suffix")
143 private String suffix = DEFAULT_SUFFIX;
144 145 /**
146 * Returns the summary of the {@code AbstractBasicContent}.
147 *
148 * @return the summary of the {@code AbstractBasicContent}.
149 */150 publicfinal String getSummary() {
151 returnthis.summary;
152 }
153 154 /**
155 * Sets the summary of the {@code AbstractBasicContent} from a JDOM
156 * {@code Element}.
157 *
158 * @param pSummaryElement
159 * the JDOM {@code Element} representing the summary to be set.
160 */161 publicfinalvoid setSummary(final Element pSummaryElement) {
162 this.summary = XMLConverter.getRawString(pSummaryElement, true);
163 this.summaryElement = pSummaryElement;
164 }
165 166 /**
167 * Returns the summary of the {@code AbstractBasicContent} as a JDOM
168 * {@code Element}.
169 *
170 * @return the summary of the {@code AbstractBasicContent} as a JDOM
171 * {@code Element}.
172 */173 publicfinal Element getSummaryElement() {
174 if (getSummary() == null) {
175 returnnull;
176 }
177 if (this.summaryElement == null) {
178 try {
179 this.summaryElement = new SAXBuilder().build(
180 new StringReader(getSummary())).getRootElement();
181 } catch (JDOMException e) {
182 thrownewPulseException("Error parsing XML for summary: "183 + e.getLocalizedMessage(), e);
184 } catch (IOException e) {
185 thrownewPulseException("Error reading XML for summary: "186 + e.getLocalizedMessage(), e);
187 }
188 }
189 return (Element) this.summaryElement.clone();
190 }
191 192 /**
193 * For JAXB only.
194 *
195 * @return this.getSummaryElement()
196 */197 @XmlElement(name = "summary-element")
198 @XmlJavaTypeAdapter(value = ElementXmlAdapter.class)
199 @SuppressWarnings("unused")
200 @Deprecated
201 private Element getSummaryElementJAXB() {
202 try {
203 return getSummaryElement();
204 } catch (LazyInitializationException e) {
205 LOGGER.debug("ignored: {}", e.getLocalizedMessage());
206 returnnull;
207 }
208 }
209 210 /**
211 * returns the title to be used for mark-up languages with a support for
212 * document titles, e.g. HTML.
213 *
214 * @return the title
215 */216 publicfinal String getTitle() {
217 returnthis.title;
218 }
219 220 /**
221 * sets the title to be used for mark-up languages with a support for
222 * document titles, e.g. HTML.
223 *
224 * @param t
225 * the title to set
226 */227 publicfinalvoid setTitle(final String t) {
228 this.title = t;
229 }
230 231 /**
232 * returns the keywords to be used for mark-up languages with a support for
233 * document keywords, e.g. HTML through meta-keywords.
234 *
235 * @return the meta keywords
236 */237 publicfinal String getMetaKeywords() {
238 returnthis.metaKeywords;
239 }
240 241 /**
242 * sets the keywords to be used for mark-up languages with a support for
243 * document keywords, e.g. HTML through meta-keywords.
244 *
245 * @param kw
246 * the meta keywords to set
247 */248 publicfinalvoid setMetaKeywords(final String kw) {
249 this.metaKeywords = kw;
250 }
251 252 /**
253 * returns the description to be used for mark-up languages with a support
254 * for document descriptions, e.g. HTML through meta-descriptions.
255 *
256 * @return the meta description
257 */258 publicfinal String getMetaDescription() {
259 returnthis.metaDescription;
260 }
261 262 /**
263 * sets the keywords to be used for mark-up languages with a support for
264 * document descriptions, e.g. HTML through meta-descriptions.
265 *
266 * @param md
267 * the meta description to set
268 */269 publicfinalvoid setMetaDescription(final String md) {
270 this.metaDescription = md;
271 }
272 273 /**
274 * Returns the suffix of the {@code AbstractBasicContent}.
275 *
276 * @return the suffix of the {@code AbstractBasicContent}.
277 */278 publicfinal String getSuffix() {
279 returnthis.suffix;
280 }
281 282 /**
283 * Sets the suffix of the {@code AbstractBasicContent}.
284 *
285 * <p>
286 * If the given {@code String} contains at least one {@code /}, the part
287 * after its last occurrence will be set as suffix. If the given
288 * {@code String} is {@code null}, the suffix will be set to
289 * {@code DEFAULTSUFFIX}.
290 * </p>
291 *
292 * @param pSuffix
293 * the suffix to be set.
294 */295 publicfinalvoid setSuffix(final String pSuffix) {
296 if (pSuffix == null) {
297 this.suffix = DEFAULT_SUFFIX;
298 return;
299 }
300 AbstractBasicContentConfig config = (AbstractBasicContentConfig) PoorMansCache
301 .getConfig(AbstractBasicContent.class);
302 this.suffix = config.convert(pSuffix.trim(), getLocale());
303 }
304 305 /**
306 * Returns the keywords of the {@code AbstractBasicContent}.
307 *
308 * @return the keywords of the {@code AbstractBasicContent}.
309 */310 publicfinal String getKeywords() {
311 returnthis.keywords;
312 }
313 314 /**
315 * Sets the keywords of the {@code AbstractBasicContent}.
316 *
317 * <p>
318 * If the given {@code String} is longer than 255 characters, it will be
319 * shortened to a maximum length of 255 characters, ending at the last space
320 * character within this range.
321 * </p>
322 *
323 * @param pKeywords
324 * the keywords to be set.
325 */326 publicfinalvoid setKeywords(final String pKeywords) {
327 if (pKeywords == null) {
328 this.keywords = "";
329 return;
330 }
331 if (pKeywords.length() > 255) {
332 this.keywords = pKeywords.substring(0, pKeywords.substring(0, 255)
333 .lastIndexOf(" ") - 1);
334 } else {
335 this.keywords = pKeywords;
336 }
337 }
338 339 /**
340 * Returns the {@code AbstractBasicContent}'s textual information as it is
341 * supposed to be supplied for the index.
342 *
343 * <p>
344 * Implementing classes may override the method to supply more information
345 * for the index.
346 * </p>
347 *
348 * @return the {@code AbstractBasicContent}'s textual information as it is
349 * supposed to be supplied for the index.
350 */351 @Override
352 public StringBuilder getFullTextValue() {
353 StringBuilder fullText = new StringBuilder();
354 fullText.append(getName());
355 String part = getKeywords();
356 if (part != null) {
357 fullText.append(' ').append(part);
358 }
359 part = getSuffix();
360 if (part != null) {
361 fullText.append(' ').append(part);
362 }
363 if (this.summaryElement != null) {
364 fullText.append(' ').append(
365 XMLConverter.getHTMLText(getSummaryElement()));
366 }
367 return fullText;
368 }
369 370 /**
371 * Returns the state of the {@code AbstractBasicContent} as a JDOM
372 * {@code Element}.
373 *
374 * @return the state of the {@code AbstractBasicContent} as a JDOM
375 * {@code Element}.
376 */377 public Element deserializeToJDOM() {
378 return deserializeToJDOM(null);
379 }
380 381 /**
382 * Returns the state of the {@code AbstractBasicContent} as a JDOM
383 * {@code Element}.
384 *
385 * @param pServiceRequest
386 * the current {@code ServiceRequest} or {@code null}.
387 *
388 * @return the state of the {@code AbstractBasicContent} as a JDOM
389 * {@code Element}.
390 */391 public Element deserializeToJDOM(finalServiceRequest pServiceRequest) {
392 Element element = new Element("Content").setAttribute("class",
393 getClass().getCanonicalName()).setAttribute("bundle",
394 getBundle().getName());
395 if (getId() != null) {
396 element.setAttribute("id", getId().toString());
397 }
398 if (getLocale() != null) {
399 element.setAttribute("locale", getLocale().toString());
400 }
401 if (getName() != null) {
402 element.addContent(new Element("name").setText(getName()));
403 }
404 if (getTitle() != null) {
405 element.addContent(new Element("title").setText(getTitle()));
406 }
407 if (getMetaKeywords() != null) {
408 element.addContent(new Element("meta-keywords")
409 .setText(getMetaKeywords()));
410 }
411 if (getMetaDescription() != null) {
412 element.addContent(new Element("meta-description")
413 .setText(getMetaDescription()));
414 }
415 addContentLocalizationMap(element);
416 addViews(element);
417 addAttachments(pServiceRequest, element);
418 if (getSummary() != null) {
419 element.addContent(new Element("summary")
420 .addContent((Element) getSummaryElement().clone()));
421 }
422 if (getKeywords() != null) {
423 element.addContent(new Element("keywords").setText(getKeywords()));
424 }
425 if (getSuffix() != null) {
426 element.addContent(new Element("suffix").setText(getSuffix()));
427 }
428 429 // add creator/created an lastModifier/lastModified
430 element.addContent(new Element("creator").addContent(getCreator()
431 .deserializeToJDOMShallow()));
432 element.addContent(new Element("created").addContent(getCreated()
433 .toString()));
434 element.addContent(new Element("created-millis")
435 .addContent(getCreatedMillis().toString()));
436 element.addContent(new Element("last-modifier")
437 .addContent(getLastModifier().deserializeToJDOMShallow()));
438 element.addContent(new Element("last-modified")
439 .addContent(getLastModified().toString()));
440 441 // add reference duration
442 if (getReferenceDuration() != null) {
443 // node.addContent(getDuration().deserializeToJDOM());
444 SAXHandler jdomSaxHandler = new SAXHandler();
445 try {
446 Lifecycle.getJAXBContext().createMarshaller()
447 .marshal(getReferenceDuration(), jdomSaxHandler);
448 } catch (JAXBException e) {
449 thrownewPulseException("Error: " + e.getLocalizedMessage(), e);
450 }
451 element.addContent(new Element("reference-duration")
452 .addContent(jdomSaxHandler.getDocument()
453 .detachRootElement()));
454 }
455 456 return element;
457 }
458 459 /**
460 * overrides {@link Content#createCopy(Locale, User)}.
461 *
462 * @param l
463 * the locale for the copy
464 * @param u
465 * the user for the copy
466 * @return the copy of the {@code AbstractBasicContent}
467 */468 @Override
469 publicabstractAbstractBasicContent createCopy(Locale l, User u);
470 471 /**
472 * @return the set of {@code ILinkCorrectableElement}s
473 * @see org.torweg.pulse.site.content.util.ILinkCorretable#getLinkCorrectables()
474 */475 public Set<ILinkCorrectableElement> getLinkCorrectables() {
476 Set<ILinkCorrectableElement> correctables = new HashSet<ILinkCorrectableElement>();
477 correctables.add(new SummaryCorrectable(this));
478 return correctables;
479 }
480 481 /**
482 * Adds the {@code Attachment}s of the {@code Content} to the given JDOM
483 * {@code Element}.
484 *
485 * @param pServiceRequest
486 * the current {@code ServiceRequest} or {@code null}.
487 * @param pElement
488 * the JDOM {@code Element} to be filled with data.
489 */490 privatevoid addAttachments(finalServiceRequest pServiceRequest,
491 final Element pElement) {
492 /* get user for extended information */493 User user = null;
494 if (pServiceRequest != null) {
495 user = pServiceRequest.getUser();
496 }
497 Element element = new Element("attachments");
498 try {
499 for (Attachment attachment : getAttachments()) {
500 Element attElem = attachment.deserializeToJDOM();
501 if (pServiceRequest == null) {
502 attElem.setAttribute("canRead", Boolean.toString(attachment
503 .getVirtualFile().canRead(user)));
504 attElem.setAttribute("canWrite", Boolean
505 .toString(attachment.getVirtualFile()
506 .canWrite(user)));
507 }
508 element.addContent(attElem);
509 }
510 } catch (LazyInitializationException e) {
511 LOGGER.trace("adding attachments: {}", e.getMessage());
512 } finally {
513 pElement.addContent(element);
514 }
515 }
516 517 /**
518 * Adds the {@code View}s of the {@code Content} to the given JDOM
519 * {@code Element}.
520 *
521 * @param pElement
522 * the JDOM {@code Element} to be filled with data.
523 */524 privatevoid addViews(final Element pElement) {
525 try {
526 for (View view : getAssociatedViews()) {
527 Element viewElem = new Element(View.class.getSimpleName());
528 viewElem.setAttribute("class", view.getClass().getSimpleName());
529 viewElem.setAttribute("id", view.getId().toString());
530 pElement.addContent(viewElem);
531 }
532 } catch (LazyInitializationException e) {
533 pElement.removeChildren(View.class.getSimpleName());
534 }
535 }
536 537 /**
538 * Adds the {@code ContentLocalizationMap} of the {@code Content} to the
539 * given JDOM {@code Element}.
540 *
541 * @param pElement
542 * the JDOM {@code Element} to be filled with data.
543 */544 privatevoid addContentLocalizationMap(final Element pElement) {
545 try {
546 if (getLocalizationMap() != null) {
547 Element element = new Element(getLocalizationMap().getClass()
548 .getSimpleName());
549 for (Map.Entry<Locale, Content> entry : getLocalizationMap()
550 .entrySet()) {
551 try {
552 element.addContent(new Element("Content")
553 .setAttribute("id",
554 entry.getValue().getId().toString())
555 .setAttribute("locale",
556 entry.getKey().toString())
557 .setAttribute("name",
558 entry.getValue().getName())
559 .setAttribute(
560 "class",
561 entry.getValue().getClass()
562 .getCanonicalName()));
563 } catch (LazyInitializationException e) {
564 continue;
565 }
566 }
567 pElement.addContent(element);
568 }
569 } catch (LazyInitializationException e) {
570 pElement.removeChild(getLocalizationMap().getClass()
571 .getSimpleName());
572 }
573 }
574 575 /**
576 * is an {@code ILinkCorrectableElement} for the summary of the
577 * {@code AbstractBasicContent}.
578 *
579 * @author Thomas Weber
580 */581 privatestaticfinalclass SummaryCorrectable implements582 ILinkCorrectableElement {
583 584 /**
585 * the abstract basic content.
586 */587 privatefinalAbstractBasicContent content;
588 589 /**
590 * creates a new {@code SummaryCorrectable} for the given
591 * {@code AbstractBasicContent}.
592 *
593 * @param c
594 * the content as a back reference
595 */596 public SummaryCorrectable(finalAbstractBasicContent c) {
597 super();
598 this.content = c;
599 }
600 601 /**
602 * @return the element
603 * @see org.torweg.pulse.site.content.util.ILinkCorrectableElement#getElement()
604 */605 public Element getElement() {
606 returnthis.content.getSummaryElement();
607 }
608 609 /**
610 * @param e
611 * the element to set
612 * @see org.torweg.pulse.site.content.util.ILinkCorrectableElement#setElement(org.jdom.Element)
613 */614 publicvoid setElement(final Element e) {
615 this.content.setSummary(e);
616 }
617 618 }
619 }
620