/**********************************************************************************
* $URL:https://source.sakaiproject.org/svn/osp/trunk/common/api-impl/src/java/org/theospi/portfolio/style/impl/StyleManagerImpl.java $
* $Id:StyleManagerImpl.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $
***********************************************************************************
*
* Copyright (c) 2006, 2007, 2008 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.theospi.portfolio.style.impl;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.jdom.CDATA;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.content.api.ContentCollection;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.content.api.LockManager;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.exception.ServerOverloadException;
import org.sakaiproject.exception.TypeException;
import org.sakaiproject.metaobj.security.AuthenticationManager;
import org.sakaiproject.metaobj.security.AllowMapSecurityAdvisor;
import org.sakaiproject.metaobj.shared.mgt.AgentManager;
import org.sakaiproject.metaobj.shared.mgt.IdManager;
import org.sakaiproject.metaobj.shared.model.Agent;
import org.sakaiproject.metaobj.shared.model.Id;
import org.sakaiproject.metaobj.shared.model.MimeType;
import org.sakaiproject.metaobj.worksite.mgt.WorksiteManager;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService.SelectionType;
import org.sakaiproject.site.cover.SiteService;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.theospi.event.EventService;
import org.theospi.event.EventConstants;
import org.theospi.portfolio.security.AuthorizationFacade;
import org.theospi.portfolio.shared.model.Node;
import org.theospi.portfolio.style.StyleConsumer;
import org.theospi.portfolio.style.StyleFunctionConstants;
import org.theospi.portfolio.style.mgt.StyleManager;
import org.theospi.portfolio.style.model.Style;
public class StyleManagerImpl extends HibernateDaoSupport
implements StyleManager {
private AuthorizationFacade authzManager = null;
private IdManager idManager = null;
private WorksiteManager worksiteManager;
private AuthenticationManager authnManager = null;
private LockManager lockManager;
private ContentHostingService contentHosting = null;
private AgentManager agentManager;
private SecurityService securityService = null;
private EventService eventService = null;
private List<String> globalSites;
private List<String> globalSiteTypes;
private List<StyleConsumer> consumers;
private boolean autoDdl = false;
public Style storeStyle(Style style) {
return storeStyle(style, true);
}
public Style storeStyle (Style style, boolean checkAuthz) {
updateFields(style, checkAuthz);
getHibernateTemplate().saveOrUpdate(style);
lockStyleFiles(style);
eventService.postEvent(EventConstants.EVENT_STYLE_ADD,style.getId().getValue());
return style;
}
public Style mergeStyle(Style style) {
return mergeStyle(style, true);
}
public Style mergeStyle (Style style, boolean checkAuthz) {
if (style.getId() == null) {
return storeStyle(style, checkAuthz);
}
updateFields(style, checkAuthz);
getHibernateTemplate().merge(style);
lockStyleFiles(style);
eventService.postEvent(EventConstants.EVENT_STYLE_REVISE,style.getId().getValue());
return style;
}
protected void updateFields(Style style, boolean checkAuthz)
{
style.setModified(new Date(System.currentTimeMillis()));
style.setStyleHash(calculateStyleHash(style));
boolean newStyle = (style.getId() == null);
if (newStyle) {
style.setCreated(new Date(System.currentTimeMillis()));
if (checkAuthz) {
getAuthzManager().checkPermission(StyleFunctionConstants.CREATE_STYLE,
getIdManager().getId(style.getSiteId()));
}
} else {
if (checkAuthz) {
getAuthzManager().checkPermission(StyleFunctionConstants.EDIT_STYLE,
style.getId());
}
}
}
protected void lockStyleFiles(Style style){
getLockManager().removeAllLocks(style.getId().getValue());
getLockManager().lockObject(style.getStyleFile().getValue(),
style.getId().getValue(), "saving a style", true);
}
public Style getStyle(Id styleId) {
return (Style) getHibernateTemplate().get(Style.class, styleId);
}
public Style getLightWeightStyle(final Id styleId) {
HibernateCallback callback = new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Style style = (Style) session.get(Style.class, styleId);
return style;
}
};
try {
Style style = (Style) getHibernateTemplate().execute(callback);
return style;
} catch (HibernateObjectRetrievalFailureException e) {
logger.debug(e);
return null;
}
}
public Collection findSiteStyles(String currentWorksiteId) {
Object[] params = new Object[]{currentWorksiteId,
getAuthnManager().getAgent()};
return getHibernateTemplate().findByNamedQuery("findSiteStyles", params);
}
public Collection findPublishedStyles(String currentWorksiteId) {
Object[] params = new Object[]{Integer.valueOf(Style.STATE_PUBLISHED),
currentWorksiteId,
getAuthnManager().getAgent()};
return getHibernateTemplate().findByNamedQuery("findPublishedStyles", params);
}
protected String buildGlobalSiteList() {
StringBuffer queryBuffer = new StringBuffer();
queryBuffer.append("(");
for (Iterator<String> i=getGlobalSites().iterator();i.hasNext();) {
String site = (String)i.next();
queryBuffer.append("'").append(site).append("',");
}
for (Iterator<String> j = getGlobalSiteTypes().iterator(); j.hasNext();) {
String type = (String)j.next();
List<Site> sites = SiteService.getSites(SelectionType.ANY, type, null, null, null, null);
for (Iterator<Site> k = sites.iterator(); k.hasNext();) {
Site theSite = (Site) k.next();
queryBuffer.append("'").append(theSite.getId()).append("',");
}
}
queryBuffer.append("'')");
return queryBuffer.toString();
}
public Collection findGlobalStyles(Agent agent) {
String query = "from Style s where ((s.siteId in " + buildGlobalSiteList() +
" and (s.globalState = ? or s.owner = ?)) or s.globalState = 1)";
Object[] params = new Object[]{Integer.valueOf(Style.STATE_PUBLISHED),
agent};
return getHibernateTemplate().find(query, params);
}
public boolean isGlobal() {
String siteId = getWorksiteManager().getCurrentWorksiteId().getValue();
if (getGlobalSites().contains(siteId)) {
return true;
}
Site site = getWorksiteManager().getSite(siteId);
if (site.getType() != null && getGlobalSiteTypes().contains(site.getType())) {
return true;
}
return false;
}
public Node getNode(Id artifactId) {
String id = getContentHosting().resolveUuid(artifactId.getValue());
if (id == null) {
return null;
}
try {
String ref = getContentHosting().getReference(id);
getSecurityService().pushAdvisor(
new AllowMapSecurityAdvisor(ContentHostingService.EVENT_RESOURCE_READ, ref));
ContentResource resource = getContentHosting().getResource(id);
String ownerId = resource.getProperties().getProperty(resource.getProperties().getNamePropCreator());
Agent owner = getAgentManager().getAgent(getIdManager().getId(ownerId));
return new Node(artifactId, resource, owner);
}
catch (PermissionException e) {
logger.error("", e);
throw new RuntimeException(e);
}
catch (IdUnusedException e) {
logger.error("", e);
throw new RuntimeException(e);
}
catch (TypeException e) {
logger.error("", e);
throw new RuntimeException(e);
}
}
public Node getNode(Reference ref) {
String nodeId = getContentHosting().getUuid(ref.getId());
return getNode(getIdManager().getId(nodeId));
}
public boolean deleteStyle(final Id styleId) {
Style style = getStyle(styleId);
getAuthzManager().checkPermission(StyleFunctionConstants.DELETE_STYLE, style.getId());
// handle things that are using this style
if (!checkStyleConsumption(styleId)) {
getLockManager().removeAllLocks(styleId.getValue());
getHibernateTemplate().delete(style);
}
else {
return false;
}
eventService.postEvent(EventConstants.EVENT_STYLE_DELETE,style.getId().getValue());
// If we get here, I think it was deleted
return true;
}
protected boolean checkStyleConsumption(Id styleId) {
for (Iterator i = getConsumers().iterator(); i.hasNext();) {
StyleConsumer sc = (StyleConsumer) i.next();
if (sc.checkStyleConsumption(styleId)) {
return true;
}
}
return false;
}
public List<StyleConsumer> getConsumers() {
return this.consumers;
}
public List getStyles(Id consumerId) {
for (Iterator<StyleConsumer> i = consumers.iterator();i.hasNext();) {
List returned = i.next().getStyles(consumerId);
if (returned != null) {
return returned;
}
}
return null;
}
public void setConsumers(List<StyleConsumer> consumers) {
this.consumers = consumers;
}
public Collection getStylesForWarehouse()
{
return getHibernateTemplate().findByNamedQuery("findStyles");
}
public void packageStyleForExport(Set styleIds, OutputStream os) throws IOException {
CheckedOutputStream checksum = new CheckedOutputStream(os,
new Adler32());
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(
checksum));
for (Iterator<String> i=styleIds.iterator();i.hasNext();) {
String id = (String) i.next();
processStyle(id, zos);
}
zos.finish();
zos.flush();
}
protected void processStyle(String styleId, ZipOutputStream zos) throws IOException {
Style style = getStyle(getIdManager().getId(styleId));
Node node = getNode(style.getStyleFile());
try {
processStyleFile(zos, node);
}
catch (ServerOverloadException e) {
throw new RuntimeException(e);
}
ZipEntry definitionFile = new ZipEntry("style-"+styleId+".xml");
zos.putNextEntry(definitionFile);
Document doc = createStyleAsXml(style);
String docStr = (new XMLOutputter()).outputString(doc);
zos.write(docStr.getBytes("UTF-8"));
zos.closeEntry();
}
protected void processStyleFile(ZipOutputStream zos, Node node)
throws ServerOverloadException, IOException {
ContentResource entity = node.getResource();
String newName = entity.getProperties().getProperty(entity.getProperties().getNamePropDisplayName());
String cleanedName = newName.substring(newName.lastIndexOf('\\')+1);
String entryName = "attachments/" + entity.getContentType() + "/" +
getNodeHash(node) + "/" + cleanedName;
storeFileInZip(zos, entity.streamContent(), entryName);
}
protected void storeFileInZip(ZipOutputStream zos, InputStream in, String entryName)
throws IOException {
byte data[] = new byte[1024 * 10];
if (File.separatorChar == '\\') {
entryName = entryName.replace('\\', '/');
}
ZipEntry newfileEntry = new ZipEntry(entryName);
zos.putNextEntry(newfileEntry);
BufferedInputStream origin = new BufferedInputStream(in, data.length);
int count;
while ((count = origin.read(data, 0, data.length)) != -1) {
zos.write(data, 0, count);
}
zos.closeEntry();
in.close();
}
protected Document createStyleAsXml(Style style) {
Element rootNode = new Element("style");
rootNode.setAttribute("formatVersion", "2.1");
addNode(rootNode, "id", style.getId().getValue());
addNode(rootNode, "name", style.getName());
addNode(rootNode, "description", style.getDescription());
addNode(rootNode, "owner", style.getOwner().getId().getValue());
addNode(rootNode, "siteId", style.getSiteId());
Node styleFileNode = getNode(style.getStyleFile());
addNode(rootNode, "nodeRef", styleFileNode.getResource().getReference());
Element items = new Element("items");
rootNode.addContent(items);
return new Document(rootNode);
}
protected void addNode(Element parentNode, String name, String value) {
Element attrNode = new Element(name);
attrNode.addContent(new CDATA(value));
parentNode.addContent(attrNode);
}
protected int getNodeHash(Node node) {
return node.getResource().getReference().hashCode();
}
/**
* @param parent the parent resource folder where attachments go
* @param siteId the site which will recieve the imported "stuff"
* @param in The Input stream representing the output stream of the export
* @return Map contains a map with keys being of type String as old Ids and the
* values as being the Style object
*/
public Map importStyleList(ContentCollection parent, String siteId, InputStream in) throws IOException {
Map styleMap = new Hashtable();
ZipInputStream zis = new ZipInputStream(in);
ZipEntry currentEntry = zis.getNextEntry();
Map attachmentMap = new Hashtable();
while (currentEntry != null) {
if (!currentEntry.isDirectory()) {
if (currentEntry.getName().startsWith("style-")) {
importStyle(siteId, zis, styleMap);
}
else if (currentEntry.getName().startsWith("attachments/")) {
importAttachmentRef(parent, currentEntry, siteId, zis, attachmentMap);
}
}
zis.closeEntry();
currentEntry = zis.getNextEntry();
}
postPocessAttachments(styleMap.values(), attachmentMap);
for (Iterator i=styleMap.entrySet().iterator();i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
String key = entry.getKey().toString();
Style style = (Style)entry.getValue();
Style found = findMatchingStyle(style);
if (found == null) {
storeStyle(style, false);
} else {
removeFromSession(style);
removeFromSession(found);
styleMap.put(key, found);
}
}
return styleMap;
}
/**
* given a stream this reads in an xml style and places the new style into the map
* @param siteId
* @param is
* @param styleMap
* @throws IOException
*/
protected void importStyle(String siteId, InputStream is, Map styleMap)
throws IOException {
SAXBuilder builder = new SAXBuilder();
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // SAK-23131
Document document = null;
byte []bytes = readStreamToBytes(is);
try {
document = builder.build(new ByteArrayInputStream(bytes));
}
catch (JDOMException e) {
throw new RuntimeException(e);
}
Element docRoot = document.getRootElement();
Id oldStyleId = getIdManager().getId(docRoot.getChildText("id"));
String name = docRoot.getChildText("name");
String description = docRoot.getChildText("description");
String nodeRef = docRoot.getChildTextNormalize("nodeRef");
Style style = new Style();
//style.setId(getIdManager().createId());
style.setName(name);
style.setDescription(description);
style.setOwner(getAuthnManager().getAgent());
style.setSiteId(siteId);
style.setCreated(new Date(System.currentTimeMillis()));
style.setModified(style.getCreated());
style.setGlobalState(Style.STATE_UNPUBLISHED);
style.setNodeRef(nodeRef);
styleMap.put(oldStyleId.getValue(), style);
}
protected byte[] readStreamToBytes(InputStream inStream) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte data[] = new byte[10*1024];
int count;
while ((count = inStream.read(data, 0, 10*1024)) != -1) {
bytes.write(data, 0, count);
}
byte []tmp = bytes.toByteArray();
bytes.close();
return tmp;
}
protected void importAttachmentRef(ContentCollection fileParent, ZipEntry currentEntry, String siteId,
ZipInputStream zis, Map attachmentMap) {
File file = new File(currentEntry.getName());
MimeType mimeType = new MimeType(file.getParentFile().getParentFile().getParentFile().getName(),
file.getParentFile().getParentFile().getName());
String contentType = mimeType.getValue();
String oldId = file.getParentFile().getName();
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int c = zis.read();
while (c != -1) {
bos.write(c);
c = zis.read();
}
String fileId = fileParent.getId() + file.getName();
ContentResource rez = null;
try {
rez = getContentHosting().getResource(fileId);
} catch(IdUnusedException iduue) {
logger.info(iduue);
}
if(rez == null) {
ContentResourceEdit resource = getContentHosting().addResource(fileId);
ResourcePropertiesEdit resourceProperties = resource.getPropertiesEdit();
resourceProperties.addProperty (ResourceProperties.PROP_DISPLAY_NAME, file.getName());
resource.setContent(bos.toByteArray());
resource.setContentType(contentType);
getContentHosting().commitResource(resource);
rez = resource;
}
attachmentMap.put(oldId, rez.getId());
}
catch (Exception exp) {
throw new RuntimeException(exp);
}
}
protected void postPocessAttachments(Collection styles, Map attachmentMap) {
for (Iterator i=styles.iterator();i.hasNext();) {
Style style = (Style) i.next();
postProcessStyleFile(style, attachmentMap);
}
}
protected void postProcessStyleFile(Style style, Map attachmentMap) {
int nodeHash = style.getNodeRef().hashCode();
String id = (String)attachmentMap.get("" + nodeHash);
if (id != null) {
String nodeId = getContentHosting().getUuid(id);
style.setStyleFile(getIdManager().getId(nodeId));
style.setStyleHash(calculateStyleHash(style));
} else {
style.setStyleFile(null);
}
}
/**
*
* @param style
* @return The calculated hash string using the Style file's content (the css file)
*/
protected String calculateStyleHash(Style style) {
String hashString = "";
try {
Node styleFileNode = getNode(style.getStyleFile());
if (styleFileNode != null) {
hashString += new String(styleFileNode.getResource().getContent());
//hashString += Integer.toString(styleFileNode.getInputStream().hashCode());
}
}
catch (ServerOverloadException e) {
return null;
}
return hashString.hashCode() + "";
}
protected void updateStyleHash() {
List styles = getHibernateTemplate().findByNamedQuery("findByNullStyleHash");
for (Iterator i = styles.iterator(); i.hasNext();) {
Style style = (Style) i.next();
style.setStyleHash(calculateStyleHash(style));
getHibernateTemplate().saveOrUpdate(style);
}
}
public void init() {
logger.info("init()");
if (isAutoDdl()) {
try {
updateStyleHash();
}
catch (Exception e) {
logger.error("Error in StyleManager.init()", e);
}
}
}
/**
*
* @param style
* @return Returns the first Style object it finds that is either globally published,
* or in the same site and also has the same styleHash value.
*/
protected Style findMatchingStyle(Style style) {
Object[] params = new Object[]{Integer.valueOf(Style.STATE_PUBLISHED),
style.getSiteId(), style.getStyleHash()};
List styles = getHibernateTemplate().findByNamedQuery("findMatchingStyle", params);
if (styles.size() > 0) {
return (Style) styles.get(0);
}
return null;
}
public void removeFromSession(Object obj) {
this.getHibernateTemplate().evict(obj);
try {
getHibernateTemplate().getSessionFactory().evict(obj.getClass());
} catch (HibernateException e) {
logger.error(e);
}
}
/**
* {@inheritDoc}
*/
public List<String> createStyleUrlList(List<Style> styles) {
if (styles != null) {
List<String> returned = new ArrayList<String>(styles.size());
for (Iterator<Style> i=styles.iterator();i.hasNext();) {
returned.add(getStyleUrl(i.next()));
}
return returned;
}
return new ArrayList<String>();
}
/**
* {@inheritDoc}
*/
public String getStyleUrl(Style style) {
Node styleNode = getNode(style.getStyleFile());
return styleNode.getExternalUri();
}
public AuthenticationManager getAuthnManager() {
return authnManager;
}
public void setAuthnManager(AuthenticationManager authnManager) {
this.authnManager = authnManager;
}
public AuthorizationFacade getAuthzManager() {
return authzManager;
}
public void setAuthzManager(AuthorizationFacade authzManager) {
this.authzManager = authzManager;
}
public List<String> getGlobalSites() {
return globalSites;
}
public void setGlobalSites(List<String> globalSites) {
this.globalSites = globalSites;
}
public List<String> getGlobalSiteTypes() {
return globalSiteTypes;
}
public void setGlobalSiteTypes(List<String> globalSiteTypes) {
this.globalSiteTypes = globalSiteTypes;
}
public IdManager getIdManager() {
return idManager;
}
public void setIdManager(IdManager idManager) {
this.idManager = idManager;
}
public WorksiteManager getWorksiteManager() {
return worksiteManager;
}
public void setWorksiteManager(WorksiteManager worksiteManager) {
this.worksiteManager = worksiteManager;
}
public LockManager getLockManager() {
return lockManager;
}
public void setLockManager(LockManager lockManager) {
this.lockManager = lockManager;
}
public AgentManager getAgentManager() {
return agentManager;
}
public void setAgentManager(AgentManager agentManager) {
this.agentManager = agentManager;
}
public ContentHostingService getContentHosting() {
return contentHosting;
}
public void setContentHosting(ContentHostingService contentHosting) {
this.contentHosting = contentHosting;
}
/**
* @return the securityService
*/
public SecurityService getSecurityService() {
return securityService;
}
/**
* @param securityService the securityService to set
*/
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public boolean isAutoDdl() {
return autoDdl;
}
public void setAutoDdl(boolean autoDdl) {
this.autoDdl = autoDdl;
}
public EventService getEventService() {
return eventService;
}
public void setEventService(EventService eventService) {
this.eventService = eventService;
}
}