1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.services.wcm.search.connector;
18
19 import org.apache.commons.lang.LocaleUtils;
20 import org.codehaus.groovy.util.ListHashMap;
21 import org.exoplatform.commons.api.search.data.SearchContext;
22 import org.exoplatform.commons.api.search.data.SearchResult;
23 import org.exoplatform.commons.search.es.ElasticSearchServiceConnector;
24 import org.exoplatform.commons.search.es.client.ElasticSearchingClient;
25 import org.exoplatform.container.xml.InitParams;
26 import org.exoplatform.services.cms.documents.DocumentService;
27 import org.exoplatform.services.cms.drives.DriveData;
28 import org.exoplatform.services.cms.impl.Utils;
29 import org.exoplatform.services.jcr.RepositoryService;
30 import org.exoplatform.services.log.ExoLogger;
31 import org.exoplatform.services.log.Log;
32 import org.exoplatform.services.wcm.core.NodeLocation;
33 import org.exoplatform.services.wcm.search.base.EcmsSearchResult;
34 import org.exoplatform.services.wcm.utils.WCMCoreUtils;
35 import org.exoplatform.web.controller.QualifiedName;
36 import org.json.simple.JSONObject;
37
38 import javax.jcr.Node;
39 import javax.jcr.RepositoryException;
40 import java.io.UnsupportedEncodingException;
41 import java.net.URLEncoder;
42 import java.time.Instant;
43 import java.time.ZoneId;
44 import java.time.format.DateTimeFormatter;
45 import java.time.format.FormatStyle;
46 import java.util.*;
47 import java.util.stream.Collectors;
48
49
50
51
52 public class FileSearchServiceConnector extends ElasticSearchServiceConnector {
53
54 private static final Log LOG = ExoLogger.getLogger(FileSearchServiceConnector.class.getName());
55
56 private RepositoryService repositoryService;
57
58 private DocumentService documentService;
59
60 public FileSearchServiceConnector(InitParams initParams, ElasticSearchingClient client, RepositoryService repositoryService, DocumentService documentService) {
61 super(initParams, client);
62 this.repositoryService = repositoryService;
63 this.documentService = documentService;
64 }
65
66 @Override
67 protected String getSourceFields() {
68 List<String> fields = Arrays.asList("name",
69 "title",
70 "workspace",
71 "path",
72 "author",
73 "createdDate",
74 "lastUpdatedDate",
75 "fileType",
76 "fileSize");
77
78 return fields.stream().map(field -> "\"" + field + "\"").collect(Collectors.joining(","));
79 }
80
81 @Override
82 protected SearchResult buildHit(JSONObject jsonHit, SearchContext searchContext) {
83 SearchResult searchResult = super.buildHit(jsonHit, searchContext);
84
85 JSONObject hitSource = (JSONObject) jsonHit.get("_source");
86 String workspace = (String) hitSource.get("workspace");
87 String nodePath = (String) hitSource.get("path");
88 String fileType = (String) hitSource.get("fileType");
89 String fileSize = (String) hitSource.get("fileSize");
90
91 String driveName = "";
92 try {
93 DriveData driveOfNode = documentService.getDriveOfNode(nodePath);
94 if(driveOfNode != null) {
95 driveName = driveOfNode.getName() + " - ";
96 }
97 } catch (Exception e) {
98 LOG.warn("Cannot get drive of node " + nodePath, e);
99 }
100
101 String lang = searchContext.getParamValue(SearchContext.RouterParams.LANG.create());
102 String detail = driveName + getFormattedFileSize(fileSize) + " - " + getFormattedDate(searchResult.getDate(), lang);
103
104 SearchResult ecmsSearchResult = new EcmsSearchResult(getUrl(nodePath),
105 getPreviewUrl(jsonHit, searchContext),
106 searchResult.getTitle(),
107 searchResult.getExcerpt(),
108 detail,
109 getImageUrl(workspace, nodePath),
110 searchResult.getDate(),
111 searchResult.getRelevancy(),
112 fileType,
113 nodePath,
114 getBreadcrumb(nodePath));
115
116 return ecmsSearchResult;
117 }
118
119 protected String getUrl(String nodePath) {
120 String url = "";
121 try {
122 url = documentService.getLinkInDocumentsApp(nodePath);
123 } catch (Exception e) {
124 LOG.error("Cannot get url of document " + nodePath, e);
125 }
126 return url;
127 }
128
129 protected String getFormattedDate(long createdDateTime, String lang) {
130 try {
131 Locale locale = LocaleUtils.toLocale(lang);
132 DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT).withLocale(locale).withZone(ZoneId.systemDefault());
133 return df.format(Instant.ofEpochMilli(createdDateTime));
134 } catch (Exception e) {
135 LOG.error("Cannot format date for timestamp " + createdDateTime, e);
136 return "";
137 }
138 }
139
140 protected String getFormattedFileSize(String fileSize) {
141 try {
142 Long size = Long.parseLong(fileSize);
143 return Utils.formatSize(size);
144 } catch (Exception e) {
145 LOG.error("Cannot format file size " + fileSize, e);
146 return "";
147 }
148 }
149
150 protected String getPreviewUrl(JSONObject jsonHit, SearchContext context) {
151 JSONObject hitSource = (JSONObject) jsonHit.get("_source");
152
153 String id = (String) jsonHit.get("_id");
154 String author = (String) hitSource.get("author");
155 String title = (String) hitSource.get("title");
156 String workspace = (String) hitSource.get("workspace");
157 String nodePath = (String) hitSource.get("path");
158 String fileType = (String) hitSource.get("fileType");
159
160 String restContextName = WCMCoreUtils.getRestContextName();
161 String repositoryName = null;
162 try {
163 repositoryName = repositoryService.getCurrentRepository().getConfiguration().getName();
164 } catch (RepositoryException e) {
165 LOG.error("Cannot get repository name", e);
166 }
167
168 StringBuffer downloadUrl = new StringBuffer();
169 downloadUrl.append('/').append(restContextName).append("/jcr/").
170 append(repositoryName).append('/').
171 append(workspace).append(nodePath);
172
173 StringBuilder url = new StringBuilder("javascript:require(['SHARED/documentPreview'], function(documentPreview) {documentPreview.init({doc:{");
174 url.append("id:'").append(id).append("',");
175 url.append("fileType:'").append(fileType).append("',");
176 url.append("title:'").append(title).append("',");
177 String linkInDocumentsApp = "";
178 try {
179 linkInDocumentsApp = documentService.getLinkInDocumentsApp(nodePath);
180 } catch (Exception e) {
181 LOG.error("Cannot get link in document app for node " + nodePath, e);
182 }
183 url.append("path:'").append(nodePath)
184 .append("', repository:'").append(repositoryName)
185 .append("', workspace:'").append(workspace)
186 .append("', downloadUrl:'").append(downloadUrl.toString())
187 .append("', openUrl:'").append(linkInDocumentsApp)
188 .append("'}");
189 if(author != null) {
190 url.append(",author:{username:'").append(author).append("'}");
191 }
192
193 url.append("})});void(0);");
194
195 return url.toString();
196 }
197
198 protected String getImageUrl(String workspace, String nodePath) {
199 try {
200 String path = nodePath.replaceAll("'", "\\\\'");
201 String encodedPath = URLEncoder.encode(path, "utf-8");
202 encodedPath = encodedPath.replaceAll ("%2F", "/");
203 String restContextName = WCMCoreUtils.getRestContextName();
204 String repositoryName = null;
205 try {
206 repositoryName = repositoryService.getCurrentRepository().getConfiguration().getName();
207 } catch (RepositoryException e) {
208 LOG.error("Cannot get repository name", e);
209 }
210 String thumbnailImage = "/" + restContextName + "/thumbnailImage/medium/" +
211 repositoryName + "/" + workspace + encodedPath;
212 return thumbnailImage;
213 } catch (UnsupportedEncodingException e) {
214 LOG.error("Cannot encode path " + nodePath, e);
215 return "";
216 }
217 }
218
219
220
221
222
223
224
225 protected Map<String, List<String>> getBreadcrumb(String nodePath) {
226 Map<String, List<String>> uris = new ListHashMap<>();
227
228 try {
229 if (nodePath.endsWith("/")) {
230 nodePath = nodePath.substring(0, nodePath.length() - 1);
231 }
232
233 DriveData drive = documentService.getDriveOfNode(nodePath);
234
235 String nodePathFromDrive = nodePath;
236
237 String driveHomePath = drive.getResolvedHomePath();
238 if(nodePath.startsWith(driveHomePath)) {
239 nodePathFromDrive = nodePath.substring(driveHomePath.length());
240 }
241
242 if (nodePathFromDrive.startsWith("/")) {
243 nodePathFromDrive = nodePathFromDrive.substring(1);
244 }
245
246 String path = driveHomePath;
247 for (String nodeName : nodePathFromDrive.split("/")) {
248 path += "/" + nodeName;
249 try {
250 Node docNode = NodeLocation.getNodeByExpression(
251 WCMCoreUtils.getRepository().getConfiguration().getName() + ":" +
252 drive.getWorkspace() + ":" + path);
253 if(docNode != null) {
254 String nodeTitle = Utils.getTitle(docNode);
255
256 String docLink = documentService.getLinkInDocumentsApp(path, drive);
257
258 List<String> titleAndLink = new ArrayList<>();
259 titleAndLink.add(nodeTitle);
260 titleAndLink.add(docLink);
261
262 uris.put(path, titleAndLink);
263 }
264 } catch (Exception e) {
265 LOG.error("Cannot get title and link of node " + nodeName, e);
266 }
267 }
268
269 } catch (Exception e){
270 LOG.error("Error while building breadcrumb of file " + nodePath, e);
271 }
272
273 return uris;
274 }
275 }