1    /*
2     * Copyright 2008 :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    */
18   package org.torweg.pulse.component.core.accesscontrol.attributes.admin;
19   
20   import java.util.ArrayList;
21   import java.util.List;
22   import java.util.Map;
23   import java.util.Set;
24   import java.util.TreeMap;
25   
26   import net.sf.json.JSONArray;
27   import net.sf.json.JSONObject;
28   
29   import org.hibernate.Criteria;
30   import org.hibernate.Session;
31   import org.hibernate.Transaction;
32   import org.hibernate.criterion.Criterion;
33   import org.hibernate.criterion.Order;
34   import org.hibernate.criterion.Restrictions;
35   import org.slf4j.Logger;
36   import org.slf4j.LoggerFactory;
37   import org.torweg.pulse.accesscontrol.Role;
38   import org.torweg.pulse.accesscontrol.attributes.AbstractAttribute;
39   import org.torweg.pulse.accesscontrol.attributes.AbstractTypedCheck;
40   import org.torweg.pulse.accesscontrol.attributes.AttributeFactory;
41   import org.torweg.pulse.accesscontrol.attributes.AttributeRegistry;
42   import org.torweg.pulse.accesscontrol.attributes.BundleAttribute;
43   import org.torweg.pulse.annotations.Action;
44   import org.torweg.pulse.annotations.Groups;
45   import org.torweg.pulse.annotations.Permission;
46   import org.torweg.pulse.annotations.RequireToken;
47   import org.torweg.pulse.bundle.Bundle;
48   import org.torweg.pulse.bundle.Result;
49   import org.torweg.pulse.component.core.accesscontrol.admin.AbstractAccessControlEditor;
50   import org.torweg.pulse.configuration.ConfigBean;
51   import org.torweg.pulse.configuration.DeprecatedConfigurable;
52   import org.torweg.pulse.invocation.lifecycle.Lifecycle;
53   import org.torweg.pulse.service.PulseException;
54   import org.torweg.pulse.service.event.JSONOutputEvent;
55   import org.torweg.pulse.service.event.XSLTOutputEvent;
56   import org.torweg.pulse.service.request.ServiceRequest;
57   import org.torweg.pulse.util.adminui.JSONCommunicationUtils;
58   import org.torweg.pulse.util.adminui.RegistryEditorResult;
59   import org.torweg.pulse.util.entity.Node;
60   
61   /**
62    * the main-controller of the {@code AttributeRegistry}.
63    * 
64    * @author Daniel Dietz
65    * @version $Revision: 2013 $
66    */
67   public class AttributeRegistryEditor extends AbstractAccessControlEditor
68           implements DeprecatedConfigurable {
69   
70       /**
71        * the logger.
72        */
73       private static final Logger LOGGER = LoggerFactory
74               .getLogger(AttributeRegistryEditor.class);
75   
76       /**
77        * the {@code ConfigBean}.
78        */
79       private AttributeRegistryEditorConfig config;
80   
81       /**
82        * @param bundle
83        *            the current {@code Bundle}
84        * @param request
85        *            the current {@code ServiceRequest}
86        * @return the initialization {@code Result} for the
87        *         {@code ContentRegistryEditor}
88        */
89       @RequireToken
90       @Action(value = "attributeRegistryTreeInit", generate = true)
91       @Permission("useAttributeRegistry")
92       @Groups(values = { "AttributeRegistryAdministrator" })
93       public final Result initEditor(final Bundle bundle,
94               final ServiceRequest request) {
95           AttributeRegistryEditorResult result = new AttributeRegistryEditorResult();
96           XSLTOutputEvent event = new XSLTOutputEvent(this.config.getAjaxXSL());
97           event.setStopEvent(true);
98           request.getEventManager().addEvent(event);
99           return result;
100  
101      }
102  
103      /**
104       * TODO: add a clear description!
105       * 
106       * @param request
107       *            the current {@code ServiceRequest}
108       * @return a JSON-response
109       */
110      @RequireToken
111      @Action(value = "browseAttributeRegistryEdit", generate = true)
112      @Permission("browseAttributeRegistryEdit")
113      @Groups(values = { "AttributeRegistryAdministrator" })
114      public final Result browseAttributeRegistryEdit(final ServiceRequest request) {
115  
116          return browseAttributeRegistry(request, true);
117  
118      }
119  
120      /**
121       * TODO: add a clear description!
122       * 
123       * @param request
124       *            the current {@code ServiceRequest}
125       * @return a JSON-response
126       */
127      @RequireToken
128      @Action(value = "browseAttributeRegistrySelect", generate = true)
129      @Permission("browseAttributeRegistrySelect")
130      @Groups(values = { "AttributeRegistryAdministrator" })
131      public final Result browseAttributeRegistrySelect(
132              final ServiceRequest request) {
133  
134          return browseAttributeRegistry(request, false);
135  
136      }
137  
138      /**
139       * TODO.
140       * 
141       * @param request
142       *            the current request
143       * @param withEditURLs
144       *            flag, if true the edit-urls of the attribute will be added to
145       *            the json
146       * 
147       * @return a JSON-response
148       */
149      private Result browseAttributeRegistry(final ServiceRequest request,
150              final boolean withEditURLs) {
151          // retrieve node-id from request
152          String nodeid = request.getCommand().getParameter("node")
153                  .getFirstValue();
154  
155          // setup
156          Session s = Lifecycle.getHibernateDataSource().createNewSession();
157          Transaction tx = s.beginTransaction();
158          JSONArray attributeNodes = new JSONArray();
159  
160          try {
161  
162              if (nodeid.length() > 5 && nodeid.substring(0, 6).equals("source")) {
163  
164                  // process root
165                  attributeNodes = processAttributeRegistryRoot(request, s,
166                          withEditURLs);
167  
168              } else {
169  
170                  // load the attribute (as Node) which is being browsed
171                  Node attributeNode = (Node) s.get(Node.class,
172                          Long.parseLong(nodeid));
173  
174                  List<? extends Node> nodeList = (List<? extends Node>) attributeNode
175                          .getChildren();
176  
177                  // Map<String, JSONObject> nodeMap = buildNodeMap(request,
178                  // nodeList, withEditURLs);
179  
180                  List<JSONObject> jsonNodeList = buildNodeList(request,
181                          nodeList, withEditURLs);
182  
183                  attributeNodes.addAll(jsonNodeList);
184  
185              }
186  
187              tx.commit();
188  
189          } catch (Exception e) {
190              tx.rollback();
191              throw new PulseException(
192                      "Core.AttributeRegistry.browseAttributeRegistryEdit.failed: "
193                              + e.getLocalizedMessage(), e);
194          } finally {
195              s.close();
196          }
197  
198          // output
199          request.getEventManager().addEvent(new JSONOutputEvent(attributeNodes));
200  
201          return null;
202      }
203  
204      /**
205       * return a {@code JSONArray} with the root-nodes of the attribute-registry
206       * according to request.
207       * 
208       * <p>
209       * If request contains parameter: "bundle" will return
210       * attribute-registry-root-node for matching (compared by name) bundle only.
211       * 
212       * if the given bundle-name does not match existing bundle an empty
213       * JSONArray is returned.
214       * </p>
215       * 
216       * @param request
217       *            the current {@code ServiceRequest}
218       * @param s
219       *            the current {@code Session}
220       * @param withEditURLs
221       *            if true the edit-urls of the attribute will be added to the
222       *            json
223       * 
224       * @return a {@code JSONArray} with the root-nodes of the
225       *         attribute-registry.
226       */
227      private JSONArray processAttributeRegistryRoot(
228              final ServiceRequest request, final Session s,
229              final boolean withEditURLs) {
230  
231          // given parameter bundle ->
232          // returns attribute-registry only for requested bundle
233          String bundleString = null;
234          if (request.getCommand().getParameter("bundle") != null) {
235              bundleString = request.getCommand().getParameter("bundle")
236                      .getFirstValue();
237          }
238  
239          // load AttributeRegistry.rootNodes (BundleAttribute)
240          AttributeRegistry attributeRegistry = (AttributeRegistry) s.get(
241                  AttributeRegistry.class, 1L);
242  
243          List<BundleAttribute> nodeList = new ArrayList<BundleAttribute>(
244                  attributeRegistry.getRootNodes());
245  
246          Map<String, JSONObject> nodeMap = new TreeMap<String, JSONObject>();
247  
248          // sort bundle-nodes
249          for (BundleAttribute bundleAttribute : nodeList) {
250  
251              JSONObject nodeJSONObject = bundleAttribute.toJSON();
252              nodeJSONObject.put("draggable", false);
253              nodeJSONObject.put("allowDrop", true);
254  
255              // add createURL to node --> AttributeRegistryEditor.create()
256              // TODO: add user-check for permission "editAttributeRegistry"
257              // only add url if true
258              if (withEditURLs) {
259                  nodeJSONObject.put(
260                          "createURL",
261                          request.getCommand()
262                                  .createCopy(false)
263                                  .setAction("createAttributeRegistryEdit")
264                                  .addHttpParameter("id",
265                                          bundleAttribute.getId().toString())
266                                  .toCommandURL(request));
267              }
268  
269              // bundle-check
270              if (bundleString == null) {
271                  nodeMap.put(bundleAttribute.getBundle().getName(),
272                          nodeJSONObject);
273              } else {
274                  // check bundle
275                  if (bundleAttribute.getBundle().getName()
276                          .equalsIgnoreCase(bundleString)) {
277                      nodeMap.put(bundleAttribute.getBundle().getName(),
278                              nodeJSONObject);
279                  }
280              }
281          }
282  
283          JSONArray rootNodes = new JSONArray();
284          rootNodes.addAll(nodeMap.values());
285          return rootNodes;
286  
287      }
288  
289      /**
290       * convenience.
291       * 
292       * @param request
293       *            the current {@code ServiceRequest}
294       * @param nodeList
295       *            a list of nodes to build the node-list for
296       * @param withEditURLs
297       *            if true the edit-urls of the attribute will be added to the
298       *            json
299       * 
300       * @return {@code List&lt;JSONObject&gt;}
301       */
302  
303      // * @return {@code Map&lt;String, JSONObject&gt;}
304      // */
305      // private Map<String, JSONObject> buildNodeMap(final ServiceRequest
306      // request,
307      private List<JSONObject> buildNodeList(final ServiceRequest request,
308              final List<? extends Node> nodeList, final boolean withEditURLs) {
309  
310          // Map<String, JSONObject> nodeMap = new TreeMap<String, JSONObject>();
311          List<JSONObject> jsonNodeList = new ArrayList<JSONObject>();
312  
313          for (Node n : nodeList) {
314              if (n != null) {
315  
316                  LOGGER.debug("  listing node: {}", n.getId());
317  
318                  JSONObject nodeJSONObject = n.toJSON();
319                  nodeJSONObject.put("draggable", true);
320                  nodeJSONObject.put("allowDrop", true);
321  
322                  // TODO: add user-check for permission "editAttributeRegistry"
323                  // only add URLs if true
324  
325                  if (withEditURLs) {
326  
327                      // add createURL to node -->
328                      // AttributeRegistryEditor.create()
329                      nodeJSONObject.put(
330                              "createURL",
331                              request.getCommand()
332                                      .createCopy(false)
333                                      .setAction("createAttributeRegistryEdit")
334                                      .addHttpParameter("id",
335                                              n.getId().toString())
336                                      .toCommandURL(request));
337  
338                      // add editURL to node --> AttributeRegistryEditor.edit()
339                      if (!((AbstractAttribute<?>) n).isSystemAttribute()) {
340                          nodeJSONObject.put(
341                                  "editURL",
342                                  request.getCommand()
343                                          .createCopy(false)
344                                          .setAction("editAttributeRegistryEdit")
345                                          .addHttpParameter("id",
346                                                  n.getId().toString())
347                                          .toCommandURL(request));
348                      }
349  
350                      // add deleteURL to node -->
351                      // AttributeRegistryEditor.delete()
352                      nodeJSONObject.put(
353                              "deleteURL",
354                              request.getCommand()
355                                      .createCopy(false)
356                                      .setAction("deleteAttributeRegistryEdit")
357                                      .addHttpParameter("id",
358                                              n.getId().toString())
359                                      .toCommandURL(request));
360  
361                      // add moveURL to node -->
362                      // AttributeRegistryEditor.move()
363                      nodeJSONObject.put(
364                              "moveURL",
365                              request.getCommand()
366                                      .createCopy(false)
367                                      .setAction("moveAttributeRegistryEdit")
368                                      .addHttpParameter("id",
369                                              n.getId().toString())
370                                      .toCommandURL(request));
371  
372                  }
373  
374                  // sort nodes
375                  // nodeMap.put(n.toJSON().get("type") + "." + n.getClass() + "."
376                  // + ((AbstractAttribute) n).getName() + "." + n.getId(),
377                  // nodeJSONObject);
378  
379                  jsonNodeList.add(nodeJSONObject);
380  
381              } else {
382                  LOGGER.debug("  NULL VALUE");
383              }
384          }
385  
386          // return nodeMap;
387          return jsonNodeList;
388      }
389  
390      /**
391       * starts create-new-({@code Attribute})-editor.
392       * <p>
393       * TODO: add a clear description!
394       * </p>
395       * 
396       * @param bundle
397       *            the current {@code Bundle}
398       * @param request
399       *            the current {@code ServiceRequest}
400       * 
401       * @return an AJAX-result
402       */
403      @RequireToken
404      @Action(value = "createAttributeRegistryEdit", generate = true)
405      @Permission("editAttributeRegistry")
406      @Groups(values = { "AttributeRegistryAdministrator" })
407      public final Result create(final Bundle bundle, final ServiceRequest request) {
408  
409          // setup
410          Session s = Lifecycle.getHibernateDataSource().createNewSession();
411          Transaction tx = s.beginTransaction();
412          RegistryEditorResult result = new RegistryEditorResult();
413  
414          try {
415  
416              for (Class<AbstractAttribute<?>> clazz : Lifecycle
417                      .getAttributeFactory().getAttributes()) {
418  
419                  result.setCreateURL(
420                          clazz.getCanonicalName(),
421                          request.getCommand()
422                                  .createCopy()
423                                  .setAction(
424                                          "createAttributeAttributeRegistryEdit")
425                                  .addHttpParameter(
426                                          "clazz",
427                                          clazz.getCanonicalName().replace('.',
428                                                  '_')).toCommandURL(request));
429  
430              }
431  
432              tx.commit();
433  
434          } catch (Exception e) {
435              tx.rollback();
436              throw new PulseException("AttributeRegistryEditor.create.failed: "
437                      + e.getLocalizedMessage(), e);
438          } finally {
439              s.close();
440          }
441  
442          // output
443          XSLTOutputEvent event = new XSLTOutputEvent(
444                  this.config.getAjaxCreateXSL());
445          event.setStopEvent(true);
446          request.getEventManager().addEvent(event);
447          return result;
448      }
449  
450      /**
451       * creates a new {@code Attribute}.
452       * <p>
453       * TODO: add a clear description!
454       * </p>
455       * 
456       * @param bundle
457       *            the current {@code Bundle}
458       * @param request
459       *            the current {@code ServiceRequest}
460       */
461      @RequireToken
462      @Action(value = "createAttributeAttributeRegistryEdit", generate = true)
463      @Permission("editAttributeRegistry")
464      @Groups(values = { "AttributeRegistryAdministrator" })
465      public final void createAttribute(final Bundle bundle,
466              final ServiceRequest request) {
467  
468          // retrieve values from request
469          String clazz = request.getCommand().getParameter("clazz")
470                  .getFirstValue();
471          Long id = Long.parseLong(request.getCommand().getParameter("id")
472                  .getFirstValue());
473          String name = request.getCommand().getParameter("name").getFirstValue();
474  
475          LOGGER.debug(
476                  "\n---> request.clazz: {}\n---> request.id: {}\n---> request.name: {}",
477                  new Object[] { clazz, id, name });
478  
479          // setup
480          Session s = Lifecycle.getHibernateDataSource().createNewSession();
481          Transaction tx = s.beginTransaction();
482          AbstractAttribute<?> newAttribute = null;
483  
484          try {
485  
486              // load the parent-attribute (as Node) the new child attribute is
487              // to be created for
488              Node parentAttribute = (Node) s.get(Node.class, id);
489  
490              // retrieve new attribute from attribute-factory
491              AttributeFactory factory = Lifecycle.getAttributeFactory();
492              for (Class<AbstractAttribute<?>> knownClazz : factory
493                      .getAttributes()) {
494                  if (knownClazz.getCanonicalName().replace('.', '_')
495                          .equals(clazz)) {
496                      newAttribute = factory.getAttribute(knownClazz, name);
497                  }
498              }
499  
500              // add new attribute to parent-attribute
501              if (newAttribute != null) {
502                  parentAttribute.addChild(newAttribute);
503              }
504  
505              // persist
506              s.saveOrUpdate(parentAttribute);
507  
508              tx.commit();
509  
510          } catch (Exception e) {
511              tx.rollback();
512              throw new PulseException(
513                      "AttributeRegistryEditor.createAttribute.failed: "
514                              + e.getLocalizedMessage(), e);
515          } finally {
516              s.close();
517          }
518  
519          // output
520          if (newAttribute != null) {
521              JSONCommunicationUtils.jsonSuccessMessage(request, "id",
522                      newAttribute.getId().toString());
523          } else {
524              JSONCommunicationUtils.jsonErrorMessage(request);
525          }
526      }
527  
528      /**
529       * starts {@code Attribute}-editor.
530       * <p>
531       * TODO: add a clear description!
532       * </p>
533       * 
534       * @param bundle
535       *            the current {@code Bundle}
536       * @param request
537       *            the current {@code ServiceRequest}
538       * 
539       * @return an AJAX-result
540       */
541      @RequireToken
542      @Action(value = "editAttributeRegistryEdit", generate = true)
543      @Permission("editAttributeRegistry")
544      @Groups(values = { "AttributeRegistryAdministrator" })
545      public final AttributeRegistryEditorResult edit(final Bundle bundle,
546              final ServiceRequest request) {
547  
548          // TODO: user-rights-check
549  
550          // retrieve request-parameters
551          Long id = Long.parseLong(request.getCommand().getParameter("id")
552                  .getFirstValue());
553  
554          // setup
555          Session s = Lifecycle.getHibernateDataSource().createNewSession();
556          Transaction tx = s.beginTransaction();
557          AttributeRegistryEditorResult result = new AttributeRegistryEditorResult();
558  
559          try {
560  
561              // load the requested attribute
562              AbstractAttribute<?> attribute = (AbstractAttribute<?>) s.get(
563                      AbstractAttribute.class, id);
564  
565              // set for result
566              result.setAttribute(attribute);
567  
568              tx.commit();
569  
570          } catch (Exception e) {
571              tx.rollback();
572              throw new PulseException("AttributeRegistryEditor.edit.failed: "
573                      + e.getLocalizedMessage(), e);
574          } finally {
575              s.close();
576          }
577  
578          // output
579          XSLTOutputEvent event = new XSLTOutputEvent(
580                  this.config.getAjaxEditorsXSL());
581          event.setStopEvent(true);
582          request.getEventManager().addEvent(event);
583          return result;
584      }
585  
586      /**
587       * deletes a {@code Attribute}.
588       * <p>
589       * TODO: add a clear description!
590       * </p>
591       * 
592       * @param bundle
593       *            the current {@code Bundle}
594       * @param request
595       *            the current {@code ServiceRequest}
596       */
597      @RequireToken
598      @Action(value = "deleteAttributeRegistryEdit", generate = true)
599      @Permission("editAttributeRegistry")
600      @Groups(values = { "AttributeRegistryAdministrator" })
601      public final void delete(final Bundle bundle, final ServiceRequest request) {
602  
603          // TODO: user-rights-check
604  
605          // retrieve request-parameters
606          Long id = Long.parseLong(request.getCommand().getParameter("id")
607                  .getFirstValue());
608  
609          // setup
610          Session s = Lifecycle.getHibernateDataSource().createNewSession();
611          Transaction tx = s.beginTransaction();
612  
613          try {
614  
615              // load the attribute
616              AbstractAttribute<?> deleteAttribute = (AbstractAttribute<?>) s
617                      .get(AbstractAttribute.class, id);
618  
619              // retrieve parent
620              Node parentAttribute = deleteAttribute.getParent();
621  
622              // remove from parent
623              parentAttribute.removeChild(deleteAttribute);
624  
625              // persist
626              s.saveOrUpdate(parentAttribute);
627              s.delete((AbstractAttribute<?>) s.get(AbstractAttribute.class, id));
628  
629              tx.commit();
630  
631          } catch (Exception e) {
632              tx.rollback();
633              throw new PulseException("AttributeRegistryEditor.delete.failed: "
634                      + e.getLocalizedMessage(), e);
635          } finally {
636              s.close();
637          }
638  
639          // output
640          JSONCommunicationUtils.jsonSuccessMessage(request);
641      }
642  
643      /**
644       * loads the admin-edit-{@code Role}s of a {@code Attribute}.
645       * <p>
646       * TODO: add a clear description!
647       * </p>
648       * 
649       * @param request
650       *            the current {@code ServiceRequest}
651       */
652      @RequireToken
653      @Action(value = "loadAdminEditRolesAttributeRegistryEditor", generate = true)
654      @Permission("editAttributeRegistry")
655      @Groups(values = { "AttributeRegistryAdministrator" })
656      public final void loadAdminEditRoles(final ServiceRequest request) {
657          loadRoles(request, 1);
658      }
659  
660      /**
661       * loads the admin-view-{@code Role}s of a {@code Attribute}.
662       * <p>
663       * TODO: add a clear description!
664       * </p>
665       * 
666       * @param request
667       *            the current {@code ServiceRequest}
668       */
669      @RequireToken
670      @Action(value = "loadAdminViewRolesAttributeRegistryEditor", generate = true)
671      @Permission("editAttributeRegistry")
672      @Groups(values = { "AttributeRegistryAdministrator" })
673      public final void loadAdminViewRoles(final ServiceRequest request) {
674          loadRoles(request, 2);
675      }
676  
677      /**
678       * loads the self-edit-{@code Role}s of a {@code Attribute}.
679       * <p>
680       * TODO: add a clear description!
681       * </p>
682       * 
683       * @param request
684       *            the current {@code ServiceRequest}
685       */
686      @RequireToken
687      @Action(value = "loadSelfEditRolesAttributeRegistryEditor", generate = true)
688      @Permission("editAttributeRegistry")
689      @Groups(values = { "AttributeRegistryAdministrator" })
690      public final void loadSelfEditRoles(final ServiceRequest request) {
691          loadRoles(request, 3);
692      }
693  
694      /**
695       * loads the self-view-{@code Role}s of a {@code Attribute}.
696       * <p>
697       * TODO: add a clear description!
698       * </p>
699       * 
700       * @param request
701       *            the current {@code ServiceRequest}
702       */
703      @RequireToken
704      @Action(value = "loadSelfViewRolesAttributeRegistryEditor", generate = true)
705      @Permission("editAttributeRegistry")
706      @Groups(values = { "AttributeRegistryAdministrator" })
707      public final void loadSelfViewRoles(final ServiceRequest request) {
708          loadRoles(request, 4);
709      }
710  
711      /**
712       * loads the triggered-{@code Role}s of a {@code Attribute}.
713       * <p>
714       * TODO: add a clear description!
715       * </p>
716       * 
717       * 
718       * @param request
719       *            the current {@code ServiceRequest}
720       */
721      @RequireToken
722      @Action(value = "loadTriggeredRolesAttributeRegistryEditor", generate = true)
723      @Permission("editAttributeRegistry")
724      @Groups(values = { "AttributeRegistryAdministrator" })
725      public final void loadTriggeredRoles(final ServiceRequest request) {
726          loadRoles(request, 5);
727      }
728  
729      /**
730       * convenience.
731       * 
732       * @param request
733       *            the current {@code ServiceRequest}
734       * @param mode
735       *            1 = admin_edit-roles
736       */
737      @SuppressWarnings("unchecked")
738      private void loadRoles(final ServiceRequest request, final int mode) {
739  
740          // retrieve values from request
741          Long attributeId = Long.parseLong(request.getCommand()
742                  .getParameter("id").getFirstValue());
743  
744          // determine whether to load associated roles or !associated roles
745          boolean unasoc = false;
746          if (request.getCommand().getParameter("unasoc") != null) {
747              unasoc = Boolean.parseBoolean(request.getCommand()
748                      .getParameter("unasoc").getFirstValue());
749          }
750  
751          // setup
752          Session s = Lifecycle.getHibernateDataSource().createNewSession();
753          Transaction tx = s.beginTransaction();
754          JSONArray data = new JSONArray();
755  
756          try {
757  
758              // load the user
759              AbstractAttribute<?> attribute = (AbstractAttribute<?>) s.get(
760                      AbstractAttribute.class, attributeId);
761  
762              Criteria criteria = buildLoadRolesCriteria(request, unasoc, s,
763                      attribute, mode);
764  
765              if (criteria != null) {
766                  long total = processCriteriaForPaging(request, criteria);
767  
768                  // load roles
769                  List<Role> roles = (List<Role>) criteria.addOrder(
770                          Order.asc("name").ignoreCase()).list();
771  
772                  // build data-array
773                  boolean totalSet = false;
774                  for (Role role : roles) {
775                      JSONObject roleObj = role.toJSON();
776                      if (!totalSet) {
777                          roleObj.put("total", total);
778                          totalSet = true;
779                      }
780                      roleObj.put("initialAsoc", !unasoc);
781                      data.add(roleObj);
782                  }
783  
784              }
785  
786              tx.commit();
787  
788          } catch (Exception e) {
789              tx.rollback();
790              throw new PulseException(
791                      "AttributeRegistryEditor.loadRoles.failed: "
792                              + e.getLocalizedMessage(), e);
793          } finally {
794              s.close();
795          }
796  
797          // output
798          request.getEventManager().addEvent(new JSONOutputEvent(data));
799      }
800  
801      /**
802       * builds the {@code Criteria} for the
803       * admin/self-edit/view-roles-assorter-load and the
804       * triggered-roles-assorter-load.
805       * 
806       * @param request
807       *            the current {@code ServiceRequest}
808       * @param unasoc
809       *            {@code true} to retrieve criteria for loading of associated
810       *            {@code Role}s, {@code false} for criteria for loading of
811       *            UN-associated {@code Role}s
812       * @param s
813       *            the current {@code Session}
814       * @param attribute
815       *            the attribute
816       * @param mode
817       *            <ul>
818       *            <li>1 = load admin-edit-roles</li>
819       *            <li>2 = load admin-view-roles</li>
820       *            <li>3 = load self-edit-roles</li>
821       *            <li>4 = load self-view-roles</li>
822       *            <li>5 = load triggered-roles</li>
823       *            </ul>
824       * 
825       * @return the built {@code Criteria}
826       */
827      private Criteria buildLoadRolesCriteria(final ServiceRequest request,
828              final boolean unasoc, final Session s,
829              final AbstractAttribute<?> attribute, final int mode) {
830  
831          Criterion nameCriterion = buildCriterionFromRequest(request, "filter",
832                  "name");
833  
834          // retrieve role-ids for loading
835          List<Long> ids = new ArrayList<Long>();
836          if (mode == 1) {
837              for (Role r : (Set<Role>) attribute.getAdminEditRoles()) {
838                  ids.add(r.getId());
839              }
840          } else if (mode == 2) {
841              for (Role r : (Set<Role>) attribute.getAdminViewRoles()) {
842                  ids.add(r.getId());
843              }
844          } else if (mode == 3) {
845              for (Role r : (Set<Role>) attribute.getSelfEditRoles()) {
846                  ids.add(r.getId());
847              }
848          } else if (mode == 4) {
849              for (Role r : (Set<Role>) attribute.getSelfViewRoles()) {
850                  ids.add(r.getId());
851              }
852          } else if (mode == 5) {
853              for (Role r : (Set<Role>) attribute.getTriggeredRoles()) {
854                  ids.add(r.getId());
855              }
856          }
857  
858          Criteria criteria = null;
859          if (unasoc) {
860  
861              // return !associated
862              if (ids.isEmpty()) {
863                  criteria = s.createCriteria(Role.class);
864              } else {
865                  criteria = s.createCriteria(Role.class).add(
866                          Restrictions.not(Restrictions.in("id", ids)));
867              }
868  
869          } else {
870  
871              // return associated
872              if (!ids.isEmpty()) {
873                  criteria = s.createCriteria(Role.class).add(
874                          Restrictions.in("id", ids));
875              }
876  
877          }
878  
879          // add name criterion
880          if (criteria != null && nameCriterion != null) {
881              criteria.add(nameCriterion);
882          }
883          return criteria;
884      }
885  
886      /**
887       * saves value changes for the {@code Roles}s of the {@code Attribute}.
888       * 
889       * @param bundle
890       *            the current {@code Bundle}
891       * @param request
892       *            the current {@code ServiceRequest}
893       */
894      @RequireToken
895      @Action(value = "saveAttributeRegistryEditor", generate = true)
896      @Permission("editAttributeRegistry")
897      @Groups(values = { "AttributeRegistryAdministrator" })
898      public final void save(final Bundle bundle, final ServiceRequest request) {
899  
900          // retrieve values from request
901          Long id = Long.parseLong(request.getCommand().getParameter("id")
902                  .getFirstValue());
903  
904          // setup
905          Session s = Lifecycle.getHibernateDataSource().createNewSession();
906          Transaction tx = s.beginTransaction();
907          // invalidCheck : if occurs
908          JSONObject error = null;
909  
910          try {
911  
912              // load the attribute
913              AbstractAttribute<?> attribute = (AbstractAttribute<?>) s.get(
914                      AbstractAttribute.class, id);
915  
916              // set changes
917              attribute.updateSettingsFromCommand(request.getCommand(), s);
918  
919              // process check
920              if (attribute.getCheck() != null) {
921                  try {
922                      attribute.getCheck().setCheckFromCommand(
923                              request.getCommand());
924                  } catch (Exception e) {
925                      LOGGER.debug(
926                              "suppressed Exception and returned JSON-error-message instead for:"
927                                      + e.getLocalizedMessage(), e);
928                      error = new JSONObject();
929                      error.put("e", "invalidCheck");
930                  }
931              }
932  
933              if (error == null) {
934                  // isRequired
935                  if (request.getCommand().getParameter("isRequired") != null) {
936                      Boolean isRequired = Boolean.valueOf(request.getCommand()
937                              .getParameter("isRequired").getFirstValue());
938                      if (isRequired != attribute.isRequired()) {
939                          attribute.setRequired(isRequired);
940                      }
941                  }
942  
943                  // admin-edit-roles
944                  processRoles(request, s, attribute, 1);
945  
946                  // admin-view-roles
947                  processRoles(request, s, attribute, 2);
948  
949                  // self-edit-roles
950                  processRoles(request, s, attribute, 3);
951  
952                  // self-view-roles
953                  processRoles(request, s, attribute, 4);
954  
955                  // triggered-roles
956                  processRoles(request, s, attribute, 5);
957  
958                  // persist
959                  s.saveOrUpdate(attribute);
960              }
961  
962              tx.commit();
963  
964          } catch (Exception e) {
965              tx.rollback();
966              throw new PulseException(
967                      "PermissionEditor.doSavePermissionEditor.failed: "
968                              + e.getLocalizedMessage(), e);
969          } finally {
970              s.close();
971          }
972  
973          // output
974          if (error == null) {
975              JSONCommunicationUtils.jsonSuccessMessage(request);
976          } else {
977              JSONCommunicationUtils.jsonErrorMessage(request, error);
978          }
979      }
980  
981      /**
982       * processes the request for {@code Role}s to add or remove from the
983       * attribute and adds or removes them according to mode.
984       * 
985       * @param request
986       *            the current {@code ServiceRequest}
987       * @param s
988       *            the current {@code Session}
989       * @param attribute
990       *            the current attribute
991       * @param mode
992       *            <ul>
993       *            <li>1 = admin-edit-roles</li>
994       *            <li>2 = admin-view-roles</li>
995       *            <li>3 = self-edit-roles</li>
996       *            <li>4 = self-view-roles</li>
997       *            <li>5 = triggered-roles</li>
998       *            </ul>
999       */
1000     private void processRoles(final ServiceRequest request, final Session s,
1001             final AbstractAttribute<?> attribute, final int mode) {
1002 
1003         if (mode == 1) {
1004             // admin-edit-roles
1005 
1006             // add to attribute
1007             List<Object> roles = retrieveAssorterObjects("addAdminEditRoles",
1008                     request, "Role", s);
1009             for (Object o : roles) {
1010                 attribute.addAdminEditRole((Role) o);
1011             }
1012 
1013             // remove from attribute
1014             roles = retrieveAssorterObjects("remAdminEditRoles", request,
1015                     "Role", s);
1016             for (Object o : roles) {
1017                 attribute.removeAdminEditRole((Role) o);
1018             }
1019         } else if (mode == 2) {
1020             // admin-view-roles
1021 
1022             // add to attribute
1023             List<Object> roles = retrieveAssorterObjects("addAdminViewRoles",
1024                     request, "Role", s);
1025             for (Object o : roles) {
1026                 attribute.addAdminViewRole((Role) o);
1027             }
1028 
1029             // remove from attribute
1030             roles = retrieveAssorterObjects("remAdminViewRoles", request,
1031                     "Role", s);
1032             for (Object o : roles) {
1033                 attribute.removeAdminViewRole((Role) o);
1034             }
1035         } else if (mode == 3) {
1036             // self-edit-roles
1037 
1038             // add to attribute
1039             List<Object> roles = retrieveAssorterObjects("addSelfEditRoles",
1040                     request, "Role", s);
1041             for (Object o : roles) {
1042                 attribute.addSelfEditRole((Role) o);
1043             }
1044 
1045             // remove from attribute
1046             roles = retrieveAssorterObjects("remSelfEditRoles", request,
1047                     "Role", s);
1048             for (Object o : roles) {
1049                 attribute.removeSelfEditRole((Role) o);
1050             }
1051         } else if (mode == 4) {
1052             // self-view-roles
1053 
1054             // add to attribute
1055             List<Object> roles = retrieveAssorterObjects("addSelfViewRoles",
1056                     request, "Role", s);
1057             for (Object o : roles) {
1058                 attribute.addSelfViewRole((Role) o);
1059             }
1060 
1061             // remove from attribute
1062             roles = retrieveAssorterObjects("remSelfViewRoles", request,
1063                     "Role", s);
1064             for (Object o : roles) {
1065                 attribute.removeSelfViewRole((Role) o);
1066             }
1067         } else if (mode == 5) {
1068             // triggered-roles
1069 
1070             // add to attribute
1071             List<Object> roles = retrieveAssorterObjects("addTriggeredRoles",
1072                     request, "Role", s);
1073             for (Object o : roles) {
1074                 attribute.addTriggeredRole((Role) o);
1075             }
1076 
1077             // remove from attribute
1078             roles = retrieveAssorterObjects("remTriggeredRoles", request,
1079                     "Role", s);
1080             for (Object o : roles) {
1081                 attribute.removeTriggeredRole((Role) o);
1082             }
1083         }
1084 
1085     }
1086 
1087     /**
1088      * loads the checks of a {@code Attribute}.
1089      * <p>
1090      * This can be either a check or the available checks if check is unset for
1091      * attribute.
1092      * </p>
1093      * <p>
1094      * TODO: add a clear description!
1095      * </p>
1096      * 
1097      * @param bundle
1098      *            the current {@code Bundle}
1099      * @param request
1100      *            the current {@code ServiceRequest}
1101      * 
1102      * @return an AJAX-result
1103      */
1104     @RequireToken
1105     @Action(value = "loadChecksAttributeRegistryEditor", generate = true)
1106     @Permission("editAttributeRegistry")
1107     @Groups(values = { "AttributeRegistryAdministrator" })
1108     public final Result loadChecks(final Bundle bundle,
1109             final ServiceRequest request) {
1110 
1111         // retrieve values from request
1112         Long id = Long.parseLong(request.getCommand().getParameter("id")
1113                 .getFirstValue());
1114 
1115         // setup
1116         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1117         Transaction tx = s.beginTransaction();
1118         AttributeRegistryEditorResult result = new AttributeRegistryEditorResult();
1119 
1120         try {
1121 
1122             // load the attribute
1123             AbstractAttribute<?> attribute = (AbstractAttribute<?>) s.get(
1124                     AbstractAttribute.class, id);
1125 
1126             // retrieve checks, either
1127             if (attribute.getCheck() == null) {
1128                 // available checks
1129                 result.setAttribute(attribute);
1130                 result.setAvailableChecks(attribute.getTypedChecks());
1131             } else {
1132                 // assigned check
1133                 result.setAttribute(attribute);
1134                 result.setAttributeCheck(attribute.getCheck());
1135             }
1136 
1137             tx.commit();
1138 
1139         } catch (Exception e) {
1140             tx.rollback();
1141             throw new PulseException(
1142                     "AttributeRegistryEditor.loadChecks.failed: "
1143                             + e.getLocalizedMessage(), e);
1144         } finally {
1145             s.close();
1146         }
1147 
1148         // output
1149         XSLTOutputEvent event = new XSLTOutputEvent(
1150                 this.config.getAjaxChecksXSL());
1151         event.setStopEvent(true);
1152         request.getEventManager().addEvent(event);
1153         return result;
1154 
1155     }
1156 
1157     /**
1158      * sets a check for a {@code Attribute}.
1159      * <p>
1160      * TODO: add a clear description!
1161      * </p>
1162      * 
1163      * @param bundle
1164      *            the current {@code Bundle}
1165      * @param request
1166      *            the current {@code ServiceRequest}
1167      */
1168     @RequireToken
1169     @Action(value = "setCheckAttributeRegistryEditor", generate = true)
1170     @Permission("editAttributeRegistry")
1171     @Groups(values = { "AttributeRegistryAdministrator" })
1172     @SuppressWarnings("unchecked")
1173     public final void setCheck(final Bundle bundle, final ServiceRequest request) {
1174 
1175         // TODO: user-rights-check
1176 
1177         // retrieve values from request
1178         Long id = Long.parseLong(request.getCommand().getParameter("id")
1179                 .getFirstValue());
1180         String clazz = request.getCommand().getParameter("clazz")
1181                 .getFirstValue();
1182 
1183         // setup
1184         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1185         Transaction tx = s.beginTransaction();
1186         boolean removeOldCheck = false;
1187 
1188         try {
1189 
1190             // load the attribute
1191             AbstractAttribute<?> attribute = (AbstractAttribute<?>) s.get(
1192                     AbstractAttribute.class, id);
1193 
1194             // store previous check
1195             AbstractTypedCheck<?> oldCheck = null;
1196             if (attribute.getCheck() != null) {
1197                 oldCheck = attribute.getCheck();
1198             }
1199 
1200             // perform changes
1201             if (clazz.equals("")) {
1202                 // just remove check
1203                 attribute.setCheck(null);
1204                 removeOldCheck = true;
1205             } else {
1206                 // build new check
1207                 for (Class<AbstractTypedCheck<?>> c : (Set<Class<AbstractTypedCheck<?>>>) attribute
1208                         .getTypedChecks()) {
1209                     if (c.getCanonicalName().equals(clazz)) {
1210                         AbstractTypedCheck<?> check = Lifecycle
1211                                 .getAttributeFactory().getTypedCheck(c);
1212                         // raw type required as value type is unknown
1213                         attribute.setCheck((AbstractTypedCheck) check);
1214                         removeOldCheck = true;
1215                     }
1216                 }
1217             }
1218 
1219             // remove oldCheck
1220             if (removeOldCheck && (oldCheck != null)) {
1221                 s.delete(oldCheck);
1222             }
1223 
1224             // persist
1225             s.saveOrUpdate(attribute);
1226 
1227             tx.commit();
1228 
1229         } catch (Exception e) {
1230             tx.rollback();
1231             throw new PulseException(
1232                     "AttributeRegistryEditor.setCheck.failed: "
1233                             + e.getLocalizedMessage(), e);
1234         } finally {
1235             s.close();
1236         }
1237 
1238         // output
1239         if (removeOldCheck) {
1240             JSONCommunicationUtils.jsonSuccessMessage(request);
1241         } else {
1242             JSONCommunicationUtils.jsonErrorMessage(request);
1243         }
1244 
1245     }
1246 
1247     /**
1248      * moves a {@code ? extends RegistryLocaleNode}. By default a
1249      * {@code RegistryLocaleNode} itself is not movable.
1250      * 
1251      * @param request
1252      *            the current {@code ServiceRequest}
1253      */
1254     @RequireToken
1255     @Action(value = "moveAttributeRegistryEdit", generate = true)
1256     @Permission("editAttributeRegistry")
1257     @Groups(values = { "AttributeRegistryAdministrator" })
1258     public final void move(final ServiceRequest request) {
1259 
1260         // setup
1261         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1262         Transaction tx = s.beginTransaction();
1263 
1264         int index = -1;
1265         // error : cannotMoveNodeToRootLevel (if occurs)
1266         JSONObject error = null;
1267 
1268         try {
1269 
1270             // load node that's to be moved
1271             AbstractAttribute<?> moveNode = (AbstractAttribute<?>) s.get(
1272                     AbstractAttribute.class,
1273                     Long.parseLong(request.getCommand()
1274                             .getParameter("movenodeid").getFirstValue()));
1275 
1276             AbstractAttribute<?> newParentNode = null;
1277 
1278             if (error == null) {
1279 
1280                 // determine new parent
1281                 newParentNode = determineNewParent(request, s);
1282 
1283                 // general ContentRegistry-move-checks
1284                 // also checks for (new parent == null)
1285                 if (newParentNode == null) {
1286                     error = new JSONObject();
1287                     error.put("e", "cannotMoveToUndefinedLevel");
1288                 }
1289             }
1290 
1291             // perform move
1292             if (error == null) {
1293 
1294                 // determine index
1295                 if (!request.getCommand().getParameter("insertpoint")
1296                         .getFirstValue().equals("append")) {
1297                     // determine add-(reference-node.)position-index
1298                     index = newParentNode
1299                             .getChildIndex((AbstractAttribute<?>) s.get(
1300                                     AbstractAttribute.class,
1301                                     Long.parseLong(request.getCommand()
1302                                             .getParameter("targetnodeid")
1303                                             .getFirstValue())));
1304                     if (request.getCommand().getParameter("insertpoint")
1305                             .getFirstValue().equals("below")) {
1306                         index += 1;
1307                     }
1308                 }
1309 
1310                 // load old parent
1311                 AbstractAttribute<?> oldParentNode = (AbstractAttribute<?>) moveNode
1312                         .getParent();
1313 
1314                 // move
1315                 oldParentNode.removeChild(moveNode);
1316                 if (index == -1) {
1317                     newParentNode.addChild(moveNode);
1318                 } else {
1319                     try {
1320                         newParentNode.addChild(index, moveNode);
1321                     } catch (IndexOutOfBoundsException e) {
1322                         newParentNode.addChild(moveNode);
1323                     }
1324                 }
1325 
1326                 // persist
1327                 s.saveOrUpdate(oldParentNode);
1328                 s.saveOrUpdate(newParentNode);
1329 
1330                 tx.commit();
1331             }
1332 
1333         } catch (Exception e) {
1334             if (tx != null) {
1335                 tx.rollback();
1336             }
1337             throw new PulseException(
1338                     "Core.AttributeRegistryEditor.moveAttributeNode.failed: "
1339                             + e.getLocalizedMessage(), e);
1340         } finally {
1341             s.close();
1342         }
1343 
1344         // output
1345         if (error == null) {
1346             JSONCommunicationUtils.jsonSuccessMessage(request);
1347         } else {
1348             JSONCommunicationUtils.jsonErrorMessage(request, error);
1349         }
1350     }
1351 
1352     /**
1353      * convenience (for move).
1354      * 
1355      * @param request
1356      *            the current {@code ServiceRequest}
1357      * @param s
1358      *            the current {@code Session}
1359      * 
1360      * @return a {@code AbstractAttribute&lt;?&gt;} which could be possible new
1361      *         parent during move operation
1362      */
1363     private AbstractAttribute<?> determineNewParent(
1364             final ServiceRequest request, final Session s) {
1365         try {
1366             if (request.getCommand().getParameter("insertpoint")
1367                     .getFirstValue().equals("append")) {
1368                 // (reference-node = target-node)
1369                 // load the potentially new parent-node
1370                 return (AbstractAttribute<?>) s.get(
1371                         AbstractAttribute.class,
1372                         Long.parseLong(request.getCommand()
1373                                 .getParameter("targetnodeid").getFirstValue()));
1374             } else {
1375                 // (reference-node = target-node.parent)
1376                 // load the potentially new parent-node
1377                 return (AbstractAttribute<?>) ((AbstractAttribute<?>) s.get(
1378                         AbstractAttribute.class,
1379                         Long.parseLong(request.getCommand()
1380                                 .getParameter("targetnodeid").getFirstValue())))
1381                         .getParent();
1382             }
1383         } catch (Exception e) {
1384             return null;
1385         }
1386     }
1387 
1388     /**
1389      * initializes the {@code AttributeRegistryEditor}.
1390      * 
1391      * @param conf
1392      *            the configuration
1393      */
1394     public final void init(final ConfigBean conf) {
1395         this.config = (AttributeRegistryEditorConfig) conf;
1396     }
1397 
1398 }
1399