001/* 002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and others. 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 * 016 * Contributors: 017 * Arnaud Kervern 018 */ 019package org.nuxeo.ecm.rating.operations; 020 021import static org.apache.commons.lang.StringUtils.isBlank; 022import static org.nuxeo.ecm.activity.ActivityHelper.getActivityId; 023import static org.nuxeo.ecm.activity.ActivityHelper.getDocumentId; 024import static org.nuxeo.ecm.activity.ActivityHelper.getUsername; 025 026import java.io.ByteArrayOutputStream; 027import java.io.IOException; 028import java.io.OutputStream; 029import java.util.ArrayList; 030import java.util.Date; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034import java.util.regex.Matcher; 035import java.util.regex.Pattern; 036 037import net.sf.json.JSONArray; 038import net.sf.json.JSONObject; 039 040import org.apache.commons.lang.StringEscapeUtils; 041import org.apache.commons.logging.Log; 042import org.apache.commons.logging.LogFactory; 043import org.codehaus.jackson.JsonGenerator; 044import org.nuxeo.ecm.activity.ActivitiesList; 045import org.nuxeo.ecm.activity.Activity; 046import org.nuxeo.ecm.activity.ActivityHelper; 047import org.nuxeo.ecm.activity.ActivityMessageHelper; 048import org.nuxeo.ecm.activity.ActivityStreamService; 049import org.nuxeo.ecm.automation.OperationContext; 050import org.nuxeo.ecm.automation.core.Constants; 051import org.nuxeo.ecm.automation.core.annotations.Context; 052import org.nuxeo.ecm.automation.core.annotations.Operation; 053import org.nuxeo.ecm.automation.core.annotations.OperationMethod; 054import org.nuxeo.ecm.automation.core.annotations.Param; 055import org.nuxeo.ecm.automation.jaxrs.io.JsonHelper; 056import org.nuxeo.ecm.core.api.Blob; 057import org.nuxeo.ecm.core.api.Blobs; 058import org.nuxeo.ecm.core.api.CoreSession; 059import org.nuxeo.ecm.core.api.DocumentLocation; 060import org.nuxeo.ecm.core.api.DocumentModel; 061import org.nuxeo.ecm.core.api.IdRef; 062import org.nuxeo.ecm.core.api.PathRef; 063import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 064import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 065import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 066import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 067import org.nuxeo.ecm.core.io.registry.context.RenderingContext.CtxBuilder; 068import org.nuxeo.ecm.platform.types.adapter.TypeInfo; 069import org.nuxeo.ecm.platform.url.DocumentViewImpl; 070import org.nuxeo.ecm.platform.url.api.DocumentView; 071import org.nuxeo.ecm.platform.url.api.DocumentViewCodecManager; 072import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper; 073import org.nuxeo.ecm.rating.api.LikeService; 074import org.nuxeo.runtime.api.Framework; 075 076/** 077 * @author <a href="mailto:akervern@nuxeo.com">Arnaud Kervern</a> 078 */ 079@Operation(id = MostLiked.ID, category = Constants.CAT_SERVICES, label = "Like a document or an activity object") 080public class MostLiked { 081 private static final Log log = LogFactory.getLog(MostLiked.class); 082 083 public static final String ID = "Services.MostLiked"; 084 085 public static Pattern HTTP_URL_PATTERN = Pattern.compile("\\b(https?://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|])"); 086 087 @Context 088 protected CoreSession session; 089 090 @Context 091 protected LikeService likeService; 092 093 @Context 094 protected ActivityStreamService activityService; 095 096 @Context 097 protected OperationContext ctx; 098 099 @Param(name = "contextPath") 100 protected String contextPath; 101 102 @Param(name = "limit") 103 protected int limit; 104 105 @Param(name = "fromDt", required = false) 106 protected Date fromDt; 107 108 @Param(name = "toDt", required = false) 109 protected Date toDt; 110 111 @Param(name = "documentLinkBuilder", required = false) 112 protected String documentLinkBuilder; 113 114 @OperationMethod 115 public Blob run() throws IOException { 116 ActivitiesList mostLikedDocuments = likeService.getMostLikedActivities(session, limit, 117 session.getDocument(new PathRef(contextPath)), fromDt, toDt); 118 119 final List<JSONObject> docsWithRate = new ArrayList<JSONObject>(); 120 for (Activity activity : mostLikedDocuments) { 121 if (ActivityHelper.isDocument(activity.getTarget())) { 122 docsWithRate.add(buildFromDocument(activity)); 123 } else if (ActivityHelper.isActivity(activity.getTarget())) { 124 docsWithRate.add(buildFromActivity(activity)); 125 } else { 126 log.info("Unable to check activity type ..."); 127 } 128 } 129 130 Map<String, Object> jsonObj = new HashMap<String, Object>(); 131 jsonObj.put("items", JSONArray.fromObject(docsWithRate)); 132 JSONObject json = JSONObject.fromObject(jsonObj); 133 return Blobs.createBlob(json.toString(), "application/json"); 134 } 135 136 protected JSONObject buildFromActivity(Activity activity) { 137 Activity miniMessage = activityService.getActivity(Long.valueOf(getActivityId(activity.getTarget()))); 138 String message = MostLiked.replaceURLsByLinks(miniMessage.getObject()); 139 Integer rating = Integer.valueOf(activity.getObject()); 140 Integer hasRated = Integer.valueOf(activity.getContext()); 141 142 Map<String, Object> value = new HashMap<String, Object>(); 143 value.put("type", "minimessage"); 144 value.put("message", message); 145 value.put("rating", rating); 146 value.put("actor", getUsername(miniMessage.getActor())); 147 value.put("profile", 148 ActivityMessageHelper.getUserProfileLink(miniMessage.getActor(), miniMessage.getDisplayActor())); 149 value.put("hasUserLiked", hasRated); 150 151 return JSONObject.fromObject(value); 152 } 153 154 protected JSONObject buildFromDocument(Activity activity) throws IOException { 155 DocumentModel doc = session.getDocument(new IdRef(getDocumentId(activity.getTarget()))); 156 Integer rating = Integer.valueOf(activity.getObject()); 157 Integer hasRated = Integer.valueOf(activity.getContext()); 158 159 OutputStream out = new ByteArrayOutputStream(); 160 JsonGenerator jg = JsonHelper.createJsonGenerator(out); 161 162 writeDocument(doc, jg); 163 164 Map<String, Object> value = new HashMap<String, Object>(); 165 value.put("rating", rating); 166 value.put("document", JSONObject.fromObject(out.toString())); 167 value.put("url", getDocumentUrl(doc)); 168 value.put("hasUserLiked", hasRated); 169 value.put("type", "document"); 170 171 return JSONObject.fromObject(value); 172 } 173 174 private static DocumentModelJsonWriter documentModelWriter; 175 176 private static void writeDocument(DocumentModel doc, JsonGenerator jg) throws IOException { 177 if (documentModelWriter == null) { 178 RenderingContext ctx = CtxBuilder.properties("dublincore", "common").get(); 179 MarshallerRegistry registry = Framework.getService(MarshallerRegistry.class); 180 documentModelWriter = registry.getUniqueInstance(ctx, DocumentModelJsonWriter.class); 181 } 182 documentModelWriter.write(doc, jg); 183 jg.flush(); 184 } 185 186 protected String getDocumentUrl(DocumentModel doc) { 187 if (Framework.isTestModeSet()) { 188 return "http://dummyurl.com"; 189 } 190 191 DocumentViewCodecManager documentViewCodecManager = Framework.getLocalService(DocumentViewCodecManager.class); 192 String codecName = isBlank(documentLinkBuilder) ? documentViewCodecManager.getDefaultCodecName() 193 : documentLinkBuilder; 194 195 DocumentLocation docLoc = new DocumentLocationImpl(session.getRepositoryName(), doc.getRef()); 196 DocumentView docView = new DocumentViewImpl(docLoc, doc.getAdapter(TypeInfo.class).getDefaultView()); 197 return VirtualHostHelper.getContextPathProperty() + "/" 198 + documentViewCodecManager.getUrlFromDocumentView(codecName, docView, false, null); 199 } 200 201 protected static String replaceURLsByLinks(String message) { 202 String escapedMessage = StringEscapeUtils.escapeHtml(message); 203 Matcher m = HTTP_URL_PATTERN.matcher(escapedMessage); 204 StringBuffer sb = new StringBuffer(escapedMessage.length()); 205 while (m.find()) { 206 String url = m.group(1); 207 m.appendReplacement(sb, computeLinkFor(url)); 208 } 209 m.appendTail(sb); 210 return sb.toString(); 211 } 212 213 protected static String computeLinkFor(String url) { 214 return "<a href=\"" + url + "\" target=\"_top\">" + url + "</a>"; 215 } 216}