/** * Koya is an alfresco module that provides a corporate orientated dataroom. * * Copyright (C) Itl Developpement 2014 * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see `<http://www.gnu.org/licenses/>`. */ package fr.itldev.koya.alfservice; import com.google.common.base.Joiner; import java.io.Serializable; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.log4j.Logger; import fr.itldev.koya.exception.KoyaServiceException; import fr.itldev.koya.model.KoyaModel; import fr.itldev.koya.model.exceptions.KoyaErrorCodes; import fr.itldev.koya.model.impl.Company; import fr.itldev.koya.model.impl.Dossier; import fr.itldev.koya.model.impl.Preferences; import fr.itldev.koya.model.impl.SalesOffer; import fr.itldev.koya.model.permissions.SitePermission; /** * * Koya companies are site whith activable aspect * */ public class CompanyService { private static final String SITE_PRESET = "site-dashboard"; private static final String PREFS_FILE_NAME = "preferences.properties"; private static final String DESC = "Koya Company"; private final Logger logger = Logger.getLogger(CompanyService.class); // service beans protected SiteService siteService; protected NodeService nodeService; protected NodeService unprotNodeService; protected KoyaNodeService koyaNodeService; protected ModelService modelService; protected AuthenticationService authenticationService; protected FileFolderService fileFolderService; protected SearchService searchService; protected CompanyPropertiesService companyPropertiesService; protected PermissionService permissionService; // <editor-fold defaultstate="collapsed" desc="getters/setters"> public void setSiteService(SiteService siteService) { this.siteService = siteService; } public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setUnprotectedNodeService(NodeService unprotNodeService) { this.unprotNodeService = unprotNodeService; } public void setKoyaNodeService(KoyaNodeService koyaNodeService) { this.koyaNodeService = koyaNodeService; } public void setModelService(ModelService modelService) { this.modelService = modelService; } public void setAuthenticationService( AuthenticationService authenticationService) { this.authenticationService = authenticationService; } public void setFileFolderService(FileFolderService fileFolderService) { this.fileFolderService = fileFolderService; } public void setSearchService(SearchService searchService) { this.searchService = searchService; } public void setCompanyPropertiesService( CompanyPropertiesService companyPropertiesService) { this.companyPropertiesService = companyPropertiesService; } public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } // </editor-fold> public Company create(String compTitle, SalesOffer sO, String spaceTemplate) throws KoyaServiceException { if (compTitle == null || compTitle.isEmpty()) { throw new KoyaServiceException(KoyaErrorCodes.COMPANY_EMPTY_TITLE); } String title; try { title = URLDecoder.decode(compTitle, "UTF-8"); } catch (Exception ex) { title = compTitle; logger.warn("not able to decode UTF-8 URL encoding company title : using input company title without modification"); } String shortName = getShortNameFromTitle(title); SiteInfo sInfo = siteService.createSite(SITE_PRESET, shortName, title, DESC + " - " + shortName, SiteVisibility.PRIVATE); Company created = koyaNodeService.getKoyaNode(sInfo.getNodeRef(), Company.class); // Creating koya-config directory // todo difference between company config (ie company hime) and koya // site config (default site) getKoyaConfigNodeRef(sInfo.getNodeRef(), true); // Creating koya:companyProperties node (COMPANYPROPERTIES_FILE_NAME) = // companyHome // in site koyaConfig folder (default location) // todo define outside site company home NodeRef companyPropertiesNode = companyPropertiesService .createCompanyConfigFile(created); // put companySite Aspect with default company.properties reference. Map<QName, Serializable> props = new HashMap<>(); props.put(KoyaModel.PROP_COMPANYHOME, companyPropertiesNode); nodeService.addAspect(created.getNodeRef(), KoyaModel.ASPECT_COMPANYSITE, props); modelService.companyInitTemplate(shortName, spaceTemplate); modelService.companyInitImports(shortName); // TODO copy config files to the koya-config directory return created; } public Company getCompany(String shortName) throws KoyaServiceException { return koyaNodeService.getKoyaNode(getSiteInfo(shortName).getNodeRef(), Company.class); } /** * List users available companies. * * @return * @throws fr.itldev.koya.exception.KoyaServiceException */ public List<Company> list() throws KoyaServiceException { return list(false); } public List<Company> list(Boolean adminMode) throws KoyaServiceException { // TODO limit user's available sites only List<Company> companies = new ArrayList<>(); List<SiteInfo> sites = null; /** * If admin, return all available companies else return * only accessible companies */ if (adminMode) { ResultSet rs = null; try { rs = searchService.query( StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_FTS_ALFRESCO, "ASPECT:\"koya:companySite\""); for (ResultSetRow r : rs) { companies.add(koyaNodeService.getKoyaNode(r.getNodeRef(), Company.class)); } } finally { if (rs != null) { rs.close(); } } } else { for (SiteInfo s : siteService.listSites(authenticationService .getCurrentUserName())) {//filter koya site aspect if (nodeService.hasAspect(s.getNodeRef(), KoyaModel.ASPECT_COMPANYSITE)) { companies.add(koyaNodeService.getKoyaNode(s.getNodeRef(), Company.class)); } } } return companies; } public SiteInfo getSiteInfo(String shortName) { return siteService.getSite(shortName); } public SiteInfo getSiteInfo(NodeRef nodeSite) { return siteService.getSite(nodeSite); } public List<SalesOffer> listSalesOffer() { return null; } public SalesOffer getSalesOffer(String offerName) { return null; } /** * ================= Preferences Methods =========================== */ /** * Get company preferences from properties file. * * @param companyName * @return * @throws fr.itldev.koya.exception.KoyaServiceException */ public Preferences getPreferences(String companyName) throws KoyaServiceException { Preferences prefs = new Preferences(); Company c = koyaNodeService.getKoyaNode(siteService .getSite(companyName).getNodeRef(), Company.class); Properties compProperties; try { // TODO use lucene path to retrieve node reference. compProperties = koyaNodeService .readPropertiesFileContent(getCompanyConfigFile(c, PREFS_FILE_NAME)); } catch (NullPointerException nex) { return prefs; } for (Object k : compProperties.keySet()) { prefs.put((String) k, compProperties.get(k)); } return prefs; } /** * Get single preference identified by a key for a company * * @param companyName * @param preferenceKey * @return * @throws fr.itldev.koya.exception.KoyaServiceException */ public String getPreference(String companyName, String preferenceKey) throws KoyaServiceException { Company c = koyaNodeService.getKoyaNode(siteService .getSite(companyName).getNodeRef(), Company.class); Properties compProperties; try { // TODO use lucene path to retrieve node reference. compProperties = koyaNodeService .readPropertiesFileContent(getCompanyConfigFile(c, PREFS_FILE_NAME)); } catch (NullPointerException nex) { return ""; } try { return compProperties.getProperty(preferenceKey); } catch (Exception ex) { return ""; } } /** * Writes current company object preferences as preferences file. * * @param companyName * @param prefsToCommit * @throws fr.itldev.koya.exception.KoyaServiceException */ public void commitPreferences(String companyName, Preferences prefsToCommit) throws KoyaServiceException { Company c = koyaNodeService.getKoyaNode(siteService .getSite(companyName).getNodeRef(), Company.class); NodeRef prefFileNodeRef = getCompanyConfigFile(c, PREFS_FILE_NAME); if (prefFileNodeRef != null) { fileFolderService.delete(prefFileNodeRef); } // create preferences file NodeRef koyaConfigNr = getKoyaConfigNodeRef(c.getNodeRef(), false); prefFileNodeRef = fileFolderService.create(koyaConfigNr, PREFS_FILE_NAME, ContentModel.TYPE_CONTENT).getNodeRef(); // write preferences file new version ContentWriter writer = fileFolderService.getWriter(prefFileNodeRef); // placeholder = write comment first line writer.putContent(Joiner.on("\n").withKeyValueSeparator("=") .join(prefsToCommit)); } /** * ================= Properties Methods ============================== */ /** * ========================================================================= */ /** * Site name normalisation method * * @return */ private String getShortNameFromTitle(String title) { String shortTitle = title.replaceAll("[àáâãäå]", "a") .replaceAll("æ", "ae").replaceAll("ç", "c") .replaceAll("[èéêë]", "e").replaceAll("[ìíîï]", "i") .replaceAll("ñ", "n").replaceAll("[òóôõö]", "o") .replaceAll("œ", "oe").replaceAll("[ùúûü]", "u") .replaceAll("[ýÿ]", "y").replaceAll("&", "and") .replaceAll("\\s+", "-").replaceAll("[.]", "").toLowerCase(); // TODO check invalid characters like € /** * shortTitle is unique even if title alredy exists. */ String buildTitle = shortTitle; int incr = 1; while (siteService.getSite(buildTitle) != null) { buildTitle = shortTitle + "-" + incr; incr++; } return buildTitle; } /** * Koya company is a site with activable aspect. * * @param n * @return */ public Boolean isKoyaCompany(NodeRef n) { return nodeService.getType(n).equals(KoyaModel.TYPE_COMPANY) && nodeService.hasAspect(n, KoyaModel.ASPECT_ACTIVABLE); } /** * Returns company specific file : file stored in 'koya-config' company * directory. * * retuns null if file doesn't exists. * * @param c * @return */ private NodeRef getCompanyConfigFile(Company c, String fileName) { // TODO Read From company Home variable NodeRef prefFileNodeRef = null; try { prefFileNodeRef = nodeService.getChildByName( getKoyaConfigNodeRef(c.getNodeRef(), false), ContentModel.ASSOC_CONTAINS, fileName); } catch (Exception ex) { } return prefFileNodeRef; } /** * Return the "koya-config" noderef under the given root. Optionally create * the folder if it does not exist yet. NOTE: must only be set to create if * within a WRITE transaction context. * <p> * Adds the "isIndexed = false" property to the koya-config folder node. * * @param rootRef * Root node reference where the "koya-config" folder should live * @param create * True to create the folder if missing, false otherwise * * @return koya-config folder ref if found, null otherwise if not creating */ @Deprecated protected NodeRef getKoyaConfigNodeRef(final NodeRef rootRef, final boolean create) { NodeRef koyaConfigRef = this.unprotNodeService.getChildByName(rootRef, ContentModel.ASSOC_CONTAINS, CompanyPropertiesService.KOYA_CONFIG); if (create && koyaConfigRef == null) { if (logger.isDebugEnabled()) { logger.debug("'koya-config' folder not found under path, creating..."); } QName assocQName = QName.createQName( NamespaceService.CONTENT_MODEL_1_0_URI, CompanyPropertiesService.KOYA_CONFIG); Map<QName, Serializable> properties = new HashMap<>(1, 1.0f); properties.put(ContentModel.PROP_NAME, (Serializable) CompanyPropertiesService.KOYA_CONFIG); ChildAssociationRef ref = this.unprotNodeService.createNode( rootRef, ContentModel.ASSOC_CONTAINS, assocQName, ContentModel.TYPE_FOLDER, properties); koyaConfigRef = ref.getChildRef(); // koya-config needs to be hidden - applies index control aspect as // part of the hidden aspect Map<QName, Serializable> props = new HashMap<>(); props.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE); props.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE); nodeService.addAspect(ref.getChildRef(), ContentModel.ASPECT_INDEX_CONTROL, props); } return koyaConfigRef; } private static String TMP_SUMMARY_DIR = "tmpDossierSummary"; public NodeRef getTmpSummaryDir(final Company company) { return getTmpDir(company, TMP_SUMMARY_DIR); } private static String TMP_ZIP_DIR = "tmpDossierZip"; public NodeRef getTmpZipDir(final Company company) { return getTmpDir(company, TMP_ZIP_DIR); } public NodeRef getTmpDir(final Company company, final String dirName) { NodeRef tmpSummaryDir = null; try { tmpSummaryDir = nodeService.getChildByName(company.getNodeRef(), ContentModel.ASSOC_CONTAINS, dirName); } catch (Exception e) { } if (tmpSummaryDir == null) { // create tmp summary dir tmpSummaryDir = AuthenticationUtil .runAsSystem(new AuthenticationUtil.RunAsWork<NodeRef>() { @Override public NodeRef doWork() throws Exception { final Map<QName, Serializable> properties = new HashMap<>(); properties.put(ContentModel.PROP_NAME, dirName); properties.put(ContentModel.PROP_TITLE, dirName); ChildAssociationRef car = nodeService.createNode( company.getNodeRef(), ContentModel.ASSOC_CONTAINS, QName.createQName( NamespaceService.CONTENT_MODEL_1_0_URI, dirName), ContentModel.TYPE_FOLDER, properties); permissionService.setPermission( car.getChildRef(), siteService.getSiteRoleGroup( company.getName(), SitePermission.CONSUMER.toString()), SitePermission.CONTRIBUTOR.toString(), true); return car.getChildRef(); } }); } return tmpSummaryDir; } }