001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.repository.site; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.parameters.Parameters; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.acting.ServiceableAction; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Redirector; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.environment.SourceResolver; 031import org.apache.commons.lang.BooleanUtils; 032import org.apache.commons.lang3.StringUtils; 033 034import org.ametys.cms.repository.Content; 035import org.ametys.core.cocoon.JSonReader; 036import org.ametys.core.right.RightManager; 037import org.ametys.core.right.RightManager.RightResult; 038import org.ametys.core.user.CurrentUserProvider; 039import org.ametys.core.util.I18nUtils; 040import org.ametys.plugins.repository.AmetysObjectIterable; 041import org.ametys.plugins.repository.AmetysObjectResolver; 042import org.ametys.plugins.repository.query.QueryHelper; 043import org.ametys.plugins.repository.query.SortCriteria; 044import org.ametys.plugins.repository.query.expression.AndExpression; 045import org.ametys.plugins.repository.query.expression.Expression; 046import org.ametys.web.filter.SharedContentsHelper; 047 048/** 049 * This action is used by the edition.select-site widget for searching sites from a query string 050 */ 051public class SearchSitesAction extends ServiceableAction 052{ 053 /** The site manager */ 054 protected SiteManager _siteManager; 055 056 /** The site types EP */ 057 protected SiteTypesExtensionPoint _siteTypesEP; 058 059 /** I18N Utils */ 060 protected I18nUtils _i18nUtils; 061 062 /** Ametys resolver */ 063 protected AmetysObjectResolver _resolver; 064 065 /** Ametys resolver */ 066 protected RightManager _rightManager; 067 068 /** User provider */ 069 protected CurrentUserProvider _userProvider; 070 071 @Override 072 public void service(ServiceManager smanager) throws ServiceException 073 { 074 super.service(smanager); 075 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 076 _siteTypesEP = (SiteTypesExtensionPoint) smanager.lookup(SiteTypesExtensionPoint.ROLE); 077 _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE); 078 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 079 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 080 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 081 } 082 083 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 084 { 085 Iterable<Site> sites; 086 087 Request request = ObjectModelHelper.getRequest(objectModel); 088 String currentSiteName = (String) request.getAttribute("siteName"); 089 090 @SuppressWarnings("unchecked") 091 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 092 093 boolean readAccessOnly = BooleanUtils.isTrue((Boolean) jsParameters.get("readAccessOnly")); 094 boolean sharedSitesOnly = BooleanUtils.isTrue((Boolean) jsParameters.get("sharedSitesOnly")); 095 096 @SuppressWarnings("unchecked") 097 List<String> siteNames = (List<String>) jsParameters.get("names"); 098 if (siteNames != null) 099 { 100 List<Site> siteList = new ArrayList<>(); 101 for (String siteName : siteNames) 102 { 103 Site site = _siteManager.getSite(siteName); 104 siteList.add(site); 105 } 106 107 sites = siteList; 108 } 109 else 110 { 111 String query = (String) jsParameters.get("query"); 112 113 Expression siteExpression = null; 114 if (StringUtils.isNotEmpty(query)) 115 { 116 String[] wildcardValues = StringUtils.isEmpty(query) ? new String[0] : query.split("\\s"); 117 118 List<Expression> expressions = new ArrayList<>(); 119 for (String wildcardValue : wildcardValues) 120 { 121 expressions.add(new SiteTitleExpression(wildcardValue)); 122 } 123 124 siteExpression = new AndExpression(expressions.toArray(new Expression[expressions.size()])); 125 } 126 127 SortCriteria sortCriteria = new SortCriteria(); 128 sortCriteria.addCriterion("title", true, true); 129 130 String siteXPathQuery = QueryHelper.getXPathQuery(null, "ametys:site", siteExpression, sortCriteria); 131 sites = _resolver.query(siteXPathQuery); 132 } 133 134 // Site matching 135 List<Map<String, Object>> matchingSites = new ArrayList<>(); 136 for (Site site : sites) 137 { 138 // Calculate shared contents only if necessary 139 long sharedContentsCount = sharedSitesOnly ? _sharedContentsSize(site, currentSiteName) : 0; 140 141 boolean readAccessCondition = !readAccessOnly || (readAccessOnly && canRead(site, request)); 142 boolean sharedSiteCondition = !sharedSitesOnly || (sharedSitesOnly && sharedContentsCount > 0); 143 144 if (readAccessCondition && sharedSiteCondition) 145 { 146 matchingSites.add(site2json(request, site, currentSiteName, readAccessOnly, sharedSitesOnly)); 147 } 148 } 149 Map<String, Object> result = new HashMap<>(); 150 result.put("sites", matchingSites); 151 152 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 153 154 return EMPTY_MAP; 155 } 156 157 /** 158 * Convert page to JSON object 159 * @param request the request 160 * @param site The site to convert 161 * @param currentSiteName the current site name 162 * @param readAccessOnly true to get site on read access only 163 * @param sharedSitesOnly true to get shared site only 164 * @return The site as JSON object 165 */ 166 protected Map<String, Object> site2json(Request request, Site site, String currentSiteName, boolean readAccessOnly, boolean sharedSitesOnly) 167 { 168 Map<String, Object> object = new HashMap<>(); 169 170 object.put("id", site.getId()); 171 object.put("name", site.getName()); 172 object.put("title", site.getTitle()); 173 object.put("description", site.getDescription()); 174 object.put("path", site.getPath()); 175 object.put("url", site.getUrl()); 176 177 SiteType siteType = _siteTypesEP.getExtension(site.getType()); 178 object.put("type", _i18nUtils.translate(siteType.getLabel())); 179 180 return object; 181 } 182 183 private long _sharedContentsSize (Site site, String currentSiteName) 184 { 185 if (currentSiteName == null) 186 { 187 return 0; 188 } 189 190 Site currentSite = _siteManager.getSite(currentSiteName); 191 Expression expr = SharedContentsHelper.getSharedContentsExpression(currentSite, site); 192 193 SortCriteria sortCriteria = new SortCriteria(); 194 sortCriteria.addCriterion("title", true, true); 195 String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", expr, sortCriteria); 196 197 AmetysObjectIterable<Content> contents = _resolver.query(xPathQuery); 198 return contents.getSize(); 199 } 200 /** 201 * Determines if the current user has a read access on site 202 * @param site the site 203 * @param request the request 204 * @return true if current user has read access 205 */ 206 protected boolean canRead(Site site, Request request) 207 { 208 String currentSiteName = (String) request.getAttribute("siteName"); 209 210 try 211 { 212 request.setAttribute("siteName", site.getName()); 213 return _rightManager.hasRight(_userProvider.getUser(), "Web_Right_Site_See_Contents", "/cms") == RightResult.RIGHT_ALLOW; 214 } 215 finally 216 { 217 request.setAttribute("siteName", currentSiteName); 218 } 219 } 220 221 /** 222 * Site title expression 223 */ 224 protected class SiteTitleExpression implements Expression 225 { 226 private String _value; 227 228 /** 229 * Creates the comparison Expression. 230 * @param value the value to test 231 */ 232 public SiteTitleExpression(String value) 233 { 234 _value = value; 235 } 236 237 public String build() 238 { 239 return "jcr:like(fn:lower-case(ametys:title), '%" + _escapeValue(_value.toLowerCase()) + "%')"; 240 } 241 242 private String _escapeValue(String value) 243 { 244 // First escape ' - " \ _ and %, Then escape ' into '' for XQuery compliance 245 return value.replaceAll("(['\\-_\"\\\\%])", "\\\\$1").replaceAll("'", "''"); 246 } 247 } 248}