/**********************************************************************************
* $URL: $
* $Id: $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.site.tool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.substitute.FontQualifier;
import org.apache.fop.fonts.substitute.FontSubstitution;
import org.apache.fop.fonts.substitute.FontSubstitutions;
import org.apache.fop.apps.MimeConstants;
import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.site.util.Participant;
import org.sakaiproject.site.util.SiteParticipantHelper;
import org.sakaiproject.thread_local.cover.ThreadLocalManager;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.util.BasicAuth;
import org.sakaiproject.util.RequestFilter;
import org.sakaiproject.util.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* this is the servlet to return the status of site copy thread based on the SessionState variable
* @author zqian
*
*/
public class SiteInfoToolServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private transient BasicAuth basicAuth;
protected static final Log log = LogFactory.getLog(SiteInfoToolServlet.class);
// create transformerFactory object needed by generatePDF
private TransformerFactory transformerFactory = null;
// create DocumentBuilder object needed by print PDF
DocumentBuilder docBuilder = null;
// XML Node/Attribute Names
protected static final String PARTICIPANTS_NODE_NAME = "PARTICIPANTS";
protected static final String SITE_TITLE_NODE_NAME = "SITE_TITLE";
protected static final String PARTICIPANT_NODE_NAME = "PARTICIPANT";
protected static final String PARTICIPANT_NAME_NODE_NAME = "NAME";
protected static final String PARTICIPANT_SECTIONS_NODE_NAME = "SECTIONS";
protected static final String PARTICIPANT_SECTION_NODE_NAME = "SECTION";
protected static final String PARTICIPANT_ID_NODE_NAME = "ID";
protected static final String PARTICIPANT_CREDIT_NODE_NAME = "CREDIT";
protected static final String PARTICIPANT_ROLE_NODE_NAME = "ROLE";
protected static final String PARTICIPANT_STATUS_NODE_NAME = "STATUS";
/** Resource bundle using current language locale */
private static ResourceLoader rb = new ResourceLoader("printParticipant");
// --------------------------------------------------------- Public Methods
/**
* Initialize this servlet.
*/
public void init() throws ServletException
{
super.init();
try {
basicAuth = new BasicAuth();
basicAuth.init();
transformerFactory = TransformerFactory.newInstance();
try
{
docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
}
catch (ParserConfigurationException e)
{
log.warn(this + " cannot get DocumentBuilder " + e.getMessage());
}
} catch (Exception e) {
log.warn(this + "init " + e.getMessage());
}
}
/**
* respond to an HTTP GET request
*
* @param req
* HttpServletRequest object with the client request
* @param res
* HttpServletResponse object back to the client
* @exception ServletException
* in case of difficulties
* @exception IOException
* in case of difficulties
*/
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
// process any login that might be present
basicAuth.doLogin(req);
// catch the login helper requests
// 1: Get site status: request url is of format https://server_name/sakai-site-manage-tool/tool/sitecopystatus/toolId
String option = req.getPathInfo();
String[] parts = option.split("/");
if ((parts.length == 3) && ("".equals(parts[0])) && ("sitecopystatus".equals(parts[1])))
{
getSiteCopyStatus(parts[2], res);
}
// 2: Print site participant list: request url if of format https://server_name/sakai-site-manage-tool/tool/printparticipant/siteId
else if ((parts.length == 3) && ("".equals(parts[0])) && ("printparticipant".equals(parts[1])))
{
getSiteParticipantList(parts[2], res);
}
}
/**
*
* @param toolId
* @param res
*/
private void getSiteCopyStatus (String toolId, HttpServletResponse res)
{
// TODO
}
private void getSiteParticipantList(String siteId, HttpServletResponse res)
{
// get the user id
String userId = SessionManager.getCurrentSessionUserId();
if (userId == null)
{
// fail the request, user not logged in yet.
log.warn(this + " HttpAccess for printing participant of site id =" + siteId + " without user loggin. ");
}
else
{
String siteReference = SiteService.siteReference(siteId);
// check whether the user has permission to view the site roster or is super user
if (SecurityService.unlock(userId, SiteService.SECURE_VIEW_ROSTER, siteReference) || SecurityService.isSuperUser())
{
print_participant(siteId);
}
else
{
log.warn(this + " HttpAccess for printing participant of site id =" + siteId + " with user id = " + userId + ": user does not have permission to view roster. " );
}
}
}
/**
* generate PDF file containing all site participant
* @param data
*/
public void print_participant(String siteId)
{
HttpServletResponse res = (HttpServletResponse) ThreadLocalManager.get(RequestFilter.CURRENT_HTTP_RESPONSE);
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
res.addHeader("Content-Disposition", "inline; filename=\"participants.pdf\"");
res.setContentType("application/pdf");
Document document = docBuilder.newDocument();
// get the participant xml document
generateParticipantXMLDocument(document, siteId);
generatePDF(document, outByteStream);
res.setContentLength(outByteStream.size());
if (outByteStream.size() > 0)
{
// Increase the buffer size for more speed.
res.setBufferSize(outByteStream.size());
}
/*
// output xml for debugging purpose
try
{
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);
}
catch (Exception e)
{
}*/
OutputStream out = null;
try
{
out = res.getOutputStream();
if (outByteStream.size() > 0)
{
outByteStream.writeTo(out);
}
res.setHeader("Refresh", "0");
out.flush();
out.close();
}
catch (Throwable ignore)
{
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (Throwable ignore)
{
}
}
}
}
/**
* Generate participant document
* @param doc
* @param siteId
*/
protected void generateParticipantXMLDocument(Document doc, String siteId)
{
List<String> providerCourseList = SiteParticipantHelper.getProviderCourseList(siteId);
Collection<Participant> participants = SiteParticipantHelper.prepareParticipants(siteId, providerCourseList);
// Create Root Element
Element root = doc.createElement(PARTICIPANTS_NODE_NAME);
String siteTitle = "";
if (siteId != null)
{
try
{
Site site = SiteService.getSite(siteId);
siteTitle = site.getTitle();
// site title
writeStringNodeToDom(doc, root, SITE_TITLE_NODE_NAME, rb.getFormattedMessage("participant_pdf_title", new String[] {siteTitle}));
}
catch (Exception e)
{
log.warn(this + ":generateParticipantXMLDocument: Cannot find site with id =" + siteId);
}
}
// Add the Root Element to Document
doc.appendChild(root);
if (participants != null)
{
// Go through all the time ranges (days)
for (Iterator<Participant> iParticipants = participants.iterator(); iParticipants.hasNext();)
{
Participant participant = iParticipants.next();
// Create Participant Element
Element participantNode = doc.createElement(PARTICIPANT_NODE_NAME);
// participant name
String participantName= participant.getName();
if (participant.getDisplayId() != null)
{
participantName +="( " + participant.getDisplayId() + " )";
}
writeStringNodeToDom(doc, participantNode, PARTICIPANT_NAME_NODE_NAME, StringUtils.trimToEmpty(participantName));
// sections
Element sectionsNode = doc.createElement(PARTICIPANT_SECTIONS_NODE_NAME);
for ( Iterator iSections = participant.getSectionEidList().iterator(); iSections.hasNext();)
{
String section = (String) iSections.next();
writeStringNodeToDom(doc, sectionsNode, PARTICIPANT_SECTION_NODE_NAME, StringUtils.trimToEmpty(section));
}
participantNode.appendChild(sectionsNode);
// registration id
writeStringNodeToDom(doc, participantNode, PARTICIPANT_ID_NODE_NAME, StringUtils.trimToEmpty(participant.getRegId()));
// credit
writeStringNodeToDom(doc, participantNode, PARTICIPANT_CREDIT_NODE_NAME, StringUtils.trimToEmpty(participant.getCredits()));
// role id
writeStringNodeToDom(doc, participantNode, PARTICIPANT_ROLE_NODE_NAME, StringUtils.trimToEmpty(participant.getRole()));
// status
writeStringNodeToDom(doc, participantNode, PARTICIPANT_STATUS_NODE_NAME, StringUtils.trimToEmpty(participant.active?rb.getString("sitegen.siteinfolist.active"):rb.getString("sitegen.siteinfolist.inactive")));
// add participant node to participants node
root.appendChild(participantNode);
}
}
}
/**
* Utility routine to write a string node to the DOM.
*/
protected Element writeStringNodeToDom(Document doc, Element parent, String nodeName, String nodeValue)
{
if (nodeValue != null)
{
Element name = doc.createElement(nodeName);
name.appendChild(doc.createTextNode(nodeValue));
parent.appendChild(name);
return name;
}
return null;
}
/**
* Takes a DOM structure and renders a PDF
*
* @param doc
* DOM structure
* @param xslFileName
* XSL file to use to translate the DOM document to FOP
*/
@SuppressWarnings("unchecked")
protected void generatePDF(Document doc, OutputStream streamOut)
{
String xslFileName = "participants-all-attrs.xsl";
Locale currentLocale = rb.getLocale();
if (currentLocale!=null){
String fullLocale = currentLocale.toString();
xslFileName = "participants-all-attrs_" + fullLocale + ".xsl";
if (getClass().getClassLoader().getResourceAsStream(xslFileName) == null){
xslFileName = "participants-all-attrs_" + currentLocale.getCountry() + ".xsl";
if (getClass().getClassLoader().getResourceAsStream(xslFileName) == null){
//We use the default file
xslFileName = "participants-all-attrs.xsl";
}
}
}
String configFileName = "userconfig.xml";
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
try
{
Configuration cfg = cfgBuilder.build(getClass().getClassLoader().getResourceAsStream(configFileName));
FopFactory fopFactory = FopFactory.newInstance();
fopFactory.setUserConfig(cfg);
fopFactory.setStrictValidation(false);
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
if (!StringUtils.isEmpty(ServerConfigurationService.getString("pdf.default.font"))) {
// this allows font substitution to support i18n chars in PDFs - SAK-21909
FontQualifier fromQualifier = new FontQualifier();
fromQualifier.setFontFamily("DEFAULT_FONT");
FontQualifier toQualifier = new FontQualifier();
toQualifier.setFontFamily(ServerConfigurationService.getString("pdf.default.font", "Helvetica"));
FontSubstitutions result = new FontSubstitutions();
result.add(new FontSubstitution(fromQualifier, toQualifier));
fopFactory.getFontManager().setFontSubstitutions(result);
}
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, streamOut);
InputStream in = getClass().getClassLoader().getResourceAsStream(xslFileName);
Transformer transformer = transformerFactory.newTransformer(new StreamSource(in));
transformer.setParameter("titleName", rb.getString("sitegen.siteinfolist.title.name"));
transformer.setParameter("titleSection", rb.getString("sitegen.siteinfolist.title.section"));
transformer.setParameter("titleId", rb.getString("sitegen.siteinfolist.title.id"));
transformer.setParameter("titleCredit", rb.getString("sitegen.siteinfolist.title.credit"));
transformer.setParameter("titleRole", rb.getString("sitegen.siteinfolist.title.role"));
transformer.setParameter("titleStatus", rb.getString("sitegen.siteinfolist.title.status"));
Source src = new DOMSource(doc);
transformer.transform(src, new SAXResult(fop.getDefaultHandler()));
} catch (Exception e)
{
e.printStackTrace();
log.warn(this+".generatePDF(): " + e);
return;
}
}
}