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 142 || readAccessOnly && canRead(site, request); 143 boolean sharedSiteCondition = !sharedSitesOnly 144 || sharedSitesOnly && sharedContentsCount > 0; 145 146 if (readAccessCondition && sharedSiteCondition) 147 { 148 matchingSites.add(site2json(request, site, currentSiteName, readAccessOnly, sharedSitesOnly)); 149 } 150 } 151 Map<String, Object> result = new HashMap<>(); 152 result.put("sites", matchingSites); 153 154 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 155 156 return EMPTY_MAP; 157 } 158 159 /** 160 * Convert page to JSON object 161 * @param request the request 162 * @param site The site to convert 163 * @param currentSiteName the current site name 164 * @param readAccessOnly true to get site on read access only 165 * @param sharedSitesOnly true to get shared site only 166 * @return The site as JSON object 167 */ 168 protected Map<String, Object> site2json(Request request, Site site, String currentSiteName, boolean readAccessOnly, boolean sharedSitesOnly) 169 { 170 Map<String, Object> object = new HashMap<>(); 171 172 object.put("id", site.getId()); 173 object.put("name", site.getName()); 174 object.put("title", site.getTitle()); 175 object.put("description", site.getDescription()); 176 object.put("path", site.getPath()); 177 object.put("url", site.getUrl()); 178 179 SiteType siteType = _siteTypesEP.getExtension(site.getType()); 180 object.put("type", _i18nUtils.translate(siteType.getLabel())); 181 182 return object; 183 } 184 185 private long _sharedContentsSize (Site site, String currentSiteName) 186 { 187 if (currentSiteName == null) 188 { 189 return 0; 190 } 191 192 Site currentSite = _siteManager.getSite(currentSiteName); 193 Expression expr = SharedContentsHelper.getSharedContentsExpression(currentSite, site); 194 195 SortCriteria sortCriteria = new SortCriteria(); 196 sortCriteria.addCriterion("title", true, true); 197 String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", expr, sortCriteria); 198 199 AmetysObjectIterable<Content> contents = _resolver.query(xPathQuery); 200 return contents.getSize(); 201 } 202 /** 203 * Determines if the current user has a read access on site 204 * @param site the site 205 * @param request the request 206 * @return true if current user has read access 207 */ 208 protected boolean canRead(Site site, Request request) 209 { 210 String currentSiteName = (String) request.getAttribute("siteName"); 211 212 try 213 { 214 request.setAttribute("siteName", site.getName()); 215 return _rightManager.hasRight(_userProvider.getUser(), "Web_Right_Site_See_Contents", "/cms") == RightResult.RIGHT_ALLOW; 216 } 217 finally 218 { 219 request.setAttribute("siteName", currentSiteName); 220 } 221 } 222 223 /** 224 * Site title expression 225 */ 226 protected class SiteTitleExpression implements Expression 227 { 228 private String _value; 229 230 /** 231 * Creates the comparison Expression. 232 * @param value the value to test 233 */ 234 public SiteTitleExpression(String value) 235 { 236 _value = value; 237 } 238 239 public String build() 240 { 241 return "jcr:like(fn:lower-case(ametys:title), '%" + _escapeValue(_value.toLowerCase()) + "%')"; 242 } 243 244 private String _escapeValue(String value) 245 { 246 // First escape ' - " \ _ and %, Then escape ' into '' for XQuery compliance 247 return value.replaceAll("(['\\-_\"\\\\%])", "\\\\$1").replaceAll("'", "''"); 248 } 249 } 250}