1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.link;
35
36 import info.magnolia.cms.beans.config.URI2RepositoryManager;
37 import info.magnolia.cms.core.Content;
38 import info.magnolia.cms.core.HierarchyManager;
39 import info.magnolia.cms.core.MgnlNodeType;
40 import info.magnolia.cms.core.NodeData;
41 import info.magnolia.cms.i18n.I18nContentSupport;
42 import info.magnolia.cms.util.ContentUtil;
43 import info.magnolia.context.MgnlContext;
44 import info.magnolia.objectfactory.Components;
45 import info.magnolia.repository.RepositoryConstants;
46
47 import java.io.UnsupportedEncodingException;
48 import java.net.URLEncoder;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51
52 import javax.jcr.Node;
53 import javax.jcr.PathNotFoundException;
54 import javax.jcr.RepositoryException;
55
56 import org.apache.commons.lang.StringUtils;
57 import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
58 import org.apache.jackrabbit.spi.commons.conversion.PathParser;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62
63
64
65 public class LinkUtil {
66
67
68
69
70 public static final Pattern EXTERNAL_LINK_PATTERN = Pattern.compile("^(\\w*://|mailto:|javascript:|tel:).*");
71
72 public static final String DEFAULT_EXTENSION = "html";
73
74 public static final String DEFAULT_REPOSITORY = RepositoryConstants.WEBSITE;
75
76
77
78
79
80
81
82 public static final Pattern LINK_OR_IMAGE_PATTERN = Pattern.compile(
83 "(<(a|img|embed) " +
84 "[^>]*" +
85 "(href|src)[ ]*=[ ]*\")" +
86 "([^\"]*)" +
87 "(\"" +
88 "[^>]*" +
89 ">)");
90
91
92
93
94 public static Pattern UUID_PATTERN = Pattern.compile(
95 "\\$\\{link:\\{uuid:\\{([^\\}]*)\\},"
96 + "repository:\\{([^\\}]*)\\},"
97 + "(workspace:\\{[^\\}]*\\},)?"
98 + "(path|handle):\\{([^\\}]*)\\}"
99 + "(,nodeData:\\{([^\\}]*)\\},"
100 + "extension:\\{([^\\}]*)\\})?"
101 + "\\}\\}"
102 + "(#([^\\?\"]*))?"
103 + "(\\?([^\"]*))?");
104
105
106
107
108 public static final Pattern LINK_PATTERN = Pattern.compile(
109 "(/[^\\.\"#\\?]*)" +
110 "(\\.([\\w[^#\\?]]+))?" +
111 "(#([^\\?\"]*))?" +
112 "(\\?([^\"]*))?"
113 );
114
115
116
117
118 private static final Logger log = LoggerFactory.getLogger(LinkUtil.class);
119
120
121
122
123
124
125
126 public static String convertUUIDtoHandle(String uuid, String repository) throws LinkException {
127 return createLinkInstance(repository, uuid).getHandle();
128 }
129
130
131
132
133
134 public static String convertUUIDtoURI(String uuid, String repository) throws LinkException {
135 return LinkTransformerManager.getInstance().getAbsolute(false).transform(createLinkInstance(repository, uuid));
136 }
137
138
139
140
141
142
143
144 public static String convertAbsoluteLinksToUUIDs(String html) {
145
146 Matcher matcher = LINK_OR_IMAGE_PATTERN.matcher(html);
147 StringBuffer res = new StringBuffer();
148 while (matcher.find()) {
149 final String href = matcher.group(4);
150 if (!isExternalLinkOrAnchor(href)) {
151 try {
152 Link link = parseLink(href);
153 String linkStr = toPattern(link);
154 linkStr = StringUtils.replace(linkStr, "\\", "\\\\");
155 linkStr = StringUtils.replace(linkStr, "$", "\\$");
156 matcher.appendReplacement(res, "$1" + linkStr + "$5");
157 }
158 catch (LinkException e) {
159
160
161 matcher.appendReplacement(res, "$0");
162 log.debug("can't parse link", e);
163 }
164 }
165 else{
166 matcher.appendReplacement(res, "$0");
167 }
168 }
169 matcher.appendTail(res);
170 return res.toString();
171 }
172
173
174
175
176
177
178
179
180
181
182 public static String convertLinksFromUUIDPattern(String str, LinkTransformer transformer) throws LinkException {
183 Matcher matcher = UUID_PATTERN.matcher(str);
184 StringBuffer res = new StringBuffer();
185 while (matcher.find()) {
186 Link link = createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
187 String replacement = transformer.transform(link);
188
189 replacement = StringUtils.replace(replacement, "\\", "\\\\");
190 replacement = StringUtils.replace(replacement,"$", "\\$");
191 matcher.appendReplacement(res, replacement);
192 }
193 matcher.appendTail(res);
194 return res.toString();
195 }
196
197 public static String convertLinksFromUUIDPattern(String str) throws LinkException {
198 LinkTransformer transformer = LinkTransformerManager.getInstance().getBrowserLink(null);
199 return convertLinksFromUUIDPattern(str, transformer);
200 }
201
202
203
204 public static boolean isInternalRelativeLink(String href) {
205
206 return !isExternalLinkOrAnchor(href) && !href.startsWith("/");
207 }
208
209
210
211
212 public static boolean isExternalLinkOrAnchor(String href) {
213 return LinkUtil.EXTERNAL_LINK_PATTERN.matcher(href).matches() || href.startsWith("#");
214 }
215
216
217
218
219
220
221
222 public static String makePathRelative(String url, String absolutePath){
223 String fromPath = StringUtils.substringBeforeLast(url, "/");
224 String toPath = StringUtils.substringBeforeLast(absolutePath, "/");
225
226
227 if (StringUtils.equals(fromPath, toPath) && StringUtils.endsWith(absolutePath, "/")) {
228 return ".";
229 }
230
231 String[] fromDirectories = StringUtils.split(fromPath, "/");
232 String[] toDirectories = StringUtils.split(toPath, "/");
233
234 int pos=0;
235 while(pos < fromDirectories.length && pos < toDirectories.length && fromDirectories[pos].equals(toDirectories[pos])){
236 pos++;
237 }
238
239 StringBuilder rel = new StringBuilder();
240 for(int i=pos; i < fromDirectories.length; i++ ){
241 rel.append("../");
242 }
243
244 for(int i=pos; i < toDirectories.length; i++ ){
245 rel.append(toDirectories[i] + "/");
246 }
247
248 rel.append(StringUtils.substringAfterLast(absolutePath, "/"));
249
250 return rel.toString();
251 }
252
253
254
255
256
257
258 public static String mapPathToRepository(String path) {
259
260 String repository = getURI2RepositoryManager().getRepository(path);
261 if(StringUtils.isEmpty(repository)){
262 repository = DEFAULT_REPOSITORY;
263 }
264 return repository;
265 }
266
267
268
269
270
271
272 public static void addParameter(StringBuffer uri, String name, String value) {
273 if (uri.indexOf("?") < 0) {
274 uri.append('?');
275 } else {
276 uri.append('&');
277 }
278 uri.append(name).append('=');
279 try {
280 uri.append(URLEncoder.encode(value, "UTF-8"));
281 } catch (UnsupportedEncodingException e) {
282 throw new RuntimeException("It seems your system does not support UTF-8 !?", e);
283 }
284 }
285
286
287
288
289
290
291
292
293
294 public static String createAbsoluteLink(NodeData nodedata) throws LinkException {
295 if(nodedata == null || !nodedata.isExist()){
296 return null;
297 }
298 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(nodedata));
299 }
300
301
302
303
304
305
306
307
308
309
310
311 public static String createAbsoluteLink(String repository, String uuid) throws RepositoryException {
312 Node jcrNode = MgnlContext.getJCRSession(repository).getNodeByIdentifier(uuid);
313
314 return createAbsoluteLink(ContentUtil.asContent(jcrNode));
315 }
316
317
318
319
320
321
322
323
324
325 public static String createAbsoluteLink(Content content) {
326 if(content == null){
327 return null;
328 }
329 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(content));
330 }
331
332
333
334
335 public static String createExternalLink(Content content) {
336 if(content == null){
337 return null;
338 }
339 return LinkTransformerManager.getInstance().getCompleteUrl().transform(createLinkInstance(content));
340 }
341
342
343
344
345
346
347
348
349
350 public static String createLink(Content node) {
351 if(node == null){
352 return null;
353 }
354 if (node.isNodeType(MgnlNodeType.NT_RESOURCE)) {
355 try {
356
357 String name = node.getName();
358 Content parent = node.getParent();
359 return LinkUtil.createLink(parent.getNodeData(name));
360 } catch (RepositoryException e) {
361 log.debug(e.getMessage(), e);
362 } catch (LinkException e) {
363 log.debug(e.getMessage(), e);
364 }
365 }
366 return LinkTransformerManager.getInstance().getBrowserLink(node.getHandle()).transform(createLinkInstance(node));
367 }
368
369
370
371
372
373
374
375
376
377 public static String createLink(NodeData nodedata) throws LinkException {
378 if(nodedata == null || !nodedata.isExist()){
379 return null;
380 }
381 try {
382 return LinkTransformerManager.getInstance().getBrowserLink(nodedata.getParent().getHandle()).transform(LinkFactory.createLink(nodedata));
383 } catch (RepositoryException e) {
384 throw new LinkException(e.getMessage(), e);
385 }
386 }
387
388
389
390
391
392
393
394
395
396
397
398 public static String createLink(String repository, String uuid) throws RepositoryException {
399 Node node = MgnlContext.getJCRSession(repository).getNodeByIdentifier(uuid);
400
401 return createLink(ContentUtil.asContent(node));
402 }
403
404 public static Link createLinkInstance(Content node) {
405 return new Link(node);
406 }
407
408 public static Link createLinkInstance(NodeData nodeData) throws LinkException{
409 try {
410 return new Link(nodeData.getParent().getWorkspace().getName(), nodeData.getParent(), nodeData);
411 } catch (RepositoryException e) {
412 throw new LinkException("can't find node " + nodeData , e);
413 }
414 }
415
416
417
418
419
420
421
422 public static Link createLinkInstance(String repository, String uuid) throws LinkException {
423 try {
424 return new Link(MgnlContext.getHierarchyManager(repository).getContentByUUID(uuid));
425 } catch (RepositoryException e) {
426 throw new LinkException("can't get node with uuid " + uuid + " and repository " + repository);
427 }
428 }
429
430
431
432
433
434
435
436
437
438
439
440 public static Link createLinkInstance(String repository, String path, String extension, String anchor, String parameters) throws LinkException {
441 Content node = null;
442 String fileName = null;
443 String nodeDataName = null;
444 NodeData nodeData = null;
445 try {
446 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
447 boolean exists = false;
448 try {
449
450
451 PathParser.checkFormat(path);
452 } catch (MalformedPathException e) {
453
454 exists = false;
455 }
456 exists = hm.isExist(path) && !hm.isNodeData(path);
457 if (exists) {
458 node = hm.getContent(path);
459 }
460 if (node == null) {
461
462
463 if (hm.isNodeData(StringUtils.substringBeforeLast(path, "/"))) {
464 fileName = StringUtils.substringAfterLast(path, "/");
465 path = StringUtils.substringBeforeLast(path, "/");
466 }
467
468
469 if (hm.isNodeData(path)) {
470 nodeDataName = StringUtils.substringAfterLast(path, "/");
471 path = StringUtils.substringBeforeLast(path, "/");
472 node = hm.getContent(path);
473 nodeData = node.getNodeData(nodeDataName);
474 }
475 }
476 if (node == null) {
477 throw new LinkException("can't find node " + path + " in repository " + repository);
478 }
479 } catch (RepositoryException e) {
480 throw new LinkException("can't get node with path " + path + " from repository " + repository);
481 }
482
483 Link link = new Link(node);
484 link.setAnchor(anchor);
485 link.setExtension(extension);
486 link.setParameters(parameters);
487 link.setFileName(fileName);
488 link.setNodeDataName(nodeDataName);
489 link.setNodeData(nodeData);
490 link.setHandle(path);
491 return link;
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 public static Link createLinkInstance(String uuid, String repository, String fallbackHandle, String nodeDataName, String extension, String anchor, String parameters) throws LinkException {
508 final String defaultRepository = StringUtils.defaultIfEmpty(repository, RepositoryConstants.WEBSITE);
509 Link link;
510 try {
511 link = createLinkInstance(defaultRepository, uuid);
512 } catch (LinkException e) {
513 try {
514 final Content node = MgnlContext.getHierarchyManager(defaultRepository).getContent(fallbackHandle != null? fallbackHandle:"");
515 link = createLinkInstance(node);
516 } catch (PathNotFoundException pnfe) {
517 log.warn("Can't find node with uuid {} or handle {} in repository {}", new Object[]{ uuid, fallbackHandle, defaultRepository});
518 link = new Link();
519 link.setUUID(uuid);
520 } catch (RepositoryException re) {
521 log.warn("Can't find node with uuid {} or handle {} in repository {}", new Object[]{ uuid, fallbackHandle, defaultRepository});
522 link = new Link();
523 link.setUUID(uuid);
524 }
525 }
526 link.setFallbackHandle(fallbackHandle);
527 link.setNodeDataName(nodeDataName);
528 link.setExtension(extension);
529 link.setAnchor(anchor);
530 link.setParameters(parameters);
531
532 return link;
533 }
534
535
536
537
538
539
540 public static Link parseUUIDLink(String uuidLink) throws LinkException{
541 Matcher matcher = UUID_PATTERN.matcher(uuidLink);
542 if(matcher.matches()){
543 return createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
544 }
545 throw new LinkException("can't parse [ " + uuidLink + "]");
546 }
547
548
549
550
551
552
553 public static Link parseLink(String link) throws LinkException{
554
555 link = StringUtils.removeStart(link, MgnlContext.getContextPath());
556
557 Matcher matcher = LINK_PATTERN.matcher(link);
558 if(matcher.matches()){
559 String orgHandle = matcher.group(1);
560 orgHandle = Components.getComponent(I18nContentSupport.class).toRawURI(orgHandle);
561 String repository = getURI2RepositoryManager().getRepository(orgHandle);
562 String handle = getURI2RepositoryManager().getHandle(orgHandle);
563 return createLinkInstance(repository, handle, matcher.group(3),matcher.group(5),matcher.group(7));
564 }
565 throw new LinkException("can't parse [ " + link + "]");
566 }
567
568
569
570
571
572
573
574 public static String toPattern(Link link) {
575 return "${link:{"
576 + "uuid:{" + link.getUUID() + "},"
577 + "repository:{" + link.getRepository() + "},"
578 + "handle:{" + link.getHandle() + "},"
579 + "nodeData:{" + StringUtils.defaultString(link.getNodeDataName()) + "},"
580 + "extension:{" + StringUtils.defaultString(link.getExtension()) + "}"
581 + "}}"
582 + (StringUtils.isNotEmpty(link.getAnchor())? "#" + link.getAnchor():"")
583 + (StringUtils.isNotEmpty(link.getParameters())? "?" + link.getParameters() : "");
584 }
585
586 private static URI2RepositoryManager getURI2RepositoryManager(){
587 return Components.getComponent(URI2RepositoryManager.class);
588 }
589 }