View Javadoc
1   /*******************************************************************************
2    * Copyright 2012 Internet2
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   ******************************************************************************/
16  /*
17   * @author mchyzer
18   * $Id: GuiSubject.java,v 1.2 2009-10-11 22:04:17 mchyzer Exp $
19   */
20  package edu.internet2.middleware.grouper.grouperUi.beans.api;
21  
22  import java.io.Serializable;
23  import java.util.HashMap;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedHashSet;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.commons.lang.StringUtils;
30  
31  import edu.internet2.middleware.grouper.Group;
32  import edu.internet2.middleware.grouper.GroupFinder;
33  import edu.internet2.middleware.grouper.GrouperSession;
34  import edu.internet2.middleware.grouper.GrouperSourceAdapter;
35  import edu.internet2.middleware.grouper.Member;
36  import edu.internet2.middleware.grouper.MemberFinder;
37  import edu.internet2.middleware.grouper.SubjectFinder;
38  import edu.internet2.middleware.grouper.entity.EntitySourceAdapter;
39  import edu.internet2.middleware.grouper.exception.GrouperSessionException;
40  import edu.internet2.middleware.grouper.grouperUi.beans.ui.GrouperRequestContainer;
41  import edu.internet2.middleware.grouper.grouperUi.beans.ui.TextContainer;
42  import edu.internet2.middleware.grouper.misc.GrouperObject;
43  import edu.internet2.middleware.grouper.misc.GrouperSessionHandler;
44  import edu.internet2.middleware.grouper.subj.GrouperSubject;
45  import edu.internet2.middleware.grouper.subj.SubjectHelper;
46  import edu.internet2.middleware.grouper.subj.UnresolvableSubject;
47  import edu.internet2.middleware.grouper.ui.util.GrouperUiConfig;
48  import edu.internet2.middleware.grouper.ui.util.GrouperUiUtils;
49  import edu.internet2.middleware.grouper.util.GrouperEmailUtils;
50  import edu.internet2.middleware.grouper.util.GrouperUtil;
51  import edu.internet2.middleware.subject.Source;
52  import edu.internet2.middleware.subject.Subject;
53  import edu.internet2.middleware.subject.provider.SourceManager;
54  
55  
56  /**
57   * subject for gui has all attributes etc, and getter to be accessed from screen
58   */
59  @SuppressWarnings("serial")
60  public class GuiSubject extends GuiObjectBase implements Serializable {
61  
62    /**
63     * return source id two pipes and subject id
64     * @return the source id two pipes and subject id
65     */
66    public String getSourceIdSubjectId() {
67      if (this.subject == null) {
68        return null;
69      }
70      return this.subject.getSourceId() + "||" + this.subject.getId();
71    }
72    
73    /**
74     * 
75     * @see java.lang.Object#equals(java.lang.Object)
76     */
77    public boolean equals(Object other) {
78      if (this == other) {
79        return true;
80      }
81      if (!(other instanceof GuiSubject)) {
82        return false;
83      }
84      return SubjectHelper.eq(this.subject, ((GuiSubject)other).subject);
85    }
86  
87    /**
88     * @see java.lang.Object#hashCode()
89     */
90    public int hashCode() {
91      return SubjectHelper.hashcode(this.subject);
92    }
93  
94    /**
95     * get the member id of the subject or null if not there
96     * @return the member id if exists or null if not
97     */
98    public String getMemberId() {
99      Member theMember = this.getMember();
100     return theMember == null ? null : theMember.getId();
101   }
102   
103   /**
104    * member
105    */
106   private Member member;
107   
108   /**
109    * get the member of the subject or null if not there
110    * @return the member if exists or null if not
111    */
112   public Member getMember() {
113     
114     if (this.member == null) {
115       GrouperSession grouperSession = GrouperSession.staticGrouperSession(false);
116       
117       //when converting json this is null, so dont do a query if just doing json beans
118       if (grouperSession != null) {
119         this.member = MemberFinder.findBySubject(grouperSession, this.getSubject(), false);
120       }
121     }
122     return this.member;
123   }
124   
125   /**
126    * if the gui subject has an email address
127    * @return true if the subject has email
128    */
129   public boolean isHasEmailAttributeInSource() {
130     if (this.subject == null) {
131       return false;
132     }
133     //if there is an email attribute in the source, then this is true
134     return !StringUtils.isBlank(GrouperEmailUtils.emailAttributeNameForSource(this.subject.getSourceId()));
135   }
136 
137   /**
138    * get the email attribute value
139    * @return the email or null or blank if not there
140    */
141   public String getEmail() {
142     if (this.subject == null) {
143       return null;
144     }
145     String emailAttributeName = GrouperEmailUtils.emailAttributeNameForSource(this.subject.getSourceId());
146     if (StringUtils.isBlank(emailAttributeName)) {
147       return null;
148     }
149     return this.subject.getAttributeValue(emailAttributeName);
150   }
151   
152   /**
153    * 
154    * @param subjects
155    * @return subjects
156    */
157   public static Set<GuiSubject> convertFromSubjects(Set<Subject> subjects) {
158     return convertFromSubjects(subjects, null, -1);
159   }
160 
161 
162   /**
163    * 
164    * @param subjects
165    * @return gui subjects
166    */
167   public static Set<GuiSubject> convertFromSubjects(Set<Subject> subjects, String configMax, int defaultMax) {
168 
169     Set<GuiSubject> tempSubjects = new LinkedHashSet<GuiSubject>();
170     
171     Integer max = null;
172     
173     if (!StringUtils.isBlank(configMax)) {
174       max = GrouperUiConfig.retrieveConfig().propertyValueInt(configMax, defaultMax);
175     }
176     
177     int count = 0;
178     for (Subject subject : GrouperUtil.nonNull(subjects)) {
179       tempSubjects.add(new GuiSubject(subject));
180       if (max != null && ++count >= max) {
181         break;
182       }
183     }
184     
185     return tempSubjects;
186     
187   }
188 
189   /**
190    * see if group or not
191    * @return if group
192    */
193   public boolean isGroup() {
194     if (this.subject == null) {
195       return false;
196     }
197     return StringUtils.equals(SubjectFinder.internal_getGSA().getId(), this.subject.getSourceId());
198   }
199   
200   /** subject */
201   private Subject subject;
202 
203   /**
204    * e.g. &lt;a href="#"&gt;John Smith&lt;/a&gt;
205    * @return short link
206    */
207   public String getShortLink() {
208     this.initScreenLabels();
209     return this.screenLabelShort2html;
210   }
211   
212   /**
213    * e.g. &lt;a href="#"&gt;John Smith&lt;/a&gt;
214    * @return short link
215    */
216   public String getShortLinkWithIcon() {
217     this.initScreenLabels();
218     return this.screenLabelShort2htmlWithIcon;
219   }
220 
221   /**
222    * e.g. &lt;a href="#"&gt;John Smith&lt;/a&gt;
223    * @return short link
224    */
225   public String getScreenLabelShort2noLink() {
226     this.initScreenLabels();
227     return this.screenLabelShort2noLink;
228   }
229 
230   /**
231    * some source Id that isnt a normal grouper one
232    */
233   private static String sourceId = null;
234   
235   /**
236    * 
237    * @return a source id
238    */
239   public static String someSourceId() {
240     if (sourceId == null) {
241       synchronized (GuiSubject.class) {
242         if (sourceId == null) {
243           //pick one at random?
244           String theSourceId = "g:gsa";
245           
246           for (Source source : SourceManager.getInstance().getSources()) {
247             if (!"g:gsa".equals(source.getId())
248                 && !"grouperEntities".equals(source.getId())
249                 && !"grouperExternal".equals(source.getId())
250                 && !"g:isa".equals(source.getId())) {
251               theSourceId = source.getId();
252               break;
253             }
254           }
255           sourceId = theSourceId;
256           
257         }
258       }
259     }
260     return sourceId;
261   }
262   
263   /**
264    * init screen labels
265    */
266   private void initScreenLabels() {
267     
268     if (this.subject == null) {
269 
270       //unresolvable
271       this.subject = new UnresolvableSubject("", null, someSourceId());
272       
273     }
274     
275     if (this.screenLabelLong == null && this.screenLabelShort == null) {
276       
277       boolean convertToUnresolvableSubject = false;
278       String unresolvableSubjectString = TextContainer.retrieveFromRequest().getText().get("guiUnresolvableSubject");
279       
280       if (this.subject instanceof UnresolvableSubject) {
281         if (!StringUtils.equals(unresolvableSubjectString, ((UnresolvableSubject)this.subject).getUnresolvableString())) {
282           //we want to use the unresolvable string from the externalized text file in the UI
283           convertToUnresolvableSubject = true;
284         }
285         
286         //convert from lazy subject error to an unresolvable
287       } else if (this.subject != null && this.subject.getName() != null && this.subject.getName().contains(" entity not found")) {
288         convertToUnresolvableSubject = true;
289       }
290 
291       if (convertToUnresolvableSubject) {
292         
293         this.subject = new UnresolvableSubject(this.subject.getId(), this.subject.getTypeName(), this.subject.getSourceId(), unresolvableSubjectString);
294 
295       }
296 
297       boolean isUnresolvableGroup = false;
298       
299       
300       if (this.subject instanceof UnresolvableSubject) {
301         if (StringUtils.equals(GrouperSourceAdapter.groupSourceId(), this.subject.getSourceId())
302             || StringUtils.equals(EntitySourceAdapter.entitySourceId(), this.subject.getSourceId())) {
303           
304           isUnresolvableGroup = true;
305           
306           GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setSubjectId(this.subject.getId());
307           String extensionName = null;
308           if (StringUtils.equals(GrouperSourceAdapter.groupSourceId(), this.subject.getSourceId())) {
309             extensionName = TextContainer.retrieveFromRequest().getText().get("guiGroupCantView");
310           } else {
311             extensionName = TextContainer.retrieveFromRequest().getText().get("guiEntityCantView");
312           }
313           GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setSubjectId(null);
314 
315           //the user does not have view on this group or entity!
316           Map<String, Set<String>> subjectAttributes = this.subject.getAttributes();
317           subjectAttributes.put("displayExtension", GrouperUtil.toSet(StringUtils.abbreviate(extensionName, 33)));
318           subjectAttributes.put("displayName", GrouperUtil.toSet(extensionName));
319           ((UnresolvableSubject) this.subject).setDescription(extensionName);
320           ((UnresolvableSubject) this.subject).setAttributes(subjectAttributes);
321         }
322       }
323       
324       String screenLabel = GrouperUiUtils.convertSubjectToLabelLong(this.subject);
325             
326       this.screenLabelLong = screenLabel;
327       
328       screenLabel = GrouperUiUtils.convertSubjectToLabel(this.subject);
329       
330       int maxWidth = GrouperUiConfig.retrieveConfig().propertyValueInt("subject.maxChars", 100);
331       if (maxWidth == -1) {
332         this.screenLabelShort = screenLabel;
333       } else {
334         this.screenLabelShort = StringUtils.abbreviate(screenLabel, maxWidth);
335       }
336 
337       screenLabel = GrouperUiUtils.convertSubjectToLabelHtmlConfigured2(this.subject);
338       
339       maxWidth = GrouperUiConfig.retrieveConfig().propertyValueInt("subject2.maxChars", 40);
340       if (maxWidth == -1) {
341         this.screenLabelShort2 = screenLabel;
342       } else {
343         this.screenLabelShort2 = StringUtils.abbreviate(screenLabel, maxWidth);
344       }
345 
346       this.screenSubjectIcon2Html = GrouperUiUtils.convertSubjectToIconHtmlConfigured2(this.subject);
347 
348       boolean hasTooltip = this.subject != null
349         && !StringUtils.isBlank(this.subject.getDescription()) && !StringUtils.equals(this.subject.getName(), this.subject.getDescription());
350       
351       GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setShowTooltip(hasTooltip);
352       GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setGuiSubject(this);
353       
354       try {
355         
356         //"<a href=\"view-subject.html\" rel=\"tooltip\" data-html=\"true\" data-delay-show=\"200\" data-placement=\"right\" title=\"" + GrouperUtil.xmlEscape(subject.getDescription(), true)  + "\">" + this.screenLabelShort2 + "</a>";
357         //"<a href=\"view-subject.html\" data-html=\"true\" data-delay-show=\"200\" data-placement=\"right\">" + this.screenLabelShort2 + "</a>";
358         Group group = null;
359         
360         if (this.isGroup()) {
361           if (this.subject instanceof GrouperSubject) {
362             group = ((GrouperSubject)this.subject).internal_getGroup();
363           }
364           if (group == null) {
365             group = GroupFinder.findByUuid(GrouperSession.staticGrouperSession(), this.subject.getId(), false);
366           }
367           if (group == null) {
368             group = new Group();
369             group.setId(this.subject.getId());
370             String cantFindGroupName = this.subject.getAttributeValue("displayName");
371             String cantFindGroupExtension = this.subject.getAttributeValue("displayExtension");
372             group.setNameDb(cantFindGroupName);
373             group.setDisplayNameDb(cantFindGroupName);
374             group.setExtensionDb(cantFindGroupExtension);
375             group.setDisplayExtensionDb(cantFindGroupExtension);
376             group.setDescriptionDb(cantFindGroupName);
377           }
378         }
379         
380         if (group != null) {
381           GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setGuiGroup(new GuiGroup(group));
382           this.screenLabelShort2html = TextContainer.retrieveFromRequest().getText().get("guiGroupShortLink");
383         } else {
384           
385           this.screenLabelShort2html = TextContainer.retrieveFromRequest().getText().get("guiSubjectShortLink");
386         }
387         
388         GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setShowIcon(true);
389 
390         if (group != null) {
391           this.screenLabelLongWithIcon = TextContainer.retrieveFromRequest().getText().get("guiGroupLongLinkWithIcon");
392         } else {
393           this.screenLabelLongWithIcon = TextContainer.retrieveFromRequest().getText().get("guiSubjectLongLinkWithIcon");
394         }
395         
396 
397         if (group != null) {
398           this.screenLabelShort2htmlWithIcon = TextContainer.retrieveFromRequest().getText().get("guiGroupShortLink");
399         } else {
400           this.screenLabelShort2htmlWithIcon = TextContainer.retrieveFromRequest().getText().get("guiSubjectShortLink");
401         }
402         
403         if (group != null) {
404           this.screenLabelShort2noLinkWithIcon = TextContainer.retrieveFromRequest().getText().get("guiGroupShort");
405         } else {
406           this.screenLabelShort2noLinkWithIcon = TextContainer.retrieveFromRequest().getText().get("guiSubjectShort");
407         }
408         
409         GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setShowIcon(false);
410 
411         if (group != null) {
412           this.screenLabelShort2noLink = TextContainer.retrieveFromRequest().getText().get("guiGroupShort");
413         } else {
414           this.screenLabelShort2noLink = TextContainer.retrieveFromRequest().getText().get("guiSubjectShort");
415         }
416 
417       } finally {
418 
419         GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setGuiSubject(null);
420         GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setShowTooltip(false);
421         GrouperRequestContainer.retrieveFromRequestOrCreate().getCommonRequestContainer().setShowIcon(false);
422 
423       }
424 
425     }
426   }
427 
428   /**
429    * short screen label for ui v2
430    * @return label
431    */
432   public String getScreenLabelShort2() {
433     this.initScreenLabels();
434     return this.screenLabelShort2;
435   }
436 
437   /**
438    * span for subject-specific icon for ui v2; e.g. '&lt;i class="fa fa-group"&gt;&lt;/i&gt; '
439    * @return label
440    */
441   public String getScreenSubjectIcon2Html() {
442     this.initScreenLabels();
443     return this.screenSubjectIcon2Html;
444   }
445 
446   /**
447    * construct with subject
448    * @param subject1
449    */
450   public GuiSubject(Subject subject1) {
451     this.subject = subject1;
452   }
453   
454   /**
455    * get screen label
456    * @return screen label
457    */
458   public String getScreenLabel() {
459     this.initScreenLabels();
460     return this.screenLabelShort;
461   }
462   
463   /**
464    * get screen label
465    * @return screen label
466    */
467   public String getScreenLabelLong() {
468     this.initScreenLabels();
469     return this.screenLabelLong;
470   }
471   
472   /** attributes in string - string format */
473   private Map<String, String> attributes = null;
474   /**
475    * long screen label
476    */
477   private String screenLabelLong = null;
478   /**
479    * long screen label with icon
480    */
481   private String screenLabelLongWithIcon = null;
482 
483   /**
484    * long screen label with icon
485    * @return screen label
486    */
487   public String getScreenLabelLongWithIcon() {
488     this.initScreenLabels();
489     return this.screenLabelLongWithIcon;
490   }
491 
492   /**
493    * short screen label
494    */
495   private String screenLabelShort = null;
496 
497   /**
498    * new short label in v2
499    */
500   private String screenLabelShort2 = null;
501   
502   /**
503    * new short label in v2 with html tooltip and link
504    */
505   private String screenLabelShort2html = null;
506 
507   /**
508    * new subject icon in v2
509    */
510   private String screenSubjectIcon2Html = null;
511 
512   /**
513    * new short label in v2 with html tooltip and link and icon
514    */
515   private String screenLabelShort2htmlWithIcon = null;
516   
517   /**
518    * new short label in v2 with no link, but with icon
519    */
520   private String screenLabelShort2noLinkWithIcon = null;
521 
522   /**
523    * new short label in v2 with no link or icon
524    */
525   private String screenLabelShort2noLink = null;
526 
527   
528   /**
529    * subject
530    * @return the subject
531    */
532   public Subject getSubject() {
533     return this.subject;
534   }
535   
536   /**
537    * attribute names for this subject
538    * @return the attribute names for this subject
539    */
540   public Set<String> getAttributeNamesNonInternal() {
541     Set<String> attributeNames = new LinkedHashSet<String>();
542     if (this.subject != null) {
543       String emailAttributeName = GrouperEmailUtils.emailAttributeNameForSource(this.subject.getSourceId());
544   
545       for (String attributeName : GrouperUtil.nonNull(this.getAttributes().keySet())) {
546         if (!StringUtils.equalsIgnoreCase("name", attributeName)
547             && !StringUtils.equalsIgnoreCase("description", attributeName)
548             && !StringUtils.equalsIgnoreCase("subjectId", attributeName)
549             && !StringUtils.equalsIgnoreCase(emailAttributeName, attributeName)) {
550           attributeNames.add(attributeName);
551         }
552       }
553     }
554     return attributeNames;
555   }
556   
557   /**
558    * attribute names for this subject to show in the expanded view
559    * @return the attribute names for this subject
560    */
561   public Set<String> getAttributeNamesExpandedView() {
562     Set<String> attributeNames = new LinkedHashSet<String>();
563     if (this.subject != null) {
564       String orderCommaSeparated = GrouperUiConfig.retrieveConfig().propertyValueString("subject2.attributes.order.expanded." + this.subject.getSourceId());
565       if (GrouperUtil.isBlank(orderCommaSeparated)) {
566         orderCommaSeparated = GrouperUiConfig.retrieveConfig().propertyValueString("subject2.attributes.order.expanded.default");
567       }
568       
569       // still empty, return them all to preserve previous behavior
570       if (GrouperUtil.isBlank(orderCommaSeparated)) {
571         return getAttributeNamesNonInternal();
572       }
573       
574       attributeNames.addAll(GrouperUtil.splitTrimToSet(orderCommaSeparated, ","));
575     }
576     return attributeNames;
577   }
578   
579   
580   /**
581    * attribute names for this subject to show in the non-expanded view
582    * @return the attribute names for this subject
583    */
584   public Set<String> getAttributeNamesNonExpandedView() {
585     Set<String> attributeNames = new LinkedHashSet<String>();
586     if (this.subject != null) {
587       String orderCommaSeparated = GrouperUiConfig.retrieveConfig().propertyValueString("subject2.attributes.order.nonexpanded." + this.subject.getSourceId());
588       if (GrouperUtil.isBlank(orderCommaSeparated)) {
589         orderCommaSeparated = GrouperUiConfig.retrieveConfig().propertyValueString("subject2.attributes.order.nonexpanded.default", "subjectId,email,name,description");
590       }
591       
592       if (!GrouperUtil.isBlank(orderCommaSeparated)) {
593 
594         // GRP-1558: default subject display shows "email" attribute, which source might not have
595         if (orderCommaSeparated.contains(",email,")) {
596 
597           if (!this.getAttributes().containsKey("email")) {
598             String emailAttribute = GrouperEmailUtils.emailAttributeNameForSource(this.subject.getSourceId());
599             if (!StringUtils.isBlank(emailAttribute)) {
600               orderCommaSeparated = GrouperUtil.replace(orderCommaSeparated, ",email,", "," + emailAttribute + ",");
601             } else {
602               orderCommaSeparated = GrouperUtil.replace(orderCommaSeparated, ",email,", ",");
603             }
604           }
605         }
606         
607         attributeNames.addAll(GrouperUtil.splitTrimToSet(orderCommaSeparated, ","));
608       }
609     }
610     return attributeNames;
611   }
612 
613   /**
614    * dynamic map of attribute name to attribute label
615    */
616   private Map<String, String> attributeLabelMap = new HashMap<String, String>() {
617 
618     /**
619      * @see java.util.HashMap#get(java.lang.Object)
620      */
621     @Override
622     public String get(Object attributeName) {
623       
624       String sourceId = GuiSubject.this.getSubject().getSourceId();
625       String sourceTextId = GrouperUiUtils.convertSourceIdToTextId(sourceId);
626       
627       String emailAttribute = GrouperEmailUtils.emailAttributeNameForSource(GuiSubject.this.subject.getSourceId());
628       
629       // subjectViewLabel__sourceTextId__attributeName
630       String key = "subjectViewLabel__" + sourceTextId + "__" + attributeName;
631       
632       if ("sourceId".equals(attributeName)) {
633         key = "subjectViewLabelSourceId";
634       } else if ("sourceName".equals(attributeName)) {
635         key = "subjectViewLabelSourceName";
636       } else if ("memberId".equals(attributeName)) {
637         key = "subjectViewLabelMemberId";
638       } else if ("subjectId".equals(attributeName)) {
639         key = "subjectViewLabelId";
640       } else if ("name".equals(attributeName)) {
641         key = "subjectViewLabelName";
642       } else if ("description".equals(attributeName)) {
643         key = "subjectViewLabelDescription";
644       } else if (!GrouperUtil.isBlank(emailAttribute) && emailAttribute.equals(attributeName)) {
645         key = "subjectViewLabelEmail";
646       }
647       
648       String value = TextContainer.textOrNull(key);
649       
650       if (StringUtils.isBlank(value)) {
651         return ((String)attributeName) + ":";
652       }
653       return value;
654       
655     }
656 
657   };
658   
659   
660   /**
661    * dynamic map of attribute name to attribute friendly description
662    */
663   private Map<String, String> attributeNameFriendlyDescriptionMap = new HashMap<String, String>() {
664 
665     /**
666      * @see java.util.HashMap#get(java.lang.Object)
667      */
668     @Override
669     public String get(Object attributeName) {
670       
671       String sourceId = GuiSubject.this.getSubject().getSourceId();
672       String sourceTextId = GrouperUiUtils.convertSourceIdToTextId(sourceId);
673       
674       String emailAttribute = GrouperEmailUtils.emailAttributeNameForSource(GuiSubject.this.subject.getSourceId());
675       
676       // subjectViewFriendlyDescription__sourceTextId__attributeName
677       String key = "subjectViewFriendlyDescription__" + sourceTextId + "__" + attributeName;
678       
679       if ("sourceId".equals(attributeName)) {
680         key = "subjectViewFriendlyDescriptionSourceId";
681       } else if ("sourceName".equals(attributeName)) {
682         key = "subjectViewFriendlyDescriptionSourceName";
683       } else if ("memberId".equals(attributeName)) {
684         key = "subjectViewFriendlyDescriptionMemberId";
685       } else if ("subjectId".equals(attributeName)) {
686         key = "subjectViewFriendlyDescriptionId";
687       } else if ("name".equals(attributeName)) {
688         key = "subjectViewFriendlyDescriptionName";
689       } else if ("description".equals(attributeName)) {
690         key = "subjectViewFriendlyDescriptionDescription";
691       } else if (!GrouperUtil.isBlank(emailAttribute) && emailAttribute.equals(attributeName)) {
692         key = "subjectViewFriendlyDescriptionEmail";
693       }
694       
695       String value = TextContainer.textOrNull(key);
696       
697       if (StringUtils.isBlank(value)) {
698         return "";
699       }
700       
701       value = StringUtils.replace(value, "$$attributeName$$", GrouperUtil.stringValue(attributeName));
702       
703       return value;
704       
705     }
706 
707   };
708 
709   
710   /**
711    * attribute label for this attribute if configured
712    * first get the text id for the source, then look in the externalized text
713    * for a label for the attribute, if not there, just use the attribute name
714    * @return the attribute label for this attribute
715    */
716   public Map<String, String> getAttributeLabel() {
717     
718     return this.attributeLabelMap;
719     
720   }
721   
722   
723   public Map<String, String> getAttributeNameFriendlyDescripton() {
724     
725     return this.attributeNameFriendlyDescriptionMap;
726     
727   }
728   
729   /**
730    * Gets a map attribute names and value. The map's key
731    * contains the attribute name and the map's value
732    * contains a Set of attribute value(s).  Note, this only does single valued attributes
733    * @return the map of attributes
734    */
735   @SuppressWarnings({ "cast", "unchecked" })
736   public Map<String, String> getAttributes() {
737     if (this.attributes == null) {
738       Map<String, String> result = new LinkedHashMap<String, String>();
739       
740       if (this.subject != null) {
741         for (String key : (Set<String>)(Object)GrouperUtil.nonNull(this.subject.getAttributes()).keySet()) {
742           Object value = this.subject.getAttributes().get(key);
743           if (value instanceof String) {
744             //if a string
745             result.put(key, (String)value);
746           } else if (value instanceof Set) {
747             //if set of one string, then add it
748             if (((Set<?>)value).size() == 1) {
749               result.put(key, (String)((Set<?>)value).iterator().next());
750             } else if (((Set<?>)value).size() > 1) {
751               //put commas in between?  not sure what else to do here
752               result.put(key, GrouperUtil.setToString((Set<?>)value));
753             }
754           }
755         }
756       }
757       
758       result.put("memberId", getMemberId());
759       result.put("sourceId", this.subject.getSourceId());
760       result.put("sourceName", this.subject.getSource().getName());
761       result.put("subjectId", this.subject.getId());
762       
763       Member theMember = this.getMember();
764       
765       {
766         String name = this.subject.getName();
767         if (this.subject instanceof UnresolvableSubject && theMember != null && !StringUtils.isBlank(theMember.getName())) {
768           name = theMember.getName();
769         }
770         result.put("name",  name);
771       }
772       
773       {
774         String description = this.subject.getDescription();
775         if (this.subject instanceof UnresolvableSubject && theMember != null && !StringUtils.isBlank(theMember.getDescription())) {
776           description = theMember.getDescription();
777         }
778         result.put("description",  description);
779         
780       }
781       
782       this.attributes = result;
783     }
784     return this.attributes;
785   }
786 
787   /**
788    * 
789    * @return long label if different than the short one
790    */
791   public String getScreenLabelLongIfDifferent() {
792     this.initScreenLabels();
793     if (this.isNeedsTooltip()) {
794       return this.screenLabelLong;
795     }
796     return null;
797   }
798 
799   /**
800    * get short screen label 
801    * @return short screen label
802    */
803   public boolean isNeedsTooltip() {
804     this.initScreenLabels();
805     return !StringUtils.equals(this.screenLabelLong, this.screenLabelShort);
806   }
807 
808 
809   /**
810    * e.g. &lt;a href="#"&gt;John Smith&lt;/a&gt;
811    * @return short link
812    */
813   public String getScreenLabelShort2noLinkWithIcon() {
814     this.initScreenLabels();
815     return this.screenLabelShort2noLinkWithIcon;
816   }
817 
818 
819   /**
820    * 
821    * @param subject
822    * @param attrName
823    * @return the value
824    */
825   public static String attributeValue(Subject subject, String attrName) {
826     if (StringUtils.equalsIgnoreCase("screenLabel", attrName)) {
827       return GrouperUiUtils.convertSubjectToLabel(subject);
828     }
829     if (subject == null) { 
830       return null;
831     }
832     if (StringUtils.equalsIgnoreCase("subjectId", attrName)) {
833       return subject.getId();
834     }
835     if (StringUtils.equalsIgnoreCase("name", attrName)) {
836       return subject.getName();
837     }
838     if (StringUtils.equalsIgnoreCase("description", attrName)) {
839       return subject.getDescription();
840     }
841     if (StringUtils.equalsIgnoreCase("typeName", attrName)) {
842       return subject.getType().getName();
843     }
844     if (StringUtils.equalsIgnoreCase("sourceId", attrName)) {
845       return subject.getSource().getId();
846     }
847     if (StringUtils.equalsIgnoreCase("sourceName", attrName)) {
848       return subject.getSource().getName();
849     }
850     //TODO switch this to attribute values comma separated
851     return subject.getAttributeValue(attrName);
852   }
853 
854   /**
855    * cant get grouper object
856    */
857   @Override
858   public GrouperObject getGrouperObject() {
859     return null;
860   }
861 
862   /**
863    * if this is a subject
864    */
865   @Override
866   public boolean isSubjectType() {
867     return true;
868   }
869 
870   /**
871    * path colon separated not applicable
872    */
873   @Override
874   public String getPathColonSpaceSeparated() {
875     return "";
876   }
877 
878   /**
879    * not applicable
880    */
881   @Override
882   public String getNameColonSpaceSeparated() {
883     return this.getScreenLabelShort2noLink();
884   }
885 
886   /**
887    * not applicable
888    */
889   @Override
890   public String getTitle() {
891     return "";
892   }
893   
894   /**
895    * @return true if this is a local entity that is disabled
896    */
897   public boolean isLocalEntityDisabled() {
898     if (this.subject == null) {
899       return false;
900     }
901     
902     if (!this.subject.getSourceId().equals("grouperEntities")) {
903       return false;
904     }
905     return (Boolean)GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
906       
907       @Override
908       public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
909         Group group = GroupFinder.findByUuid(GrouperSession.staticGrouperSession(), GuiSubject.this.subject.getId(), false);
910         if (group == null) {
911           return false;
912         }
913         
914         return !group.isEnabled();
915       }
916     });
917   }
918 }