package org.sakaiproject.site.tool.helper.order.impl;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.event.cover.EventTrackingService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SitePage;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.api.ToolManager;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.util.SortedIterator;
import org.sakaiproject.util.Web;
import uk.org.ponder.util.UniversalRuntimeException;
/**
*
* @author Joshua Ryan joshua.ryan@asu.edu
*
*/
public class SitePageEditHandler {
public Site site;
public SiteService siteService;
public ToolManager toolManager;
public SessionManager sessionManager;
public ServerConfigurationService serverConfigurationService;
private Map<String, SitePage> pages;
public String[] selectedTools = new String[] {};
private Set<String> unhideables;
public String state;
public String title = "";
public String test;
public boolean update;
public boolean done;
//Just something dumb to bind to in order to supress warning messages
public String nil = null;
private final String TOOL_CFG_FUNCTIONS = "functions.require";
private final String PORTAL_VISIBLE = "sakai-portal:visible";
private final String TOOL_CFG_MULTI = "allowMultiple";
private final String SITE_UPD = "site.upd";
private final String HELPER_ID = "sakai.tool.helper.id";
private final String UNHIDEABLES_CFG = "poh.unhideables";
private final String PAGE_ADD = "pageorder.add";
private final String PAGE_DELETE = "pageorder.delete";
private final String PAGE_RENAME = "pageorder.rename";
private final String PAGE_SHOW = "pageorder.show";
private final String PAGE_HIDE = "pageorder.hide";
private final String PAGE_ENABLE = "pageorder.enable";
private final String PAGE_DISABLE = "pageorder.disable";
private final String SITE_REORDER = "pageorder.reorder";
private final String SITE_RESET = "pageorder.reset";
//System config for which tools can be added to a site more then once
private final String MULTI_TOOLS = "sakai.site.multiPlacementTools";
// Tool session attribute name used to schedule a whole page refresh.
public static final String ATTR_TOP_REFRESH = "sakai.vppa.top.refresh";
private String[] defaultMultiTools = {"sakai.news", "sakai.iframe"};
/**
* Gets the current tool
* @return Tool
*/
public Tool getCurrentTool() {
return toolManager.getCurrentTool();
}
/**
* Generates the currentPlacementId as used by the portal to name the iframe the tool lives in
* @return String currentPlacementId
*/
public String getCurrentPlacementId() {
return Web.escapeJavascript("Main" + toolManager.getCurrentPlacement().getId());
}
/**
* Gets the pages for the current site
* @return Map of pages (id, page)
*/
public Map<String, SitePage> getPages() {
if (site == null) {
init();
}
if (update) {
pages = new LinkedHashMap<String, SitePage>();
if (site != null)
{
List<SitePage> pageList = site.getOrderedPages();
for (int i = 0; i < pageList.size(); i++) {
SitePage page = pageList.get(i);
pages.put(page.getId(), page);
}
}
}
return pages;
}
/**
* Initialization method, just gets the current site in preperation for other calls
*
*/
public void init() {
if (site == null) {
String siteId = null;
try {
siteId = sessionManager.getCurrentToolSession()
.getAttribute(HELPER_ID + ".siteId").toString();
}
catch (java.lang.NullPointerException npe) {
// Site ID wasn't set in the helper call!!
}
if (siteId == null) {
siteId = toolManager.getCurrentPlacement().getContext();
}
try {
site = siteService.getSite(siteId);
update = siteService.allowUpdateSite(site.getId());
title = site.getTitle();
} catch (IdUnusedException e) {
// The siteId we were given was bogus
e.printStackTrace();
}
}
String conf = serverConfigurationService.getString(UNHIDEABLES_CFG);
if (conf != null) {
unhideables = new HashSet<String>();
String[] toolIds = conf.split(",");
for (int i = 0; i < toolIds.length; i++) {
unhideables.add(toolIds[i].trim());
}
}
}
/**
* Wrapper around siteService to save a site
* @param site
* @throws IdUnusedException
* @throws PermissionException
*/
public void saveSite(Site site) {
try {
siteService.save(site);
}
catch (Exception e) {
throw UniversalRuntimeException.accumulate(e, "Error saving site");
}
}
/**
* Gets the list of tools that can be added to the current site
* @return List of Tools
*/
public List<Tool> getAvailableTools() {
List<Tool> tools = new ArrayList<Tool>();
if (site == null) {
init();
}
Set<String> categories = new HashSet<String>();
if (site.getType() == null || siteService.isUserSite(site.getId())) {
categories.add("myworkspace");
}
else {
categories.add(site.getType());
}
Set<Tool> toolRegistrations = toolManager.findTools(categories, null);
List<String> multiPlacementToolIds = new ArrayList<String>();
String items[];
if (serverConfigurationService.getString(MULTI_TOOLS) != null &&
!"".equals(serverConfigurationService.getString(MULTI_TOOLS)))
items = serverConfigurationService.getString(MULTI_TOOLS).split(",");
else
items = defaultMultiTools;
for (int i = 0; i < items.length; i++) {
multiPlacementToolIds.add(items[i]);
}
SortedIterator i = new SortedIterator(toolRegistrations.iterator(), new ToolComparator());
for (; i.hasNext();)
{
Tool tr = (Tool) i.next();
if (tr != null) {
Properties config = tr.getRegisteredConfig();
String allowMultiple = config.getProperty(TOOL_CFG_MULTI);
if (multiPlacementToolIds.contains(tr.getId()) || "true".equals(allowMultiple)) {
tools.add(tr);
}
else if (site.getToolForCommonId(tr.getId()) == null) {
tools.add(tr);
}
}
}
return tools;
}
/**
* Process tool adds
* @return
*/
public String addTools () {
for (int i = 0; i < selectedTools.length; i++) {
SitePage page = null;
try {
page = site.addPage();
Tool tool = toolManager.getTool(selectedTools[i]);
page.setTitle(tool.getTitle());
ToolConfiguration placement = page.addTool(tool.getId());
siteService.save(site);
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_ADD, "/site/" + site.getId() +
"/page/" + page.getId() +
"/tool/" + selectedTools[i] +
"/placement/" + placement.getId(), false));
}
catch(Exception e) {
throw UniversalRuntimeException.accumulate(e, "Error adding tool " + selectedTools[i]);
}
}
return "success";
}
/**
* Process the 'Save' post on page ordering.
*
*/
public String savePages () {
if (state != null) {
String[] pages = state.split(" ");
for (int i = 0; i < pages.length; i++) {
if (pages[i] != null) {
SitePage realPage = site.getPage(pages[i]);
realPage.setPosition(i);
}
}
site.setCustomPageOrdered(true);
try {
siteService.save(site);
EventTrackingService.post(
EventTrackingService.newEvent(SITE_REORDER, "/site/" + site.getId(), false));
}
catch (IdUnusedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (PermissionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ToolSession session = sessionManager.getCurrentToolSession();
session.setAttribute(ATTR_TOP_REFRESH, Boolean.TRUE);
return "done";
}
/**
* Allows the Cancel button to return control to the tool calling this helper
*
*/
public String cancel() {
ToolSession session = sessionManager.getCurrentToolSession();
session.setAttribute(ATTR_TOP_REFRESH, Boolean.TRUE);
return "done";
}
public String reset() {
site.setCustomPageOrdered(false);
try {
siteService.save(site);
EventTrackingService.post(
EventTrackingService.newEvent(SITE_RESET, "/site/" + site.getId(), false));
}
catch (IdUnusedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (PermissionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* Checks if a given toolId is required or not for the current site being edited
*
* @param toolId
* @return true if the tool is required
*/
public boolean isRequired(String toolId) {
if (site == null) {
init();
}
List<String> requiredTools = null;
if (site.getType() == null || siteService.isUserSite(site.getId())) {
requiredTools = serverConfigurationService.getToolsRequired("myworkspace");
}
else {
requiredTools = serverConfigurationService.getToolsRequired(site.getType());
}
if (requiredTools != null && requiredTools.contains(toolId)) {
return true;
}
return false;
}
/**
* Checks if users can see a page or not
*
* @param page The SitePage whose visibility is in question
* @return true if users can see the page
*/
public boolean isVisible(SitePage page) {
List<ToolConfiguration> tools = page.getTools();
Iterator<ToolConfiguration> iPt = tools.iterator();
boolean visible = false;
while( !visible && iPt.hasNext() )
{
ToolConfiguration placement = iPt.next();
Properties roleConfig = placement.getConfig();
String visibility = roleConfig.getProperty(PORTAL_VISIBLE);
if ( ! "false".equals(visibility) ) visible = true;
}
return visible;
}
/**
* Checks if users without site.upd can see a page or not
*
* @param page The SitePage whose visibility is in question
* @return true if users with out site.upd can see the page
*/
public boolean isEnabled(SitePage page) {
List<ToolConfiguration> tools = page.getTools();
Iterator<ToolConfiguration> iPt = tools.iterator();
boolean visible = false;
while( !visible && iPt.hasNext() )
{
ToolConfiguration placement = iPt.next();
Properties roleConfig = placement.getConfig();
String roleList = roleConfig.getProperty(TOOL_CFG_FUNCTIONS);
if (roleList == null || !(roleList.indexOf(SITE_UPD) > -1)) {
visible = true;
}
}
return visible;
}
/**
* Checks if the site has been ordered yet
*
* @return true if the site has custom ordering
*/
public boolean isSiteOrdered() {
return site.isCustomPageOrdered();
}
/**
* Checks to see if a given tool is allowed to be hidden.
*
* Useful for tools that have other requried permissions where setting the page
* as visible may not make it visible to all users and thus causes some confusion.
*
* @return true if this tool is allowed to be hidden
*/
public boolean allowsHide(String toolId) {
if (unhideables == null || !unhideables.contains(toolId))
return true;
return false;
}
/**
* Checks to see if a given SitePage is allowed to be hidden.
*
* Useful for pages with tools that have other requried permissions where setting
* the page as visible may not make it visible to all users and thus causes some
* confusion.
*
* @return true if this tool is allowed to be hidden
*/
public boolean allowsHide(SitePage page) {
List<ToolConfiguration> tools = page.getTools();
Iterator<ToolConfiguration> iPt = tools.iterator();
boolean hideable = true;
while( hideable && iPt.hasNext() )
{
ToolConfiguration placement = iPt.next();
if (!allowsHide(placement.getToolId())) {
hideable = false;
}
}
return hideable;
}
/**
* Disables a page for any user who doesn't have site.upd
*
* @param pageId The Id of the Page
* @return true for sucess, false for failuer
* @throws IdUnusedException, PermissionException
*/
public boolean disablePage(String pageId) throws IdUnusedException, PermissionException {
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_DISABLE, "/site/" + site.getId() +
"/page/" + pageId, false));
return pageVisibilityHelper(pageId, false, false);
}
/**
* Enables a page for any user who doesn't have site.upd
*
* @param pageId The Id of the Page
* @return true for sucess, false for failuer
* @throws IdUnusedException, PermissionException
*/
public boolean enablePage(String pageId) throws IdUnusedException, PermissionException {
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_ENABLE, "/site/" + site.getId() +
"/page/" + pageId, false));
return pageVisibilityHelper(pageId, true, true);
}
/**
* Hides a page from any user who doesn't have site.upd
* Implies enabled
*
* @param pageId The Id of the Page
* @return true for sucess, false for failuer
* @throws IdUnusedException, PermissionException
*/
public boolean hidePage(String pageId) throws IdUnusedException, PermissionException {
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_HIDE, "/site/" + site.getId() +
"/page/" + pageId, false));
return pageVisibilityHelper(pageId, false, true);
}
/**
* Unhides a page for any user who doesn't have site.upd
* Implies enabled
*
* @param pageId The Id of the Page
* @return true for sucess, false for failuer
* @throws IdUnusedException, PermissionException
*/
public boolean showPage(String pageId) throws IdUnusedException, PermissionException {
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_SHOW, "/site/" + site.getId() +
"/page/" + pageId, false));
return pageVisibilityHelper(pageId, true, true);
}
/**
* Handles the visiibility of a page with a combination of visible/enabled
* @param pageId The Id of the Page
* @param visible - Affects the sakai:portal-visible value for tools
* @param enabled - Affects site.upd in functions.require for tools
* @return true for sucess, false for failuer
* @throws IdUnusedException, PermissionException
*/
private boolean pageVisibilityHelper(String pageId, boolean visible, boolean enabled)
throws IdUnusedException, PermissionException{
if (site == null) {
init();
}
SitePage page = site.getPage(pageId);
List<ToolConfiguration> tools = page.getTools();
Iterator<ToolConfiguration> iterator = tools.iterator();
//If all the tools on a page require site.upd then only users with site.upd will see
//the page in the site nav of Charon... not sure about the other Sakai portals floating about
while( iterator.hasNext() ) {
ToolConfiguration placement = iterator.next();
Properties roleConfig = placement.getPlacementConfig();
String roleList = roleConfig.getProperty(TOOL_CFG_FUNCTIONS);
String visibility = roleConfig.getProperty(PORTAL_VISIBLE);
boolean saveChanges = false;
if ( "false".equals(visibility) && visible) {
visibility = "true";
saveChanges = true;
} else if ( ( !"false".equals(visibility) ) && !visible ) {
visibility = "false";
saveChanges = true;
}
if (roleList == null) {
roleList = "";
}
if (!(roleList.indexOf(SITE_UPD) > -1) && !enabled) {
if (roleList.length() > 0) {
roleList += ",";
}
roleList += SITE_UPD;
saveChanges = true;
}
else if (enabled) {
roleList = roleList.replaceAll("," + SITE_UPD, "");
roleList = roleList.replaceAll(SITE_UPD, "");
saveChanges = true;
}
if (saveChanges) {
roleConfig.setProperty(TOOL_CFG_FUNCTIONS, roleList);
if ( visibility != null ) {
roleConfig.setProperty(PORTAL_VISIBLE, visibility);
}
placement.save();
siteService.save(site);
}
}
return true;
}
/**
* Adds a new single tool page to the current site
* @param toolId
* @param title
* @return the newly added SitePage
*/
public SitePage addPage (String toolId, String title) {
SitePage page = null;
try {
page = site.addPage();
page.setTitle(title);
ToolConfiguration placement = page.addTool(toolId);
siteService.save(site);
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_ADD, "/site/" + site.getId() +
"/page/" + page.getId() +
"/tool/" + toolId +
"/placement/" + placement.getId(), false));
}
catch (Exception e) {
throw UniversalRuntimeException.accumulate(e, "Error adding page " + title);
}
init();
return page;
}
/**
* Removes a page from the site
*
* @param pageId
* @return title of page removed
* @throws IdUnusedException
* @throws PermissionException
*/
public String removePage(String pageId) {
SitePage page = site.getPage(pageId);
site.removePage(page);
saveSite(site);
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_DELETE, "/site/" + site.getId() +
"/page/" + page.getId(), false));
return page.getTitle();
}
/**
* Sets the title of a page, and if there is only one tool on a page the title of that tool.
* Also optionally will alter the configuration of a tool
*
* @param pageId
* @param newTitle
* @param newConfig
* @return the old title of the page
* @throws IdUnusedException
* @throws PermissionException
*/
public String setTitle(String pageId, String newTitle) {
SitePage page = site.getPage(pageId);
String oldTitle = page.getTitle();
page.setTitle(newTitle);
page.setTitleCustom(true);
// TODO: Find a way to call each tool to ask what fields they need configured
// and what methods to use to validate the input..
if (page.getTools().size() == 1) {
ToolConfiguration tool = (ToolConfiguration) page.getTools().get(0);
tool.setTitle(newTitle);
}
saveSite(site);
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_RENAME, "/site/" + site.getId() +
"/page/" + page.getId() +
"/old_title/" + oldTitle +
"/new_title/" + page.getTitle(), false));
return oldTitle;
}
/**
* Resets page title to the default and resets titleCustom flag
*
* @param pageId The Id of the Page
* @return reset page title
* @throws IdUnusedException, PermissionException
*/
public String resetTitle(String pageId) throws IdUnusedException, PermissionException {
SitePage page = site.getPage(pageId);
String oldTitle = page.getTitle();
page.setTitleCustom(false);
String newTitle = page.getTitle();
page.setTitle(newTitle);
// TODO: Find a way to call each tool to ask what fields they need configured
// and what methods to use to validate the input..
if (page.getTools().size() == 1) {
ToolConfiguration tool = (ToolConfiguration) page.getTools().get(0);
tool.setTitle(newTitle);
}
saveSite(site);
EventTrackingService.post(
EventTrackingService.newEvent(PAGE_RENAME, "/site/" + site.getId() +
"/page/" + page.getId() +
"/old_title/" + oldTitle +
"/new_title/" + page.getTitle(), false));
return newTitle;
}
/**
* Sets property config/value of page tool, if only one tool on page
*
* @param pageId
* @param config
* @param value
* @return the old title of the page
* @throws IdUnusedException
* @throws PermissionException
*/
public void setConfig(String pageId, String config, String value) {
SitePage page = site.getPage(pageId);
// TODO: Find a way to call each tool to ask what fields they need configured
// and what methods to use to validate the input..
if (page.getTools().size() == 1 && !"nil".equals(value)) {
ToolConfiguration tool = (ToolConfiguration) page.getTools().get(0);
tool.getPlacementConfig().setProperty(config, value);
}
saveSite(site);
}
/**
* ** Copied from SiteAction.java.. should be in a common place such as util?
*
* @author joshuaryan
*
*/
private class ToolComparator implements Comparator<Tool>
{
/**
* implementing the Comparator compare function
* @param o1 The first object
* @param o2 The second object
* @return The compare result. 1 is o1 < o2; 0 is o1.equals(o2); -1 otherwise
*/
public int compare (Tool o1, Tool o2)
{
try {
return o1.getTitle().compareTo(o2.getTitle());
}
catch (Exception e)
{
}
return -1;
} // compare
} //ToolComparator
}