1    /**
2     * 
3     */
4    package org.torweg.pulse.site.content.admin;
5    
6    import java.io.StringReader;
7    import java.util.GregorianCalendar;
8    import java.util.HashSet;
9    import java.util.List;
10   import java.util.Locale;
11   import java.util.Set;
12   import java.util.TimeZone;
13   import java.util.TreeMap;
14   
15   import net.sf.json.JSONArray;
16   import net.sf.json.JSONObject;
17   
18   import org.hibernate.HibernateException;
19   import org.hibernate.Session;
20   import org.hibernate.Transaction;
21   import org.hibernate.search.FullTextSession;
22   import org.jdom.Document;
23   import org.jdom.input.SAXBuilder;
24   import org.slf4j.Logger;
25   import org.slf4j.LoggerFactory;
26   import org.torweg.pulse.annotations.Action;
27   import org.torweg.pulse.annotations.Groups;
28   import org.torweg.pulse.annotations.Permission;
29   import org.torweg.pulse.annotations.RequireToken;
30   import org.torweg.pulse.bundle.Bundle;
31   import org.torweg.pulse.bundle.Controller;
32   import org.torweg.pulse.configuration.ConfigBean;
33   import org.torweg.pulse.configuration.DeprecatedConfigurable;
34   import org.torweg.pulse.invocation.lifecycle.Lifecycle;
35   import org.torweg.pulse.service.PulseException;
36   import org.torweg.pulse.service.event.JSONOutputEvent;
37   import org.torweg.pulse.service.event.XSLTOutputEvent;
38   import org.torweg.pulse.service.request.Command;
39   import org.torweg.pulse.service.request.Parameter;
40   import org.torweg.pulse.service.request.ServiceRequest;
41   import org.torweg.pulse.site.View;
42   import org.torweg.pulse.site.content.AbstractBasicContent;
43   import org.torweg.pulse.site.content.Attachment;
44   import org.torweg.pulse.site.content.Content;
45   import org.torweg.pulse.site.content.ContentLocalizationMap;
46   import org.torweg.pulse.site.content.InconsistentLocalizationException;
47   import org.torweg.pulse.site.map.SitemapNode;
48   import org.torweg.pulse.util.adminui.FCKEditorResult;
49   import org.torweg.pulse.util.adminui.JSONCommunicationUtils;
50   import org.torweg.pulse.util.adminui.RightsCheckUtils;
51   import org.torweg.pulse.util.time.Duration;
52   import org.torweg.pulse.util.xml.XMLConverter;
53   import org.torweg.pulse.vfs.VirtualFile;
54   
55   /**
56    * provides a set of basic editor methods for {@code AbstractBasicContent}.
57    * 
58    * TODO.
59    * 
60    * @author Daniel Dietz
61    * @version $Revision: 1984 $
62    */
63   public class AbstractBasicContentEditor extends Controller implements
64           DeprecatedConfigurable {
65   
66       /**
67        * the logger.
68        */
69       protected static final Logger LOGGER = LoggerFactory
70               .getLogger(AbstractBasicContentEditor.class);
71   
72       /**
73        * the config bean of the controller.
74        */
75       private AbstractBasicContentEditorConfig config; // NOPMD
76   
77       /**
78        * starts the FCKEditor for the summary of the content determined by the
79        * request.
80        * 
81        * @param bundle
82        *            the current {@code Bundle}
83        * @param request
84        *            the current {@code ServiceRequest}
85        * 
86        * @return the initialized summary-editor
87        */
88       @RequireToken
89       @Action(value = "initSummaryEditor", generate = true)
90       @Permission("viewAbstractBasicContent")
91       @Groups(values = { "ContentAdministrator" })
92       public final FCKEditorResult initSummaryEditor(final Bundle bundle,
93               final ServiceRequest request) {
94   
95           // get contentId from request
96           Long contentId = Long.parseLong(request.getCommand().getParameter("id")
97                   .getFirstValue());
98   
99           // setup
100          Session s = Lifecycle.getHibernateDataSource().createNewSession();
101          Transaction tx = s.beginTransaction();
102          AbstractBasicContent content = null;
103  
104          try {
105  
106              // load the requested content
107              content = (AbstractBasicContent) s.get(Content.class, contentId);
108  
109              tx.commit();
110          } catch (Exception e) {
111              tx.rollback();
112              throw new PulseException(
113                      "AbstractBasicContentContentEditor.initSummaryEditor.failed: "
114                              + e.getLocalizedMessage(), e);
115          } finally {
116              s.close();
117          }
118  
119          // output
120          FCKEditorResult result = new FCKEditorResult();
121          if (content != null) {
122              result.setContentLocale(content.getLocale());
123              result.setFCKContent(content.getSummary());
124          }
125          XSLTOutputEvent event = new XSLTOutputEvent(this.config.getFCKAjaxXSL());
126          event.setStopEvent(true);
127          request.getEventManager().addEvent(event);
128          return result;
129      }
130  
131      /**
132       * 
133       * @param bundle
134       *            the current {@code Bundle}
135       * @param request
136       *            the current {@code ServiceRequest}
137       */
138      @RequireToken
139      @Action(value = "saveSummary", generate = true)
140      @Permission("editAbstractBasicContent")
141      @Groups(values = { "ContentAdministrator" })
142      public final void saveSummary(final Bundle bundle,
143              final ServiceRequest request) {
144  
145          // get contentId from request
146          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
147                  .getFirstValue());
148          String value = request.getCommand().getParameter("value")
149                  .getFirstValue();
150  
151          // setup
152          Session s = Lifecycle.getHibernateDataSource().createNewSession();
153          Transaction tx = s.beginTransaction();
154          AbstractBasicContent content = null;
155          // error : userHasNoEditRightsForLocale (if occurs)
156          JSONObject error = null;
157  
158          try {
159  
160              // load the requested content
161              content = (AbstractBasicContent) s.get(Content.class, contentId);
162  
163              // check user
164              error = RightsCheckUtils.checkUserAgainstLocale(error, bundle,
165                      request, content.getLocale());
166  
167              if (error == null) {
168  
169                  // set the new summary
170                  content.setSummary(buildDocument(value).getRootElement());
171  
172                  // persist
173                  s.saveOrUpdate(content);
174              }
175  
176              // update associated virtual files
177              content.updateAssociatedVirtualFiles();
178  
179              // search-related
180              AbstractBasicContentEditor.initHibernateSearchFix(s, content);
181  
182              tx.commit();
183  
184          } catch (Exception e) {
185              tx.rollback();
186              throw new PulseException(
187                      "AbstractBasicContentEditor.saveSummary.failed"
188                              + e.getLocalizedMessage(), e);
189          } finally {
190              s.close();
191          }
192  
193          // output
194          if (error == null) {
195              JSONCommunicationUtils.jsonSuccessMessage(request);
196          } else {
197              JSONCommunicationUtils.jsonErrorMessage(request, error);
198          }
199      }
200  
201      /**
202       * @param bundle
203       *            the current {@code Bundle}
204       * @param request
205       *            the current {@code ServiceRequest}
206       * @return a result representing the summary of the current content
207       */
208      @RequireToken
209      @Action(value = "loadSummary", generate = true)
210      @Permission("viewAbstractBasicContent")
211      @Groups(values = { "ContentAdministrator" })
212      public final AbstractBasicContentEditorResult loadSummary(
213              final Bundle bundle, final ServiceRequest request) {
214  
215          // get contentId from request
216          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
217                  .getFirstValue());
218  
219          // setup
220          Session s = Lifecycle.getHibernateDataSource().createNewSession();
221          Transaction tx = s.beginTransaction();
222          AbstractBasicContent content = null;
223  
224          try {
225  
226              // load the requested content
227              content = (AbstractBasicContent) s.get(Content.class, contentId);
228  
229              tx.commit();
230  
231          } catch (Exception e) {
232              tx.rollback();
233              throw new PulseException(
234                      "AbstractBasicContentEditor.loadSummary.failed"
235                              + e.getLocalizedMessage(), e);
236          } finally {
237              s.close();
238          }
239  
240          // output
241          AbstractBasicContentEditorResult result = new AbstractBasicContentEditorResult();
242          if (content != null) {
243              result.setContent(content);
244          }
245          XSLTOutputEvent event = new XSLTOutputEvent(
246                  this.config.getAjaxSummaryXSL());
247          event.setStopEvent(true);
248          request.getEventManager().addEvent(event);
249          return result;
250      }
251  
252      /**
253       * starts the {@code ContentLocalizationMap}-editor for a
254       * {@code AbstractBasicContent}.
255       * 
256       * @param bundle
257       *            the current {@code Bundle}
258       * @param request
259       *            the current {@code ServiceRequest}
260       * 
261       * @return an AJAX-result
262       */
263      @RequireToken
264      @Action(value = "initContentLocalizationMap", generate = true)
265      @Permission("viewLocalizationMaps")
266      @Groups(values = { "ContentAdministrator" })
267      public final AbstractBasicContentEditorResult loadContentLocalizationMap(
268              final Bundle bundle, final ServiceRequest request) {
269  
270          // get contentId from request
271          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
272                  .getFirstValue());
273  
274          // setup
275          Session s = Lifecycle.getHibernateDataSource().createNewSession();
276          Transaction tx = s.beginTransaction();
277          AbstractBasicContentEditorResult result = new AbstractBasicContentEditorResult();
278  
279          try {
280  
281              // load the requested content
282              AbstractBasicContent content = (AbstractBasicContent) s.get(
283                      Content.class, contentId);
284  
285              // init ContentLocalizationMap
286              content.getLocalizationMap().keySet().size();
287              content.getLocalizationMap().values().size();
288  
289              result.setContent(content);
290  
291              tx.commit();
292  
293          } catch (Exception e) {
294              tx.rollback();
295              throw new PulseException(
296                      "AbstractBasicContentEditor.loadContentLocalizationMap.failed"
297                              + e.getLocalizedMessage(), e);
298          } finally {
299              s.close();
300          }
301  
302          // output
303          LOGGER.debug("{} ### {} ### ", bundle.getName(), this.config);
304          XSLTOutputEvent event = new XSLTOutputEvent(
305                  this.config.getAjaxContentLocalizationMapXSL());
306          event.setStopEvent(true);
307          request.getEventManager().addEvent(event);
308          return result;
309  
310      }
311  
312      /**
313       * starts the {@code ContentLocalizationMap}-editor for a
314       * {@code AbstractBasicContent}.
315       * 
316       * @param bundle
317       *            the current {@code Bundle}
318       * @param request
319       *            the current {@code ServiceRequest}
320       */
321      @RequireToken
322      @Action(value = "contentLocalizationMapAdd", generate = true)
323      @Permission("editLocalizationMaps")
324      @Groups(values = { "ContentAdministrator" })
325      public final void addToLocalizationMap(final Bundle bundle,
326              final ServiceRequest request) {
327  
328          // retrieve values from request
329          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
330                  .getFirstValue());
331          Long addId = Long.parseLong(request.getCommand().getParameter("addid")
332                  .getFirstValue());
333  
334          // setup
335          Session s = Lifecycle.getHibernateDataSource().createNewSession();
336          Transaction tx = s.beginTransaction();
337  
338          try {
339  
340              // load the content
341              AbstractBasicContent content = (AbstractBasicContent) s.get(
342                      Content.class, contentId);
343  
344              JSONObject error = RightsCheckUtils.checkUserAgainstLocale(bundle,
345                      request, content.getLocale());
346  
347              if (error != null) {
348                  JSONCommunicationUtils.jsonErrorMessage(request, error);
349                  tx.rollback();
350                  return;
351              }
352  
353              // load the add-content
354              AbstractBasicContent addContent = (AbstractBasicContent) s.get(
355                      Content.class, addId);
356  
357              // preliminary checks
358              if (hasLocaleClashes(content, addContent)) {
359                  error = new JSONObject();
360                  error.put("e", "duplicate locales");
361                  JSONCommunicationUtils.jsonErrorMessage(request, error);
362                  tx.rollback();
363                  return;
364              }
365              if (content.getLocale().equals(addContent.getLocale())) {
366                  error = new JSONObject();
367                  error.put("e", "sameLocalesError");
368                  JSONCommunicationUtils.jsonErrorMessage(request, error);
369                  tx.rollback();
370                  return;
371              }
372  
373              try {
374                  buildUnionLocalizationMap(s, tx, content, addContent);
375              } catch (InconsistentLocalizationException e) {
376                  error = new JSONObject();
377                  error.put("e", e.getLocalizedMessage());
378                  JSONCommunicationUtils.jsonErrorMessage(request, error);
379                  tx.rollback();
380                  return;
381              } catch (HibernateException e) {
382                  tx.rollback();
383                  tx = s.beginTransaction();
384                  content = (AbstractBasicContent) s.get(Content.class,
385                          content.getId());
386                  addContent = (AbstractBasicContent) s.get(Content.class,
387                          addContent.getId());
388                  buildUnionLocalizationMap(s, tx, addContent, content);
389              }
390  
391          } catch (Exception e) {
392              tx.rollback();
393              LOGGER.error(e.getLocalizedMessage(), e);
394              JSONObject error = new JSONObject();
395              if (e.getCause() != null) {
396                  error.put("e", e.getMessage() + "<br/>"
397                          + e.getCause().getMessage());
398              } else {
399                  error.put("e", e.getMessage());
400              }
401              JSONCommunicationUtils.jsonErrorMessage(request, error);
402              return;
403          } finally {
404              s.close();
405          }
406  
407          // success output
408          JSONCommunicationUtils.jsonSuccessMessage(request);
409      }
410  
411      /**
412       * @param s
413       *            the current Hibernate session
414       * @param tx
415       *            the current transaction
416       * @param dstContent
417       *            the destination content
418       * @param srcContent
419       *            the source content
420       * @throws InconsistentLocalizationException
421       *             on locale overlaps when building the union localization map
422       * @throws HibernateException
423       *             if the {@code ContentLocalizationMap} which is going to be
424       *             discarded is associated with other entities
425       */
426      private void buildUnionLocalizationMap(final Session s,
427              final Transaction tx, final AbstractBasicContent dstContent,
428              final AbstractBasicContent srcContent) {
429          // get ContentLocalizationMap
430          ContentLocalizationMap locMap = dstContent.getLocalizationMap();
431  
432          // retrieve the src-contents' localization-map, since
433          // it has to be deleted if empty after operation
434          ContentLocalizationMap delLocMap = srcContent.getLocalizationMap();
435  
436          // add the content to the localization-map
437          Set<Content> srcContents = new HashSet<Content>();
438          srcContents.addAll(delLocMap.values());
439          for (Content content : srcContents) {
440              locMap.put(content.getLocale(), content);
441          }
442          s.delete(delLocMap);
443          s.saveOrUpdate(srcContent);
444  
445          tx.commit();
446      }
447  
448      /**
449       * checks if the {@code ContentLocalizationMap}s of the two contents
450       * overlap.
451       * 
452       * @param content1
453       *            the first content
454       * @param content2
455       *            the second content
456       * @return {@code true}, if and only if the two
457       *         {@code ContentLocalizationMap}s have no locales in common.
458       *         Otherwise {@code false}.
459       */
460      private boolean hasLocaleClashes(final AbstractBasicContent content1,
461              final AbstractBasicContent content2) {
462          Set<Locale> c2Locales = new HashSet<Locale>();
463          c2Locales.addAll(content2.getLocalizationMap().keySet());
464          for (Locale c1Locale : content1.getLocalizationMap().keySet()) {
465              if (c2Locales.contains(c1Locale)) {
466                  return true;
467              }
468          }
469          return false;
470      }
471  
472      /**
473       * starts the {@code ContentLocalizationMap}-editor for a
474       * {@code AbstractBasicContent}.
475       * 
476       * @param bundle
477       *            the current {@code Bundle}
478       * @param request
479       *            the current {@code ServiceRequest}
480       */
481      @RequireToken
482      @Action(value = "contentLocalizationMapRemove", generate = true)
483      @Permission("editLocalizationMaps")
484      @Groups(values = { "ContentAdministrator" })
485      public final void removeFromLocalizationMap(final Bundle bundle,
486              final ServiceRequest request) {
487  
488          // retrieve values from request
489          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
490                  .getFirstValue());
491          Long removeId = Long.parseLong(request.getCommand()
492                  .getParameter("removeid").getFirstValue());
493  
494          // setup
495          Session s = Lifecycle.getHibernateDataSource().createNewSession();
496          Transaction tx = s.beginTransaction();
497          // userHasNoEditRightsForLocale : if occurs
498          JSONObject error = null;
499  
500          try {
501  
502              // load the content
503              AbstractBasicContent content = (AbstractBasicContent) s.get(
504                      Content.class, contentId);
505  
506              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
507                      content.getLocale());
508  
509              if (error == null) {
510  
511                  // load the remove-content
512                  AbstractBasicContent removeContent = (AbstractBasicContent) s
513                          .get(Content.class, removeId);
514  
515                  // get ContentLocalizationMap
516                  ContentLocalizationMap locMap = content.getLocalizationMap();
517  
518                  // remove the content from localization-map
519                  locMap.remove(removeContent.getLocale());
520  
521                  // persist
522                  s.saveOrUpdate(removeContent);
523                  s.saveOrUpdate(content);
524  
525                  tx.commit();
526  
527              }
528  
529          } catch (Exception e) {
530              tx.rollback();
531              throw new PulseException(
532                      "AbstractBasicContentEditor.removeFromLocalizationMap.failed"
533                              + e.getLocalizedMessage(), e);
534          } finally {
535              s.close();
536          }
537  
538          // output
539          if (error == null) {
540              JSONCommunicationUtils.jsonSuccessMessage(request);
541          } else {
542              JSONCommunicationUtils.jsonErrorMessage(request, error);
543          }
544  
545      }
546  
547      /**
548       * loads the {@code SitemapNode}s a content is assigned to.
549       * 
550       * @param bundle
551       *            the current {@code Bundle}
552       * @param request
553       *            the current {@code ServiceRequest}
554       */
555      @RequireToken
556      @Action(value = "loadSitemapNodesForContent", generate = true)
557      @Permission("viewAbstractBasicContent")
558      @Groups(values = { "ContentAdministrator" })
559      public final void findSitemapNodesForContent(final Bundle bundle,
560              final ServiceRequest request) {
561  
562          // retrieve values from request
563          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
564                  .getFirstValue());
565  
566          // setup
567          Session s = Lifecycle.getHibernateDataSource().createNewSession();
568          Transaction tx = s.beginTransaction();
569          JSONArray responseArray = new JSONArray();
570          // userHasNoEditRightsForLocale : if occurs
571          JSONObject error = null;
572  
573          try {
574  
575              // load the content
576              AbstractBasicContent content = (AbstractBasicContent) s.get(
577                      Content.class, contentId);
578  
579              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
580                      content.getLocale());
581  
582              if (error == null) {
583  
584                  // load the sitemap-nodes
585                  @SuppressWarnings("unchecked")
586                  List<SitemapNode> sitemapNodes = (List<SitemapNode>) s
587                          .createQuery(
588                                  "from SitemapNode sn where sn.view.content.id=?")
589                          .setLong(0, contentId).list();
590  
591                  // add sitemap-nodes to response-array
592                  for (SitemapNode sn : sitemapNodes) {
593                      JSONObject snJSON = sn.toJSON();
594                      JSONArray expandInfo = buildExpandInfoArray(sn);
595                      snJSON.put("expandInfo", expandInfo);
596                      responseArray.add(snJSON);
597                  }
598  
599              }
600              tx.commit();
601  
602          } catch (Exception e) {
603              tx.rollback();
604              throw new PulseException(
605                      "AbstractBasicContentEditor.findSitemapNodesForContent.failed"
606                              + e.getLocalizedMessage(), e);
607          } finally {
608              s.close();
609          }
610  
611          // output
612          if (error == null) {
613              JSONCommunicationUtils.jsonSuccessMessage(request, "sitemapNodes",
614                      responseArray);
615          } else {
616              JSONCommunicationUtils.jsonErrorMessage(request, error);
617          }
618      }
619  
620      /**
621       * loads the {@code Attachment}s for a {@code AbstractBasicContent}.
622       * 
623       * @param bundle
624       *            the current {@code Bundle}
625       * @param request
626       *            the current {@code ServiceRequest}
627       */
628      @RequireToken
629      @Action(value = "loadAttachmentsForContent", generate = true)
630      @Permission("viewAbstractBasicContent")
631      @Groups(values = { "ContentAdministrator" })
632      public final void loadAttachmentsForContent(final Bundle bundle,
633              final ServiceRequest request) {
634  
635          // retrieve values from request
636          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
637                  .getFirstValue());
638  
639          // setup
640          Session s = Lifecycle.getHibernateDataSource().createNewSession();
641          Transaction tx = s.beginTransaction();
642          JSONArray responseArray = new JSONArray();
643          // userHasNoEditRightsForLocale : if occurs
644          JSONObject error = null;
645  
646          try {
647  
648              // load the content
649              AbstractBasicContent content = (AbstractBasicContent) s.get(
650                      Content.class, contentId);
651  
652              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
653                      content.getLocale());
654  
655              if (error == null) {
656  
657                  // sort attachments alphabetically
658                  TreeMap<String, Attachment> sortMap = new TreeMap<String, Attachment>();
659                  for (Attachment att : content.getAttachments()) {
660                      sortMap.put(att.getName() + "__" + att.getId(), att);
661                  }
662  
663                  // add attachments to response-array
664                  for (Attachment att : sortMap.values()) {
665                      responseArray.add(att.toJSON());
666                  }
667  
668              }
669  
670              tx.commit();
671  
672          } catch (Exception e) {
673              tx.rollback();
674              throw new PulseException(
675                      "AbstractBasicContentEditor.loadAttachmentsForContent.failed"
676                              + e.getLocalizedMessage(), e);
677          } finally {
678              s.close();
679          }
680  
681          // output
682          if (error == null) {
683              request.getEventManager().addEvent(
684                      new JSONOutputEvent(responseArray));
685          } else {
686              JSONCommunicationUtils.jsonErrorMessage(request, error);
687          }
688      }
689  
690      /**
691       * adds an {@code Attachment}to a {@code AbstractBasicContent}.
692       * 
693       * @param bundle
694       *            the current {@code Bundle}
695       * @param request
696       *            the current {@code ServiceRequest}
697       */
698      @RequireToken
699      @Action(value = "addAttachmentToContent", generate = true)
700      @Permission("editAbstractBasicContent")
701      @Groups(values = { "ContentAdministrator" })
702      public final void addAttachmentToContent(final Bundle bundle,
703              final ServiceRequest request) {
704  
705          // retrieve values from request
706          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
707                  .getFirstValue());
708          Long vfsId = Long.parseLong(request.getCommand().getParameter("vfsid")
709                  .getFirstValue());
710          String name = request.getCommand().getParameter("name").getFirstValue();
711  
712          // setup
713          Session s = Lifecycle.getHibernateDataSource().createNewSession();
714          Transaction tx = s.beginTransaction();
715          // userHasNoEditRightsForLocale : if occurs
716          JSONObject error = null;
717  
718          try {
719  
720              // load the content
721              AbstractBasicContent content = (AbstractBasicContent) s.get(
722                      Content.class, contentId);
723  
724              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
725                      content.getLocale());
726  
727              // perform name-check
728              if (error == null && name.equals("")) {
729                  error = new JSONObject();
730                  error.put("e", "nameCannotBeEmty");
731              }
732  
733              if (error == null) {
734  
735                  // load virtual-file and create new attachment
736                  Attachment attachment = new Attachment((VirtualFile) s.get(
737                          VirtualFile.class, vfsId), name);
738  
739                  // add attachment to content
740                  content.getAttachments().add(attachment);
741  
742                  // persist
743                  s.saveOrUpdate(content);
744  
745              }
746  
747              tx.commit();
748  
749          } catch (Exception e) {
750              tx.rollback();
751              throw new PulseException(
752                      "AbstractBasicContentEditor.addAttachmentToContent.failed"
753                              + e.getLocalizedMessage(), e);
754          } finally {
755              s.close();
756          }
757  
758          // output
759          if (error == null) {
760              JSONCommunicationUtils.jsonSuccessMessage(request);
761          } else {
762              JSONCommunicationUtils.jsonErrorMessage(request, error);
763          }
764      }
765  
766      /**
767       * initializes the file-browser for the attachments-editor.
768       * 
769       * <p>
770       * Sets {@code int} 2 as the mode for the result which will cause xsl:choose
771       * to use html/js that builds up the editor for a {@code Attachment}.
772       * </p>
773       * 
774       * @param bundle
775       *            the current {@code Bundle}
776       * @param request
777       *            the current {@code ServiceRequest}
778       * 
779       * @return an AJAX-result: to initialize the file-browser from
780       */
781      @RequireToken
782      @Action(value = "editAttachmentOfContent", generate = true)
783      @Permission("editAbstractBasicContent")
784      @Groups(values = { "ContentAdministrator" })
785      public final AbstractBasicContentEditorResult editAttachmentOfContent(
786              final Bundle bundle, final ServiceRequest request) {
787  
788          // get content-/attachment-id from request
789          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
790                  .getFirstValue());
791          Long attachmentId = Long.parseLong(request.getCommand()
792                  .getParameter("attachmentid").getFirstValue());
793  
794          // setup
795          Session s = Lifecycle.getHibernateDataSource().createNewSession();
796          Transaction tx = s.beginTransaction();
797          AbstractBasicContentEditorResult result = new AbstractBasicContentEditorResult();
798          // userHasNoEditRightsForLocale : if occurs
799          JSONObject error = null;
800  
801          try {
802  
803              // load content
804              AbstractBasicContent content = (AbstractBasicContent) s.get(
805                      Content.class, contentId);
806  
807              // check
808              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
809                      content.getLocale());
810  
811              if (error == null) {
812                  // set content for result
813                  result.setContent(content);
814                  // load the requested attachment and set for result
815                  result.setAttachment((Attachment) s.get(Attachment.class,
816                          attachmentId));
817              }
818  
819              tx.commit();
820  
821          } catch (Exception e) {
822              tx.rollback();
823              throw new PulseException(
824                      "AbstractBasicContentEditor.editAttachmentOfContent.failed: "
825                              + e.getLocalizedMessage(), e);
826          } finally {
827              s.close();
828          }
829  
830          // set attachment-editor-mode for result
831          result.setMode(2);
832  
833          XSLTOutputEvent event = new XSLTOutputEvent(
834                  this.config.getAttachmentsAjaxXSL());
835          event.setStopEvent(true);
836          request.getEventManager().addEvent(event);
837          return result;
838      }
839  
840      /**
841       * removes an {@code Attachment} from the {@code AbstractBasicContent}.
842       * 
843       * @param bundle
844       *            the current {@code Bundle}
845       * @param request
846       *            the current {@code ServiceRequest}
847       */
848      @RequireToken
849      @Action(value = "removeAttachmentFromContent", generate = true)
850      @Permission("editAbstractBasicContent")
851      @Groups(values = { "ContentAdministrator" })
852      public final void removeAttachmentFromContent(final Bundle bundle,
853              final ServiceRequest request) {
854  
855          // get content-/attachment-id from request
856          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
857                  .getFirstValue());
858          Long attachmentId = Long.parseLong(request.getCommand()
859                  .getParameter("attachmentid").getFirstValue());
860  
861          // setup
862          Session s = Lifecycle.getHibernateDataSource().createNewSession();
863          Transaction tx = s.beginTransaction();
864          // userHasNoEditRightsForLocale : if occurs
865          JSONObject error = null;
866  
867          try {
868  
869              // load content
870              AbstractBasicContent content = (AbstractBasicContent) s.get(
871                      Content.class, contentId);
872  
873              // check
874              error = RightsCheckUtils.checkUserAgainstLocale(bundle, request,
875                      content.getLocale());
876  
877              if (error == null) {
878  
879                  // load the attachment requested for deletion
880                  Attachment toDelete = (Attachment) s.get(Attachment.class,
881                          attachmentId);
882  
883                  // remove the attachment from the content
884                  content.getAttachments().remove(toDelete);
885  
886                  // persist
887                  s.saveOrUpdate(content);
888                  s.delete(toDelete);
889  
890              }
891  
892              tx.commit();
893  
894          } catch (Exception e) {
895              tx.rollback();
896              throw new PulseException(
897                      "AbstractBasicContentEditor.removeAttachmentFromContent.failed: "
898                              + e.getLocalizedMessage(), e);
899          } finally {
900              s.close();
901          }
902  
903          // output
904          if (error == null) {
905              JSONCommunicationUtils.jsonSuccessMessage(request);
906          } else {
907              JSONCommunicationUtils.jsonErrorMessage(request, error);
908          }
909      }
910  
911      /**
912       * starts the FCKEditor for the summary of the content determined by the
913       * request.
914       * 
915       * @param bundle
916       *            the current {@code Bundle}
917       * @param request
918       *            the current {@code ServiceRequest}
919       * 
920       * @return the initialized summary-editor
921       */
922      @RequireToken
923      @Action(value = "initAttachmentDescriptionEditor", generate = true)
924      @Permission("editAbstractBasicContent")
925      @Groups(values = { "ContentAdministrator" })
926      public final FCKEditorResult initAttachmentDescriptionEditor(
927              final Bundle bundle, final ServiceRequest request) {
928  
929          // get content-/attachment-id from request
930          Long contentId = Long.parseLong(request.getCommand().getParameter("id")
931                  .getFirstValue());
932          Long attachmentId = Long.parseLong(request.getCommand()
933                  .getParameter("attachmentid").getFirstValue());
934  
935          // setup
936          Session s = Lifecycle.getHibernateDataSource().createNewSession();
937          Transaction tx = s.beginTransaction();
938          AbstractBasicContent content = null;
939          Attachment attachment = null;
940  
941          try {
942  
943              // load the requested content
944              content = (AbstractBasicContent) s.get(Content.class, contentId);
945              attachment = (Attachment) s.get(Attachment.class, attachmentId);
946  
947              tx.commit();
948          } catch (Exception e) {
949              tx.rollback();
950              throw new PulseException(
951                      "AbstractBasicContentEditor.initAttachmentDescriptionEditor.failed: "
952                              + e.getLocalizedMessage(), e);
953          } finally {
954              s.close();
955          }
956  
957          // output
958          FCKEditorResult result = new FCKEditorResult();
959          if (content != null) {
960              result.setContentLocale(content.getLocale());
961              result.setFCKContent(attachment.getDescriptionText());
962          }
963          XSLTOutputEvent event = new XSLTOutputEvent(this.config.getFCKAjaxXSL());
964          event.setStopEvent(true);
965          request.getEventManager().addEvent(event);
966          return result;
967      }
968  
969      /**
970       * starts the FCKEditor for the summary of the content determined by the
971       * request.
972       * 
973       * @param bundle
974       *            the current {@code Bundle}
975       * @param request
976       *            the current {@code ServiceRequest}
977       * 
978       * @return the initialized summary-editor
979       */
980      @RequireToken
981      @Action(value = "loadAttachmentDescription", generate = true)
982      @Permission("editAbstractBasicContent")
983      @Groups(values = { "ContentAdministrator" })
984      public final FCKEditorResult loadAttachmentDescription(final Bundle bundle,
985              final ServiceRequest request) {
986  
987          // get attachment-id from request
988          Long attachmentId = Long.parseLong(request.getCommand()
989                  .getParameter("attachmentid").getFirstValue());
990  
991          // setup
992          Session s = Lifecycle.getHibernateDataSource().createNewSession();
993          Transaction tx = s.beginTransaction();
994          Attachment attachment = null;
995  
996          try {
997  
998              // load the requested attachment
999              attachment = (Attachment) s.get(Attachment.class, attachmentId);
1000 
1001             tx.commit();
1002         } catch (Exception e) {
1003             tx.rollback();
1004             throw new PulseException(
1005                     "AbstractBasicContentEditor.loadAttachmentDescription.failed: "
1006                             + e.getLocalizedMessage(), e);
1007         } finally {
1008             s.close();
1009         }
1010 
1011         // output
1012         FCKEditorResult result = new FCKEditorResult();
1013         if (attachment != null) {
1014             result.setPanelReloadContent(attachment.getDescription());
1015         }
1016         XSLTOutputEvent event = new XSLTOutputEvent(this.config.getFCKAjaxXSL());
1017         event.setStopEvent(true);
1018         request.getEventManager().addEvent(event);
1019         return result;
1020     }
1021 
1022     /**
1023      * 
1024      * @param bundle
1025      *            the current {@code Bundle}
1026      * @param request
1027      *            the current {@code ServiceRequest}
1028      */
1029     @RequireToken
1030     @Action(value = "saveAttachmentDescriptionEditor", generate = true)
1031     @Permission("editAbstractBasicContent")
1032     @Groups(values = { "ContentAdministrator" })
1033     public final void saveAttachmentDescription(final Bundle bundle,
1034             final ServiceRequest request) {
1035 
1036         // get values from request
1037         Long contentId = Long.parseLong(request.getCommand().getParameter("id")
1038                 .getFirstValue());
1039         Long attachmentId = Long.parseLong(request.getCommand()
1040                 .getParameter("attachmentid").getFirstValue());
1041         String value = request.getCommand().getParameter("value")
1042                 .getFirstValue();
1043 
1044         // setup
1045         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1046         Transaction tx = s.beginTransaction();
1047         AbstractBasicContent content = null;
1048         // error : userHasNoEditRightsForLocale (if occurs)
1049         JSONObject error = null;
1050 
1051         try {
1052 
1053             // load the requested content
1054             content = (AbstractBasicContent) s.get(Content.class, contentId);
1055 
1056             // check user
1057             error = RightsCheckUtils.checkUserAgainstLocale(error, bundle,
1058                     request, content.getLocale());
1059 
1060             if (error == null) {
1061 
1062                 // retrieve attachment
1063                 Attachment attachment = (Attachment) s.get(Attachment.class,
1064                         attachmentId);
1065 
1066                 // set the new description
1067                 attachment
1068                         .setDescription(buildDocument(value).getRootElement());
1069 
1070                 // persist
1071                 s.saveOrUpdate(attachment);
1072             }
1073 
1074             tx.commit();
1075 
1076         } catch (Exception e) {
1077             tx.rollback();
1078             throw new PulseException(
1079                     "AbstractBasicContentEditor.saveAttachmentDescriptionEditor.failed"
1080                             + e.getLocalizedMessage(), e);
1081         } finally {
1082             s.close();
1083         }
1084 
1085         // output
1086         if (error == null) {
1087             JSONCommunicationUtils.jsonSuccessMessage(request);
1088         } else {
1089             JSONCommunicationUtils.jsonErrorMessage(request, error);
1090         }
1091     }
1092 
1093     /**
1094      * initializes the file-browser for the attachments-editor.
1095      * 
1096      * <p>
1097      * Sets {@code int} 1 as the mode for the result which will cause xsl:choose
1098      * to use js that builds up the file-browser-panel.
1099      * </p>
1100      * 
1101      * @param bundle
1102      *            the current {@code Bundle}
1103      * @param request
1104      *            the current {@code ServiceRequest}
1105      * 
1106      * @return an AJAX-result: to initialize the file-browser from
1107      */
1108     @RequireToken
1109     @Action(value = "initFileBrowserForAttachmentsForContent", generate = true)
1110     @Permission("editAbstractBasicContent")
1111     @Groups(values = { "ContentAdministrator" })
1112     public final AbstractBasicContentEditorResult initFileBrowserForAttachmentsForContent(
1113             final Bundle bundle, final ServiceRequest request) {
1114 
1115         // get contentId from request
1116         Long contentId = Long.parseLong(request.getCommand().getParameter("id")
1117                 .getFirstValue());
1118 
1119         // setup
1120         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1121         Transaction tx = s.beginTransaction();
1122         AbstractBasicContent content = null;
1123 
1124         try {
1125 
1126             // load the requested content
1127             content = (AbstractBasicContent) s.get(Content.class, contentId);
1128 
1129             tx.commit();
1130         } catch (Exception e) {
1131             tx.rollback();
1132             throw new PulseException(
1133                     "AbstractBasicContentEditor.initFileBrowserForAttachmentsForContent.failed: "
1134                             + e.getLocalizedMessage(), e);
1135         } finally {
1136             s.close();
1137         }
1138 
1139         // output
1140         AbstractBasicContentEditorResult result = new AbstractBasicContentEditorResult();
1141         if (content != null) {
1142             result.setContent(content);
1143         }
1144 
1145         // set file-browser-mode for result
1146         result.setMode(1);
1147 
1148         XSLTOutputEvent event = new XSLTOutputEvent(
1149                 this.config.getAttachmentsAjaxXSL());
1150         event.setStopEvent(true);
1151         request.getEventManager().addEvent(event);
1152         return result;
1153     }
1154 
1155     /**
1156      * Returns a result to initialise the reference-duration-editor.
1157      * 
1158      * @param request
1159      *            the current {@code ServiceRequest}
1160      * 
1161      * @return a {@code ViewEditorResult}
1162      */
1163     @RequireToken
1164     @Action(value = "initReferenceDurationEditor", generate = true)
1165     @Permission("editAbstractBasicContent")
1166     @Groups(values = { "ContentAdministrator" })
1167     public final AbstractBasicContentEditorResult initReferenceDurationEditor(
1168             final ServiceRequest request) {
1169 
1170         AbstractBasicContentEditorResult result = new AbstractBasicContentEditorResult();
1171 
1172         // retrieve the content id from the request
1173         Long contentId = Long.parseLong(request.getCommand().getParameter("id")
1174                 .getFirstValue());
1175 
1176         // setup
1177         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1178         Transaction tx = s.beginTransaction();
1179 
1180         try {
1181 
1182             // load the content that is being edited
1183             Content content = (Content) s.get(Content.class, contentId);
1184 
1185             // fully initialize the content
1186             content.initLazyFields();
1187 
1188             // add the sitemapNode to the result
1189             result.setContent((AbstractBasicContent) content);
1190             result.addTimeZones();
1191 
1192             tx.commit();
1193 
1194         } catch (Exception e) {
1195             tx.rollback();
1196             throw new PulseException(
1197                     "AbstractBasicContentEditor.initReferenceDurationEditor.failed: "
1198                             + e.getLocalizedMessage(), e);
1199         } finally {
1200             s.close();
1201         }
1202 
1203         // output
1204         XSLTOutputEvent event = new XSLTOutputEvent(
1205                 this.config.getAjaxReferenceDurationEditorXSL());
1206         event.setStopEvent(true);
1207         request.getEventManager().addEvent(event);
1208 
1209         return result;
1210     }
1211 
1212     /**
1213      * Removes the reference {@code Duration} from a {@code Content} specified
1214      * by given <tt>id</tt> in request.
1215      * 
1216      * @param request
1217      *            the current {@code ServiceRequest}
1218      */
1219     @RequireToken
1220     @Action(value = "removeReferenceDuration", generate = true)
1221     @Permission("editAbstractBasicContent")
1222     @Groups(values = { "ContentAdministrator" })
1223     public final void removeReferenceDuration(final ServiceRequest request) {
1224 
1225         // retrieve the content id from the request
1226         Long contentId = Long.parseLong(request.getCommand().getParameter("id")
1227                 .getFirstValue());
1228 
1229         // setup
1230         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1231         Transaction tx = s.beginTransaction();
1232 
1233         try {
1234 
1235             // load the sitemapnode that is being edited
1236             Content content = (Content) s.get(Content.class, contentId);
1237 
1238             // remove duration from sitemapnode
1239             if (content != null) {
1240                 content.removeReferenceDuration();
1241                 s.saveOrUpdate(content);
1242             }
1243 
1244             tx.commit();
1245 
1246         } catch (Exception e) {
1247             tx.rollback();
1248             throw new PulseException(
1249                     "AbstractBasicContentEditor.removeReferenceDuration.failed: "
1250                             + e.getLocalizedMessage(), e);
1251         } finally {
1252             s.close();
1253         }
1254 
1255         // output
1256         JSONCommunicationUtils.jsonSuccessMessage(request);
1257     }
1258 
1259     /**
1260      * @param sn
1261      *            the node to retrieve the expand-info for
1262      * 
1263      * @return a JSONArray containing the parent-nodes up until root
1264      */
1265     private JSONArray buildExpandInfoArray(final SitemapNode sn) {
1266         JSONArray expandInfo = new JSONArray();
1267         SitemapNode parent = (SitemapNode) sn.getParent();
1268         int i = 0;
1269         while (parent != null) {
1270             expandInfo.add(i, parent.toJSON());
1271             parent = (SitemapNode) parent.getParent();
1272             i++;
1273         }
1274         return expandInfo;
1275     }
1276 
1277     /**
1278      * convenience.
1279      * 
1280      * @param s
1281      *            the string to use as description
1282      * 
1283      * @return the description as {@code Document}
1284      */
1285     protected Document buildDocument(final String s) {
1286         // setup
1287         Document document;
1288 
1289         try {
1290             if (s != null) {
1291                 document = XMLConverter.getCleanedXHTML(s);
1292             } else {
1293                 document = new SAXBuilder().build(new StringReader(
1294                         "<body></body>"));
1295             }
1296         } catch (Exception e) {
1297             throw new PulseException(e);
1298         }
1299         document.getRootElement().setName("body");
1300 
1301         // debug
1302         if (LOGGER.isTraceEnabled()) {
1303             LOGGER.trace("AbstractBasicContentEditor.buildDocument:\n{}",
1304                     XMLConverter.getPrettyString(document, true));
1305         }
1306         return document;
1307     }
1308 
1309     /**
1310      * sets the {@code AbstractBasicContent}'s basic attributes: name, keywords,
1311      * suffix, title, meta-keywords, meta-description and attachments.
1312      * 
1313      * @param content
1314      *            the content to process
1315      * @param request
1316      *            the current request
1317      * @param s
1318      *            the current {@code Session}
1319      * 
1320      * @return {@code JSONObject} error if occurs
1321      */
1322     public static final JSONObject setBasics(
1323             final AbstractBasicContent content, final ServiceRequest request,
1324             final Session s) {
1325 
1326         // set the name
1327         if (request.getCommand().getParameter("name") != null) {
1328             content.setName(request.getCommand().getParameter("name")
1329                     .getFirstValue().trim());
1330         }
1331 
1332         // set the suffix
1333         if (request.getCommand().getParameter("suffix") != null) {
1334             content.setSuffix(request.getCommand().getParameter("suffix")
1335                     .getFirstValue().trim());
1336         }
1337 
1338         // set the keywords
1339         if (request.getCommand().getParameter("keywords") != null) {
1340             content.setKeywords(request.getCommand().getParameter("keywords")
1341                     .getFirstValue().trim());
1342         }
1343 
1344         // set the title
1345         if (request.getCommand().getParameter("title") != null) {
1346             content.setTitle(request.getCommand().getParameter("title")
1347                     .getFirstValue().trim());
1348         }
1349 
1350         // set the meta-keywords
1351         if (request.getCommand().getParameter("metakeywords") != null) {
1352             content.setMetaKeywords(request.getCommand()
1353                     .getParameter("metakeywords").getFirstValue().trim());
1354         }
1355 
1356         // set the meta-description
1357         if (request.getCommand().getParameter("metadescription") != null) {
1358             content.setMetaDescription(request.getCommand()
1359                     .getParameter("metadescription").getFirstValue().trim());
1360         }
1361 
1362         // process request for attachments
1363         for (String parameterName : request.getCommand().getParameterNames()) {
1364             if (parameterName.length() > 11
1365                     && parameterName.startsWith("attachment_")) {
1366 
1367                 String[] attachmentProperties = parameterName.split("_");
1368 
1369                 // load the attachment
1370                 Attachment attachment = (Attachment) s.get(Attachment.class,
1371                         Long.parseLong(attachmentProperties[1]));
1372 
1373                 // process received value
1374                 boolean hasChanged = proccessAttachmentValue(attachment,
1375                         attachmentProperties[2], request.getCommand()
1376                                 .getParameter(parameterName));
1377 
1378                 if (hasChanged) {
1379                     s.saveOrUpdate(attachment);
1380                 }
1381 
1382             }
1383         }
1384 
1385         // process request for reference duration
1386         return AbstractBasicContentEditor.processDuration(request, content);
1387 
1388     }
1389 
1390     /**
1391      * Processes the given request for a reference {@code Duration} to set for
1392      * the {@code Content}.
1393      * 
1394      * @param request
1395      *            the {@code ServiceRequest}
1396      * @param content
1397      *            the {@code SitemapNode}
1398      * 
1399      * @return a {@code JSONObject}-error if occurs, {@code null} otherwise
1400      */
1401     private static JSONObject processDuration(final ServiceRequest request,
1402             final Content content) {
1403 
1404         LOGGER.error("processing reference duration for content");
1405 
1406         JSONObject error = new JSONObject();
1407 
1408         GregorianCalendar startCal = buildGregCal("start_",
1409                 request.getCommand(), content.getLocale());
1410         if (startCal == null) {
1411             return null;
1412         }
1413 
1414         // GregorianCalendar currentDate = new GregorianCalendar();
1415         // if (currentDate.getTimeInMillis() > startCal.getTimeInMillis()) {
1416         // error.put("e", "startDateBeforeActualDateIsNotAllowed");
1417         // return error;
1418         // }
1419 
1420         GregorianCalendar endCal = buildGregCal("end_", request.getCommand(),
1421                 content.getLocale());
1422         if (endCal == null) {
1423             error.put("e", "noEndDateSpecified");
1424             return error;
1425         }
1426 
1427         if (startCal.getTimeInMillis() > endCal.getTimeInMillis()) {
1428             error.put("e", "startDateAfterEndDateIsNotAllowed");
1429             return error;
1430         }
1431 
1432         LOGGER.error("setting reference duration for content: {}",
1433                 new Duration(startCal.getTime(), endCal.getTime()));
1434 
1435         content.setReferenceDuration(new Duration(startCal.getTime(), endCal
1436                 .getTime()));
1437 
1438         return null;
1439 
1440     }
1441 
1442     /**
1443      * Builds a {@code GregorianCalendar} from the given {@code Command}.
1444      * 
1445      * @param parameterPrefix
1446      *            either "start_" to retrieve the start-calendar or "end_" to
1447      *            retrieve the end-calendar (as prefix for the
1448      *            request-parameters [<tt>YYYY</tt>, <tt>MM</tt>, <tt>DD</tt>,
1449      *            <tt>tz</tt> , <tt>hh</tt>, <tt>mm</tt>, <tt>ss</tt>])
1450      * @param c
1451      *            the {@code Command}
1452      * @param locale
1453      *            the current {@code Locale}
1454      * 
1455      * @return a {@code GregorianCalendar}, {@code null} if any of the required
1456      *         request-parameters is missing
1457      */
1458     private static GregorianCalendar buildGregCal(final String parameterPrefix,
1459             final Command c, final Locale locale) {
1460 
1461         Parameter year = c.getParameter(parameterPrefix + "YYYY");
1462         if (year == null) {
1463             return null;
1464         }
1465         Parameter month = c.getParameter(parameterPrefix + "MM");
1466         if (month == null) {
1467             return null;
1468         }
1469         Parameter date = c.getParameter(parameterPrefix + "DD");
1470         if (date == null) {
1471             return null;
1472         }
1473         Parameter hrs = c.getParameter(parameterPrefix + "hh");
1474         if (hrs == null) {
1475             return null;
1476         }
1477         Parameter mins = c.getParameter(parameterPrefix + "mm");
1478         if (mins == null) {
1479             return null;
1480         }
1481         Parameter secs = c.getParameter(parameterPrefix + "ss");
1482         if (secs == null) {
1483             return null;
1484         }
1485         Parameter timezone = c.getParameter(parameterPrefix + "tz");
1486         if (timezone == null) {
1487             return null;
1488         }
1489 
1490         GregorianCalendar cal = (GregorianCalendar) GregorianCalendar
1491                 .getInstance(TimeZone.getTimeZone(timezone.getFirstValue()),
1492                         locale);
1493 
1494         cal.set(Integer.valueOf(year.getFirstValue()),
1495                 Integer.valueOf(month.getFirstValue()),
1496                 Integer.valueOf(date.getFirstValue()),
1497                 Integer.valueOf(hrs.getFirstValue()),
1498                 Integer.valueOf(mins.getFirstValue()),
1499                 Integer.valueOf(secs.getFirstValue()));
1500 
1501         return cal;
1502     }
1503 
1504     /**
1505      * sets property-changes for an {@code Attachment}.
1506      * 
1507      * @param attachment
1508      *            the attachment
1509      * @param propertyName
1510      *            the name of the property
1511      * @param requestParameter
1512      *            the request-parameter to retrieve the value from
1513      * 
1514      * @return {@code true} if the {@code Attachment} has changed, {@code false}
1515      *         otherwise
1516      */
1517     private static boolean proccessAttachmentValue(final Attachment attachment,
1518             final String propertyName, final Parameter requestParameter) {
1519         boolean hasChanged = false;
1520 
1521         // name change
1522         if (propertyName.equals("name")
1523                 && !requestParameter.getFirstValue().trim().equals("")) {
1524             attachment.setName(requestParameter.getFirstValue().trim());
1525             hasChanged = true;
1526         }
1527 
1528         return hasChanged;
1529     }
1530 
1531     /**
1532      * takes care of updating the search index whenever a content has been
1533      * changed.
1534      * <p>
1535      * This method must be called from subeditors, when a change content is
1536      * persisted.
1537      * </p>
1538      * <p>
1539      * TODO: Check whether this can be done with an interceptor.
1540      * </p>
1541      * 
1542      * @param s
1543      *            the current {@code ServiceSession}
1544      * @param content
1545      *            the changed {@code Content} to be (re)indexed
1546      */
1547     public static final void initHibernateSearchFix(final Session s,
1548             final Content content) {
1549         /* TODO: fix for Hibernate Search (make this more elegant!) */
1550         if (content.getAssociatedViews() != null) {
1551             LOGGER.debug(" >>> Starting Hibernate Search work around...");
1552             for (View v : content.getAssociatedViews()) {
1553                 LOGGER.debug(" >>> >> Processing View '{}' [{}]", v.getName(),
1554                         v.getId());
1555                 if (v.getSitemapNode() != null) {
1556                     ((FullTextSession) s).index(v.getSitemapNode());
1557                     LOGGER.debug(" >>> >> >> Processing SitemapNode '{}' [{}]",
1558                             v.getSitemapNode().getName(), v.getSitemapNode()
1559                                     .getId());
1560                 }
1561             }
1562         }
1563     }
1564 
1565     /**
1566      * configures the {@code Controller}.
1567      * 
1568      * @param c
1569      *            the configuration
1570      */
1571     public void init(final ConfigBean c) {
1572         this.config = (AbstractBasicContentEditorConfig) c;
1573     }
1574 
1575     /**
1576      * Returns the configuration.
1577      * 
1578      * @return the configuration
1579      */
1580     protected final AbstractBasicContentEditorConfig getConfig() {
1581         return this.config;
1582     }
1583 
1584 }
1585