package net.sourceforge.fenixedu.presentationTier.renderers;
import java.util.SortedSet;
import java.util.TreeSet;
import net.sourceforge.fenixedu.domain.Person;
import net.sourceforge.fenixedu.domain.organizationalStructure.Accountability;
import net.sourceforge.fenixedu.domain.organizationalStructure.Contract;
import net.sourceforge.fenixedu.domain.organizationalStructure.Function;
import net.sourceforge.fenixedu.domain.organizationalStructure.PersonFunction;
import net.sourceforge.fenixedu.domain.organizationalStructure.Unit;
import net.sourceforge.fenixedu.presentationTier.servlets.filters.ChecksumRewriter;
import org.joda.time.YearMonthDay;
import pt.ist.fenixWebFramework.renderers.OutputRenderer;
import pt.ist.fenixWebFramework.renderers.components.Face;
import pt.ist.fenixWebFramework.renderers.components.HtmlComponent;
import pt.ist.fenixWebFramework.renderers.components.HtmlLink;
import pt.ist.fenixWebFramework.renderers.components.HtmlLinkWithPreprendedComment;
import pt.ist.fenixWebFramework.renderers.components.HtmlList;
import pt.ist.fenixWebFramework.renderers.components.HtmlListItem;
import pt.ist.fenixWebFramework.renderers.components.HtmlText;
import pt.ist.fenixWebFramework.renderers.layouts.Layout;
import pt.ist.fenixWebFramework.renderers.utils.RenderKit;
import pt.ist.fenixWebFramework.renderers.utils.RenderUtils;
public class UnitStructureRenderer extends OutputRenderer {
private String rootUnitClasses;
private String unitClasses;
private String levelClasses;
private String employeesSectionClasses;
private String employeesClasses;
private String functionsClasses;
private String implicitFunctionsClasses;
private String membersClasses;
private String homepageLocation;
private boolean showEmployeesSection;
private boolean showOnlyPeopleWithFunctions;
private boolean showEmptyEmployeesSection;
private boolean showEmptyFunctions;
private boolean showImplicityFunctionLabel;
private boolean showDates;
private String bundle;
private String employeesSectionKey;
private String implicityFunctionKey;
private String dateFromKey;
private String dateToKey;
private String dateSinceKey;
public boolean isShowOnlyPeopleWithFunctions() {
return showOnlyPeopleWithFunctions;
}
public void setShowOnlyPeopleWithFunctions(boolean showPeopleWithFunctions) {
this.showOnlyPeopleWithFunctions = showPeopleWithFunctions;
}
public boolean isShowEmployeesSection() {
return showEmployeesSection;
}
public String getBundle() {
return bundle;
}
/**
* Chooses the bundle for all messages. If no bundle is specified the
* default bundle for the current module is used.
*
* @property
*/
public void setBundle(String bundle) {
this.bundle = bundle;
}
public String getDateFromKey() {
return dateFromKey;
}
/**
* The resource key to be used when displaying a date in the form:
* key <begin> to <end>.
*
* @property
*/
public void setDateFromKey(String dateFromKey) {
this.dateFromKey = dateFromKey;
}
public String getDateSinceKey() {
return dateSinceKey;
}
/**
* The resource key to be used when displaying a date in the form:
* key <begin>.
*
* @property
*/
public void setDateSinceKey(String dateSinceKey) {
this.dateSinceKey = dateSinceKey;
}
public String getDateToKey() {
return dateToKey;
}
/**
* The resource key to be used when displaying a date in the form: From
* <begin> key <end>.
*
* @property
*/
public void setDateToKey(String dateToKey) {
this.dateToKey = dateToKey;
}
public String getEmployeesClasses() {
return employeesClasses;
}
/**
* Sets the classes to be used when displaying each employee item.
*
* @property
*/
public void setEmployeesClasses(String employeesClasses) {
this.employeesClasses = employeesClasses;
}
public String getEmployeesSectionClasses() {
return employeesSectionClasses;
}
/**
* Sets the classes to be used in the employees section item.
*
* @property
*/
public void setEmployeesSectionClasses(String employeesSectionClasses) {
this.employeesSectionClasses = employeesSectionClasses;
}
public String getEmployeesSectionKey() {
return employeesSectionKey;
}
/**
* Sets the key to be used has the label of the employees section.
*
* @property
*/
public void setEmployeesSectionKey(String employeesSectionKey) {
this.employeesSectionKey = employeesSectionKey;
}
public String getFunctionsClasses() {
return functionsClasses;
}
/**
* Sets the classes to use in each function item.
*
* @property
*/
public void setFunctionsClasses(String functionsClasses) {
this.functionsClasses = functionsClasses;
}
public String getHomepageLocation() {
return homepageLocation;
}
/**
* Indicates the url for each person's homepage. The url can have the form
* /path/%s/subpath
where %s
will be replaced by
* the person's system's username.
*
* @property
*/
public void setHomepageLocation(String homepageLocation) {
this.homepageLocation = homepageLocation;
}
public String getImplicitFunctionsClasses() {
return implicitFunctionsClasses;
}
/**
* Sets the classe sto use when displaying an implicit function item. If
* this property is not set then the normal function classes will be used.
*
* @param implicitFunctionsClasses
*/
public void setImplicitFunctionsClasses(String implicitFunctionsClasses) {
this.implicitFunctionsClasses = implicitFunctionsClasses;
}
public String getImplicityFunctionKey() {
return implicityFunctionKey;
}
/**
* Sets the key to be used has the label used to mark implicity functions.
*
* @property
*/
public void setImplicityFunctionKey(String implicityFunctionKey) {
this.implicityFunctionKey = implicityFunctionKey;
}
public String getLevelClasses() {
return levelClasses;
}
/**
* Sets the classes to use to mark each successive list. To the value given
* here, the renderer will append the depth of the inner list.
*
* @property
*/
public void setLevelClasses(String levelClasses) {
this.levelClasses = levelClasses;
}
public String getMembersClasses() {
return membersClasses;
}
/**
* Sets the classes to use when displaying each person's item.
*
* @property
*/
public void setMembersClasses(String membersClasses) {
this.membersClasses = membersClasses;
}
public String getRootUnitClasses() {
return rootUnitClasses;
}
/**
* Chooses the classes to apply to he root unit.
*
* @property
*/
public void setRootUnitClasses(String rootUnitClasses) {
this.rootUnitClasses = rootUnitClasses;
}
public boolean isShowEmptyEmployeesSection() {
return showEmptyEmployeesSection;
}
/**
* Chooses if the employees sections is shown when there are not direct
* employees.
*
* @property
*/
public void setShowEmptyEmployeesSection(boolean showEmptyEmployeesSection) {
this.showEmptyEmployeesSection = showEmptyEmployeesSection;
}
public boolean isShowEmptyFunctions() {
return showEmptyFunctions;
}
/**
* Chooses if each function is shown when there aren't persons under that
* function.
*
* @property
*/
public void setShowEmptyFunctions(boolean showEmptyFunctions) {
this.showEmptyFunctions = showEmptyFunctions;
}
public String getUnitClasses() {
return unitClasses;
}
/**
* Sets the classes to be used when generating each unit's item.
*
* @property
*/
public void setUnitClasses(String unitClasses) {
this.unitClasses = unitClasses;
}
/**
* Indicates if all employees should be grouped under an employees section.
*
* @property
*/
public void setShowEmployeesSection(boolean showEmployeesSection) {
this.showEmployeesSection = showEmployeesSection;
}
public boolean isShowImplicityFunctionLabel() {
return showImplicityFunctionLabel;
}
/**
* Indicates if each implicity function should be added a label.
*
* @property
*/
public void setShowImplicityFunctionLabel(boolean showImplicityFunctionLabel) {
this.showImplicityFunctionLabel = showImplicityFunctionLabel;
}
protected Layout getLayout(Object object, Class type) {
return new OrganigramLayout();
}
protected class OrganigramLayout extends Layout {
@Override
public HtmlComponent createComponent(Object object, Class type) {
Unit unit = (Unit) object;
return createStructure(unit, new HtmlList(), 1);
}
}
protected HtmlComponent createStructure(Unit unit, HtmlList root, int level) {
HtmlListItem item = createItem(root, getUnitClasses(level), generateUnitName(unit, getUnitFace(level)));
HtmlList elements = createList(level, null);
if (!isShowOnlyPeopleWithFunctions()) {
addEmployees(unit, elements, level);
}
addFunctions(unit, elements, level);
addSubUnits(unit, elements, level);
if (!elements.getChildren().isEmpty()) {
item.addChild(elements);
}
return root;
}
protected String getUnitClasses(int level) {
return level == 1 ? getRootUnitClasses() : getUnitClasses();
}
protected Face getUnitFace(int level) {
return level == 1 ? Face.H3 : Face.STRONG;
}
protected HtmlList createList(int level, HtmlListItem item) {
HtmlList list = new HtmlList();
if (getLevelClasses() != null) {
list.setClasses(getLevelClasses() + level);
}
if (item != null) {
item.addChild(list);
}
return list;
}
protected HtmlComponent generateUnitName(Unit unit, Face face) {
HtmlText header = new HtmlText(unit.getNameI18n().getContent());
header.setFace(face);
return header;
}
protected void addEmployees(Unit unit, HtmlList root, int level) {
SortedSet persons = getPersonsWithContract(unit, new TreeSet(Person.COMPARATOR_BY_NAME_AND_ID));
HtmlList destiny = root;
if ((isShowEmployeesSection() && !persons.isEmpty()) || isShowEmptyEmployeesSection()) {
HtmlListItem employeesItem = createItem(root, getEmployeesSectionClasses(), getEmployeesSectionKey(), Face.STRONG);
if (!persons.isEmpty()) {
destiny = createList(level + 1, employeesItem);
}
}
for (Person person : persons) {
addEmployee(person, destiny);
}
}
private SortedSet getPersonsWithContract(Unit unit, SortedSet persons) {
for (Contract contract : unit.getWorkingContracts()) {
if (!isAccountabilityVisible(contract)) {
continue;
}
persons.add(contract.getPerson());
}
return persons;
}
protected HtmlListItem createItem(HtmlList root, String classes, String key, Face face) {
return createItem(root, classes, key, true, face);
}
protected HtmlListItem createItem(HtmlList root, String classes, String key, boolean resource, Face face) {
return createItem(root, classes, createLabel(key, resource, face));
}
protected HtmlListItem createItem(HtmlList root, String classes, HtmlComponent component) {
HtmlListItem item = root.createItem();
item.setClasses(classes);
if (component != null) {
item.addChild(component);
}
return item;
}
protected HtmlComponent createLabel(String key, boolean resource, Face face) {
HtmlText text = new HtmlText(resource ? getString(key) : key);
text.setFace(face);
return text;
}
protected String getString(String key) {
return RenderUtils.getResourceString(getBundle(), key);
}
protected boolean isAccountabilityVisible(Accountability accountability) {
return accountability.isActive(new YearMonthDay());
}
private void addEmployee(Person person, HtmlList root) {
createItem(root, getEmployeesClasses(), generatePerson(person));
}
protected HtmlComponent generatePerson(Person person) {
HtmlComponent name = generatePersonName(person);
if (person.isHomePageAvailable()) {
HtmlLink link = new HtmlLinkWithPreprendedComment(ChecksumRewriter.NO_CHECKSUM_PREFIX_HAS_CONTEXT_PREFIX);
link.setUrl(String.format(getHomepageLocation(), person.getIstUsername()));
link.setModuleRelative(false);
link.setIndented(false);
link.setBody(name);
name = link;
}
return name;
}
private HtmlComponent generatePersonName(Person person) {
RenderKit instance = RenderKit.getInstance();
return instance.renderUsing(new PersonNameRenderer(), getContext(), person.getName(), String.class);
}
protected void addFunctions(Unit unit, HtmlList root, int level) {
for (Function function : unit.getOrderedFunctions()) {
SortedSet pfs = getPersonFunctions(function, new TreeSet(
PersonFunction.COMPARATOR_BY_PERSON_NAME));
if (!pfs.isEmpty() || isShowEmptyFunctions()) {
HtmlListItem item = createItem(root, getFunctionClasses(function), function.getTypeName().getContent(), false,
Face.STRONG);
if (function.isInherentFunction() && isShowImplicityFunctionLabel()) {
addImplicitMark(item);
}
if (!pfs.isEmpty()) {
HtmlList persons = createList(level + 1, item);
for (PersonFunction pf : pfs) {
addPersonFunction(pf, persons);
}
}
}
}
}
private String getFunctionClasses(Function function) {
if (function.isInherentFunction() && getImplicitFunctionsClasses() != null) {
return getImplicitFunctionsClasses();
} else {
return getFunctionsClasses();
}
}
protected void addImplicitMark(HtmlListItem item) {
HtmlText mark = new HtmlText("(" + getString(getImplicityFunctionKey()) + ")");
mark.setFace(Face.STANDARD);
item.addChild(mark);
}
protected SortedSet getPersonFunctions(Function function, TreeSet collection) {
for (PersonFunction pf : function.getPersonFunctions()) {
if (!isAccountabilityVisible(pf)) {
continue;
}
collection.add(pf);
}
return collection;
}
private void addPersonFunction(PersonFunction pf, HtmlList root) {
HtmlComponent name = generatePerson(pf.getPerson());
HtmlComponent range = generateDateRange(pf.getBeginDate(), pf.getEndDate());
HtmlListItem item = root.createItem();
item.setClasses(getMembersClasses());
item.addChild(name);
if (isShowDates()) {
item.addChild(range);
}
}
protected HtmlComponent generateDateRange(YearMonthDay beginDate, YearMonthDay endDate) {
HtmlText text = new HtmlText();
if (endDate == null) {
text.setText(String.format("(%s %s)", getString(getDateSinceKey()), beginDate));
} else {
text.setText(String.format("(%s %s %s %s)", getString(getDateFromKey()), beginDate, getString(getDateToKey()),
endDate));
}
text.setFace(Face.STANDARD);
return text;
}
protected void addSubUnits(Unit unit, HtmlList root, int level) {
SortedSet units = new TreeSet(Unit.COMPARATOR_BY_NAME_AND_ID);
units.addAll(unit.getActiveSubUnits(new YearMonthDay()));
for (Unit sub : units) {
if (hasPersonFunctions(sub)) {
createStructure(sub, root, level + 1);
}
}
}
public boolean isShowDates() {
return showDates;
}
public void setShowDates(boolean showDates) {
this.showDates = showDates;
}
private boolean hasPersonFunctions(Unit unit) {
for (Function function : unit.getOrderedActiveFunctions()) {
if (!getPersonFunctions(function, new TreeSet(PersonFunction.COMPARATOR_BY_PERSON_NAME)).isEmpty()) {
return true;
}
}
return false;
}
}