/** * ESUP-Portail Helpdesk - Copyright (c) 2004-2009 ESUP-Portail consortium. */ package org.esupportail.helpdesk.web.controllers; import java.util.ArrayList; import java.util.List; import javax.faces.model.SelectItem; import org.apache.myfaces.custom.tree2.TreeState; import org.esupportail.commons.aop.cache.RequestCache; import org.esupportail.commons.services.authentication.AuthUtils; import org.esupportail.commons.services.logging.Logger; import org.esupportail.commons.services.logging.LoggerImpl; import org.esupportail.helpdesk.domain.FaqScope; import org.esupportail.helpdesk.domain.beans.Department; import org.esupportail.helpdesk.domain.beans.Faq; import org.esupportail.helpdesk.domain.beans.FaqEvent; import org.esupportail.helpdesk.domain.beans.User; import org.esupportail.helpdesk.web.beans.AbstractFirstLastNode; import org.esupportail.helpdesk.web.beans.FaqNode; import org.esupportail.helpdesk.web.beans.FaqScopeI18nKeyProvider; import org.esupportail.helpdesk.web.beans.FaqTreeModel; import org.springframework.util.StringUtils; /** * A bean to manage FAQs. */ public class FaqsController extends AbstractContextAwareController { /** * The serialization id. */ private static final long serialVersionUID = -3933417227566291045L; /** * A logger. */ private final Logger logger = new LoggerImpl(getClass()); /** * The default FAQ scope i18n key suffix of the current element. */ private String defaultFaqScopeI18nSuffix; /** * The current department. */ private Department department; /** * The department to update. */ private Department departmentToUpdate; /** * The current FAQ. */ private Faq faq; /** * The FAQ to update. */ private Faq faqToUpdate; /** * The sub FAQs of the current element. */ private List<Faq> subFaqs; /** * The tree model for FAQs. */ private FaqTreeModel viewTree; /** * The tree model to move FAQs. */ private FaqTreeModel moveTree; /** * True for the edit interface. */ private boolean editInterface; /** * True if the current user can edit the current item. */ private boolean userCanEdit; /** * The target department. */ private Department targetDepartment; /** * The target FAQ. */ private Faq targetFaq; /** * Bean constructor. */ public FaqsController() { super(); } /** * @see org.esupportail.helpdesk.web.controllers.AbstractContextAwareController#afterPropertiesSetInternal() */ @Override public void afterPropertiesSetInternal() { super.afterPropertiesSetInternal(); } /** * @see org.esupportail.helpdesk.web.controllers.AbstractContextAwareController#reset() */ @Override public void reset() { super.reset(); viewTree = null; moveTree = null; department = null; faq = null; subFaqs = null; editInterface = false; } /** * @see java.lang.Object#toString() */ @Override public String toString() { return getClass().getSimpleName() + "#" + hashCode(); } /** * @return true if the current user is allowed to acces the view. */ @RequestCache public boolean isPageAuthorized() { User user = getCurrentUser(); if (getDomainService().userCanEditFaqs(user)) { return true; } if (getDomainService().hasVisibleFaq(user, getClient())) { return true; } return false; } /** * JSF callback. * @return a String. */ public String enter() { if (!isPageAuthorized()) { return null; } updateInterface(); return "navigationFaqs"; } /** * @return the departments visible for the FAQs. */ protected List<Department> getVisibleDepartments() { return getDomainService().getFaqViewDepartments( getCurrentUser(), getSessionController().getClient()); } /** * @param theFaq * @return true if the current user can view a FAQ. */ @RequestCache public boolean userCanViewFaq(final Faq theFaq) { return getDomainService().userCanViewFaq( getCurrentUser(), theFaq, getVisibleDepartments()); } /** * @param authType * @return the params for deep links on the page. */ protected String getPermLink(final String authType) { return getUrlBuilder().getFaqsUrl(authType, faq); } /** * @return a permanent link to the page for application users. */ public String getApplicationPermLink() { return getPermLink(AuthUtils.APPLICATION); } /** * @return a permanent link to the page for CAS users. */ public String getCasPermLink() { return getPermLink(AuthUtils.CAS); } /** * @return a permanent link to the page for Shibboleth users. */ public String getShibbolethPermLink() { return getPermLink(AuthUtils.SHIBBOLETH); } /** * @return a permanent link to the page for specific users. */ public String getSpecificPermLink() { return getPermLink(AuthUtils.SPECIFIC); } /** * @return the scopeItems for a department */ @RequestCache protected List<SelectItem> getDepartmentScopeItems() { if (!userCanEdit) { return null; } List<SelectItem> scopeItems = new ArrayList<SelectItem>(); scopeItems.add(new SelectItem( FaqScope.DEFAULT, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.DEFAULT)))); scopeItems.add(new SelectItem( FaqScope.ALL, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.ALL)))); scopeItems.add(new SelectItem( FaqScope.AUTHENTICATED, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.AUTHENTICATED)))); scopeItems.add(new SelectItem( FaqScope.MANAGER, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.MANAGER)))); return scopeItems; } /** * @param parentEffectiveScope * @param root * @return the scopeItems for a FAQ entity */ @RequestCache protected List<SelectItem> getFaqEntityScopeItems( final String parentEffectiveScope, final boolean root) { if (!userCanEdit) { return null; } List<SelectItem> scopeItems = new ArrayList<SelectItem>(); scopeItems.add(new SelectItem( FaqScope.DEFAULT, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.DEFAULT)))); if (FaqScope.ALL.equals(parentEffectiveScope)) { scopeItems.add(new SelectItem( FaqScope.ALL, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.ALL)))); } if (FaqScope.ALL.equals(parentEffectiveScope) || FaqScope.AUTHENTICATED.equals(parentEffectiveScope)) { scopeItems.add(new SelectItem( FaqScope.AUTHENTICATED, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.AUTHENTICATED)))); } if (root) { scopeItems.add(new SelectItem( FaqScope.MANAGER, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.ADMIN)))); } else { if (FaqScope.ALL.equals(parentEffectiveScope) || FaqScope.AUTHENTICATED.equals(parentEffectiveScope) || FaqScope.DEPARTMENT.equals(parentEffectiveScope)) { scopeItems.add(new SelectItem( FaqScope.DEPARTMENT, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.DEPARTMENT)))); } scopeItems.add(new SelectItem( FaqScope.MANAGER, getString(FaqScopeI18nKeyProvider.getI18nKey(FaqScope.MANAGER)))); } return scopeItems; } /** * @return the scopeItems */ @RequestCache public List<SelectItem> getScopeItems() { if (!userCanEdit) { return null; } if (department != null) { return getDepartmentScopeItems(); } if (faq == null) { return null; } String parentEffectiveScope; boolean root = faq.getDepartment() == null; if (faq.getParent() == null) { if (root) { parentEffectiveScope = getDomainService().getDepartmentDefaultFaqScope(); } else { parentEffectiveScope = faq.getDepartment().getEffectiveDefaultFaqScope(); } } else { parentEffectiveScope = faq.getParent().getEffectiveScope(); } return getFaqEntityScopeItems(parentEffectiveScope, root); } /** * @return the interfaceItems */ @RequestCache public List<SelectItem> getInterfaceItems() { if (!getDomainService().userCanEditFaqs(getCurrentUser())) { editInterface = false; return null; } List<SelectItem> interfaceItems = new ArrayList<SelectItem>(); interfaceItems.add(new SelectItem( Boolean.TRUE, getString("FAQS.INTERFACE_ITEM.EDIT"))); interfaceItems.add(new SelectItem( Boolean.FALSE, getString("FAQS.INTERFACE_ITEM.VIEW"))); return interfaceItems; } /** * Add FAQs to the view tree. * @param parentNode * @param faqs */ @SuppressWarnings("unchecked") protected void addViewTreeFaqs( final FaqNode parentNode, final List<Faq> faqs) { for (Faq theFaq : faqs) { if (userCanViewFaq(theFaq)) { FaqNode faqNode = new FaqNode(theFaq); addViewTreeFaqs( faqNode, getDomainService().getSubFaqs(theFaq)); if (faqNode.getChildCount() > 0) { ((FaqNode) faqNode.getChildren().get(0)).setFirst(true); ((FaqNode) faqNode.getChildren().get( faqNode.getChildCount() - 1)).setLast(true); } parentNode.getChildren().add(faqNode); parentNode.setLeaf(false); } } } /** * @return the root node. */ @SuppressWarnings("unchecked") protected FaqNode buildRootViewNode() { FaqNode rootNode = new FaqNode(); addViewTreeFaqs(rootNode, getDomainService().getRootFaqs()); for (Department theDepartment : getDomainService().getEnabledDepartments()) { if (editInterface) { FaqNode departmentNode = new FaqNode(theDepartment); addViewTreeFaqs( departmentNode, getDomainService().getRootFaqs(theDepartment)); departmentNode.markFirstAndLastChildNodes(); if (departmentNode.getChildCount() > 0 || (editInterface && getDomainService().userCanEditDepartmentFaqs( getCurrentUser(), theDepartment))) { rootNode.getChildren().add(departmentNode); rootNode.setLeaf(false); } } else { addViewTreeFaqs( rootNode, getDomainService().getRootFaqs(theDepartment)); } } rootNode.markFirstAndLastChildNodes(); return rootNode; } /** * JSF callback. */ public void refreshViewTree() { TreeState treeState = null; if (viewTree != null) { treeState = viewTree.getTreeState(); } FaqNode rootNode = buildRootViewNode(); viewTree = new FaqTreeModel(rootNode); if (treeState != null) { viewTree.setTreeState(treeState); } } /** * Add FAQs to the move tree. * @param parentNode * @param faqToMove * @param faqs */ @SuppressWarnings("unchecked") protected void addMoveTreeFaqs( final FaqNode parentNode, final Faq faqToMove, final List<Faq> faqs) { for (Faq theFaq : faqs) { if (!theFaq.equals(faqToMove)) { FaqNode faqNode = new FaqNode(theFaq); addMoveTreeFaqs( faqNode, faqToMove, getDomainService().getSubFaqs(theFaq)); AbstractFirstLastNode.markFirstAndLastChildNodes(faqNode); parentNode.getChildren().add(faqNode); parentNode.setLeaf(false); } } } /** * @param showEmptyDepartments * @param faqToMove * @return the root node. */ @SuppressWarnings("unchecked") protected FaqNode buildRootMoveNode( final boolean showEmptyDepartments, final Faq faqToMove) { FaqNode rootNode = new FaqNode(); if (getDomainService().userCanEditRootFaqs(getCurrentUser())) { addMoveTreeFaqs(rootNode, faqToMove, getDomainService().getRootFaqs()); } for (Department theDepartment : getDomainService().getEnabledDepartments()) { if (getDomainService().userCanEditDepartmentFaqs(getCurrentUser(), theDepartment)) { FaqNode departmentNode = new FaqNode(theDepartment); addMoveTreeFaqs( departmentNode, faqToMove, getDomainService().getRootFaqs(theDepartment)); AbstractFirstLastNode.markFirstAndLastChildNodes(departmentNode); if (departmentNode.getChildCount() > 0 || showEmptyDepartments) { rootNode.getChildren().add(departmentNode); rootNode.setLeaf(false); } } } AbstractFirstLastNode.markFirstAndLastChildNodes(rootNode); return rootNode; } /** * Refresh the move tree. * @param showEmptyDepartments * @param faqToMove */ public void refreshMoveTree( final boolean showEmptyDepartments, final Faq faqToMove) { FaqNode rootNode = buildRootMoveNode(showEmptyDepartments, faqToMove); moveTree = new FaqTreeModel(rootNode); moveTree.setActiveNode(rootNode.getIdentifier()); targetFaq = null; targetDepartment = null; } /** * JSF callback. */ public void updateInterface() { if (logger.isDebugEnabled()) { logger.debug("updateInterface()..."); } refreshViewTree(); if (faq != null) { setFaq(faq); } else if (department != null) { setDepartment(department); } else { setRoot(); } } /** * JSF callback. */ public void moveFaqUp() { getDomainService().moveFaqUp(faqToUpdate); updateInterface(); } /** * JSF callback. */ public void moveFaqDown() { getDomainService().moveFaqDown(faqToUpdate); updateInterface(); } /** * JSF callback. */ public void moveFaqFirst() { getDomainService().moveFaqFirst(faqToUpdate); updateInterface(); } /** * JSF callback. */ public void moveFaqLast() { getDomainService().moveFaqLast(faqToUpdate); updateInterface(); } /** * Add a FAQ. */ public void addFaq() { Faq newFaq = new Faq(); if (faq != null) { newFaq.setParent(faq); newFaq.setDepartment(faq.getDepartment()); } else { newFaq.setParent(null); newFaq.setDepartment(department); } newFaq.setLabel(getString("FAQS.TEXT.NEW_FAQ_LABEL")); newFaq.setScope(FaqScope.DEFAULT); getDomainService().addFaq(newFaq); updateInterface(); setFaq(newFaq); getDomainService().addFaqEvent(FaqEvent.create(getCurrentUser(), newFaq)); } /** * JSF callback. */ public void updateFaq() { if (!StringUtils.hasText(faqToUpdate.getLabel())) { addErrorMessage(null, "FAQS.MESSAGE.ENTER_TITLE"); return; } faqToUpdate.setLastUpdateNow(); getDomainService().updateFaq(faqToUpdate); refreshViewTree(); setFaq(faqToUpdate); getDomainService().addFaqEvent(FaqEvent.update(getCurrentUser(), faqToUpdate)); } /** * JSF callback. */ public void deleteFaq() { getDomainService().deleteFaq(faqToUpdate); refreshViewTree(); if (faqToUpdate.getParent() != null) { setFaq(faqToUpdate.getParent()); } else if (faqToUpdate.getDepartment() != null) { setDepartment(faqToUpdate.getDepartment()); } else { setRoot(); } getDomainService().addFaqEvent(FaqEvent.delete(getCurrentUser(), faqToUpdate)); } /** * JSF callback. * @return a String. */ public String moveFaq() { refreshMoveTree(true, faqToUpdate); return "moveFaq"; } /** * JSF callback. * @return a String. */ public String doMoveFaq() { Department origDepartment = faqToUpdate.getDepartment(); boolean move = false; if (origDepartment != null && !origDepartment.equals(targetDepartment)) { move = true; } if (targetDepartment != null && !targetDepartment.equals(origDepartment)) { move = true; } getDomainService().moveFaq(faqToUpdate, targetDepartment, targetFaq); updateInterface(); if (move) { getDomainService().addFaqEvent( FaqEvent.moveFrom(getCurrentUser(), faqToUpdate, origDepartment)); getDomainService().addFaqEvent( FaqEvent.moveTo(getCurrentUser(), faqToUpdate, origDepartment)); } return "faqMoved"; } /** * JSF callback. */ public void updateDepartment() { getDomainService().updateDepartment(departmentToUpdate); refreshViewTree(); setDepartment(departmentToUpdate); } /** * @return the department */ public Department getDepartment() { return department; } /** * @param faqs * @return only the visible FAQs */ protected List<Faq> filterSubFaqs(final List<Faq> faqs) { List<Faq> allowedFaqs = new ArrayList<Faq>(); for (Faq theFaq : faqs) { if (getDomainService().userCanViewFaq( getCurrentUser(), theFaq, getVisibleDepartments())) { allowedFaqs.add(theFaq); } } return allowedFaqs; } /** */ protected void setRootInternal() { faq = null; this.department = null; defaultFaqScopeI18nSuffix = null; setSubFaqs(filterSubFaqs(getDomainService().getRootFaqs())); userCanEdit = getDomainService().userCanEditRootFaqs(getCurrentUser()); } /** * @param theDepartment the department to set */ protected void setDepartmentInternal(final Department theDepartment) { setRootInternal(); department = theDepartment; setDepartmentToUpdate(department); defaultFaqScopeI18nSuffix = getDomainService().getDepartmentDefaultFaqScope(); setSubFaqs(filterSubFaqs(getDomainService().getRootFaqs(department))); userCanEdit = getDomainService().userCanEditDepartmentFaqs( getCurrentUser(), department); } /** * @param theFaq * @return the suffix for the default faq scope of a FAQ */ protected String getFaqDefaultFaqScopeI18nSuffix(final Faq theFaq) { String suffix; if (theFaq.getParent() == null) { if (theFaq.getDepartment() == null) { suffix = getDomainService().getDepartmentDefaultFaqScope(); } else { suffix = theFaq.getDepartment().getEffectiveDefaultFaqScope(); } } else { suffix = theFaq.getParent().getEffectiveScope(); } if (theFaq.getDepartment() == null && FaqScope.MANAGER.equals(suffix)) { suffix = FaqScope.ADMIN; } return suffix; } /** * @param theFaq the faq to set */ protected void setFaqInternal(final Faq theFaq) { setRootInternal(); faq = theFaq; setFaqToUpdate(faq); defaultFaqScopeI18nSuffix = getFaqDefaultFaqScopeI18nSuffix(faq); setSubFaqs(filterSubFaqs(getDomainService().getSubFaqs(faq))); userCanEdit = getDomainService().userCanEditFaq( getCurrentUser(), faq); } /** * @param node */ public void setNode(final FaqNode node) { if (node.getFaq() != null) { setFaqInternal(node.getFaq()); } else if (node.getDepartment() != null) { setDepartmentInternal(node.getDepartment()); } else { setRootInternal(); } viewTree.setActiveNode(node.getIdentifier()); viewTree.getTreeState().setSelected(node.getIdentifier()); } /** */ public void setRoot() { setNode(viewTree.getRootNode()); } /** * @param department the department to set */ public void setDepartment(final Department department) { if (viewTree == null) { refreshViewTree(); } FaqNode node = viewTree.findNode(department); if (node != null) { setNode(node); } else { addErrorMessage(null, "FAQS.MESSAGE.DEPARTMENT_NOT_VISIBLE", department.getLabel()); setNode(viewTree.getRootNode()); } } /** * @param faq the faq to set */ public void setFaq(final Faq faq) { if (viewTree == null) { refreshViewTree(); } FaqNode node = viewTree.findNode(faq); if (node != null) { setNode(node); } else { addErrorMessage(null, "FAQS.MESSAGE.FAQ_NOT_VISIBLE", String.valueOf(faq.getId())); setNode(viewTree.getRootNode()); } } /** * @return the faq */ public Faq getFaq() { return faq; } /** * @return the viewTree */ public FaqTreeModel getViewTree() { return viewTree; } /** * @return the subFaqs */ public List<Faq> getSubFaqs() { return subFaqs; } /** * @param subFaqs the subFaqs to set */ protected void setSubFaqs(final List<Faq> subFaqs) { this.subFaqs = subFaqs; } /** * @return the faqToUpdate */ public Faq getFaqToUpdate() { return faqToUpdate; } /** * @param faqToUpdate the faqToUpdate to set */ public void setFaqToUpdate(final Faq faqToUpdate) { this.faqToUpdate = new Faq(faqToUpdate); } /** * @return the editInterface */ public boolean isEditInterface() { return editInterface; } /** * @param editInterface the editInterface to set */ public void setEditInterface(final boolean editInterface) { this.editInterface = editInterface; } /** * @return the userCanEdit */ public boolean isUserCanEdit() { return userCanEdit; } /** * @param userCanEdit the userCanEdit to set */ protected void setUserCanEdit(final boolean userCanEdit) { this.userCanEdit = userCanEdit; } /** * @return the departmentToUpdate */ public Department getDepartmentToUpdate() { return departmentToUpdate; } /** * @param departmentToUpdate the departmentToUpdate to set */ public void setDepartmentToUpdate(final Department departmentToUpdate) { this.departmentToUpdate = new Department(departmentToUpdate); } /** * @return the defaultFaqScopeI18nSuffix */ public String getDefaultFaqScopeI18nSuffix() { return defaultFaqScopeI18nSuffix; } /** * @return the moveTree */ public FaqTreeModel getMoveTree() { return moveTree; } /** * @param targetDepartment the targetDepartment to set */ public void setTargetDepartment(final Department targetDepartment) { this.targetDepartment = targetDepartment; } /** * @param targetFaq the targetFaq to set */ public void setTargetFaq(final Faq targetFaq) { this.targetFaq = targetFaq; this.targetDepartment = targetFaq.getDepartment(); } }