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.NodeData;
39 import info.magnolia.cms.i18n.I18nContentSupport;
40 import info.magnolia.context.MgnlContext;
41 import info.magnolia.jcr.util.NodeTypes;
42 import info.magnolia.objectfactory.Components;
43 import info.magnolia.repository.RepositoryConstants;
44
45 import java.io.UnsupportedEncodingException;
46 import java.net.URLEncoder;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50 import javax.jcr.Node;
51 import javax.jcr.PathNotFoundException;
52 import javax.jcr.Property;
53 import javax.jcr.PropertyType;
54 import javax.jcr.RepositoryException;
55 import javax.jcr.Session;
56
57 import org.apache.commons.lang3.StringUtils;
58 import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
59 import org.apache.jackrabbit.spi.commons.conversion.PathParser;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63
64
65
66
67
68 public class LinkUtil {
69
70
71
72
73 public static final Pattern EXTERNAL_LINK_PATTERN = Pattern.compile("^(\\w*://|mailto:|javascript:|tel:).*");
74
75 public static final String DEFAULT_EXTENSION = "html";
76
77 public static final String DEFAULT_REPOSITORY = RepositoryConstants.WEBSITE;
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 private static final Logger log = LoggerFactory.getLogger(LinkUtil.class);
116
117
118
119
120
121
122
123
124 public static String convertUUIDtoHandle(String uuid, String workspaceName) throws LinkException {
125 return createLinkInstance(workspaceName, uuid).getPath();
126 }
127
128
129
130
131
132 public static String convertUUIDtoURI(String uuid, String workspaceName) throws LinkException {
133 return LinkTransformerManager.getInstance().getAbsolute(false).transform(createLinkInstance(workspaceName, uuid));
134 }
135
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 } catch (LinkException e) {
158
159
160 matcher.appendReplacement(res, "$0");
161 log.debug("can't parse link", e);
162 }
163 } else {
164 matcher.appendReplacement(res, "$0");
165 }
166 }
167 matcher.appendTail(res);
168 return res.toString();
169 }
170
171
172
173
174
175
176
177
178
179
180
181 public static String convertLinksFromUUIDPattern(String str, LinkTransformer transformer) throws LinkException {
182 Matcher matcher = UUID_PATTERN.matcher(str);
183 StringBuffer res = new StringBuffer();
184 while (matcher.find()) {
185 Link link = createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
186 String replacement = transformer.transform(link);
187
188 replacement = StringUtils.replace(replacement, "\\", "\\\\");
189 replacement = StringUtils.replace(replacement, "$", "\\$");
190 matcher.appendReplacement(res, replacement);
191 }
192 matcher.appendTail(res);
193 return res.toString();
194 }
195
196 public static String convertLinksFromUUIDPattern(String str) throws LinkException {
197 LinkTransformer transformer = LinkTransformerManager.getInstance().getBrowserLink(null);
198 return convertLinksFromUUIDPattern(str, transformer);
199 }
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
223 public static String makePathRelative(String url, String absolutePath) {
224 String fromPath = StringUtils.substringBeforeLast(url, "/");
225 String toPath = StringUtils.substringBeforeLast(absolutePath, "/");
226
227
228 if (StringUtils.equals(fromPath, toPath) && StringUtils.endsWith(absolutePath, "/")) {
229 return ".";
230 }
231
232 String[] fromDirectories = StringUtils.split(fromPath, "/");
233 String[] toDirectories = StringUtils.split(toPath, "/");
234
235 int pos = 0;
236 while (pos < fromDirectories.length && pos < toDirectories.length && fromDirectories[pos].equals(toDirectories[pos])) {
237 pos++;
238 }
239
240 StringBuilder rel = new StringBuilder();
241 for (int i = pos; i < fromDirectories.length; i++) {
242 rel.append("../");
243 }
244
245 for (int i = pos; i < toDirectories.length; i++) {
246 rel.append(toDirectories[i]).append("/");
247 }
248
249 rel.append(StringUtils.substringAfterLast(absolutePath, "/"));
250
251 return rel.toString();
252 }
253
254
255
256
257
258
259
260 public static String mapPathToRepository(String path) {
261 String workspaceName = getURI2RepositoryManager().getRepository(path);
262 if (StringUtils.isEmpty(workspaceName)) {
263 workspaceName = DEFAULT_REPOSITORY;
264 }
265 return workspaceName;
266 }
267
268
269
270
271
272
273 public static void addParameter(StringBuffer uri, String name, String value) {
274 if (uri.indexOf("?") < 0) {
275 uri.append('?');
276 } else {
277 uri.append('&');
278 }
279 uri.append(name).append('=');
280 try {
281 uri.append(URLEncoder.encode(value, "UTF-8"));
282 } catch (UnsupportedEncodingException e) {
283 throw new RuntimeException("It seems your system does not support UTF-8 !?", e);
284 }
285 }
286
287
288
289
290
291
292
293
294
295 @Deprecated
296 public static String createAbsoluteLink(NodeData nodedata) throws LinkException {
297 if (nodedata == null || !nodedata.isExist()) {
298 return null;
299 }
300 try {
301 if (nodedata.getType() != PropertyType.BINARY) {
302 return createAbsoluteLink(nodedata.getJCRProperty());
303 }
304 return createAbsoluteLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
305 } catch (RepositoryException e) {
306 throw new LinkException(e);
307 }
308 }
309
310
311
312
313
314
315 public static String createAbsoluteLink(Property property) throws LinkException {
316 if (property == null) {
317 return null;
318 }
319 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(property));
320 }
321
322
323
324
325
326
327
328
329
330 public static String createAbsoluteLink(String workspaceName, String uuid) throws RepositoryException {
331 Node jcrNode = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
332 return createAbsoluteLink(jcrNode);
333 }
334
335
336
337
338
339
340
341
342
343 @Deprecated
344 public static String createAbsoluteLink(Content content) {
345 if (content == null) {
346 return null;
347 }
348 return createAbsoluteLink(content.getJCRNode());
349 }
350
351
352
353
354
355
356 public static String createAbsoluteLink(Node node) {
357 if (node == null) {
358 return null;
359 }
360 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(node));
361 }
362
363
364
365
366
367
368 @Deprecated
369 public static String createExternalLink(Content content) {
370 if (content == null) {
371 return null;
372 }
373 return createExternalLink(content.getJCRNode());
374 }
375
376
377
378
379 public static String createExternalLink(Node node) {
380 if (node == null) {
381 return null;
382 }
383 return LinkTransformerManager.getInstance().getCompleteUrl().transform(createLinkInstance(node));
384 }
385
386
387
388
389
390
391
392
393
394 @Deprecated
395 public static String createLink(Content node) {
396 if (node == null) {
397 return null;
398 }
399 return createLink(node.getJCRNode());
400 }
401
402
403
404
405
406
407
408 public static String createLink(Node node) {
409 if (node == null) {
410 return null;
411 }
412 try {
413 return LinkTransformerManager.getInstance().getBrowserLink(node.getPath()).transform(createLinkInstance(node));
414 } catch (RepositoryException e) {
415 log.debug(e.getMessage(), e);
416 }
417 return null;
418 }
419
420
421
422
423
424
425
426
427
428 @Deprecated
429 public static String createLink(NodeData nodedata) throws LinkException {
430 if (nodedata == null || !nodedata.isExist()) {
431 return null;
432 }
433 try {
434 if (nodedata.getType() != PropertyType.BINARY) {
435 return createLink(nodedata.getJCRProperty());
436 }
437 return createLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
438 } catch (RepositoryException e) {
439 throw new LinkException(e.getMessage(), e);
440 }
441 }
442
443
444
445
446
447
448
449 public static String createLink(Property property) throws LinkException {
450 if (property == null) {
451 return null;
452 }
453 try {
454 return LinkTransformerManager.getInstance().getBrowserLink(property.getParent().getPath()).transform(createLinkInstance(property));
455 } catch (RepositoryException e) {
456 throw new LinkException(e.getMessage(), e);
457 }
458 }
459
460
461
462
463
464
465
466
467
468 public static String createLink(String workspaceName, String uuid) throws RepositoryException {
469 Node node = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
470 return createLink(node);
471 }
472
473
474
475
476
477
478
479 @Deprecated
480 public static Link createLinkInstance(Content node) {
481 return createLinkInstance(node.getJCRNode());
482 }
483
484
485
486
487
488
489 public static Link createLinkInstance(Node node) {
490 return new Link(node);
491 }
492
493
494
495
496
497
498
499 @Deprecated
500 public static Link createLinkInstance(NodeData nodeData) throws LinkException {
501 try {
502 if (nodeData.getType() != PropertyType.BINARY) {
503 return createLinkInstance(nodeData.getJCRProperty());
504 }
505 return createLinkInstance(MgnlContext.getJCRSession(nodeData.getHierarchyManager().getWorkspace().getName()).getNode(nodeData.getHandle()));
506 } catch (RepositoryException e) {
507 throw new LinkException("can't find node " + nodeData, e);
508 }
509 }
510
511 public static Link createLinkInstance(Property property) throws LinkException {
512 return new Link(property);
513 }
514
515
516
517
518
519
520
521
522 public static Link createLinkInstance(String workspaceName, String uuid) throws LinkException {
523 try {
524 return new Link(MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid));
525 } catch (RepositoryException e) {
526 throw new LinkException("can't get node with uuid " + uuid + " and repository " + workspaceName);
527 }
528 }
529
530
531
532
533
534
535
536
537
538
539
540 public static Link createLinkInstance(String workspaceName, String path, String extension, String anchor, String parameters) throws LinkException {
541 Node node = null;
542 String fileName = null;
543 String nodeDataName = null;
544 Property property = null;
545 try {
546 Session session = MgnlContext.getJCRSession(workspaceName);
547
548 boolean exists = false;
549 try {
550 PathParser.checkFormat(path);
551 } catch (MalformedPathException e) {
552
553 }
554 exists = session.itemExists(path) && !session.propertyExists(path);
555 if (exists) {
556 node = session.getNode(path);
557 }
558 if (node == null) {
559 if (session.nodeExists(path)) {
560 node = session.getNode(path);
561 }
562 if (node != null && node.isNodeType(NodeTypes.Resource.NAME) && node.hasProperty("fileName")) {
563 fileName = node.getProperty("fileName").getString();
564 }
565 if (session.propertyExists(path)) {
566 nodeDataName = StringUtils.substringAfterLast(path, "/");
567 path = StringUtils.substringBeforeLast(path, "/");
568 property = node.getProperty(nodeDataName);
569 }
570 }
571 if (node == null) {
572 throw new LinkException("can't find node " + path + " in repository " + workspaceName);
573 }
574 } catch (RepositoryException e) {
575 throw new LinkException("can't get node with path " + path + " from repository " + workspaceName);
576 }
577
578 Link link = new Link(node);
579 link.setAnchor(anchor);
580 link.setExtension(extension);
581 link.setParameters(parameters);
582 link.setFileName(fileName);
583 link.setPropertyName(nodeDataName);
584 link.setProperty(property);
585 link.setPath(path);
586 return link;
587 }
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602 public static Link createLinkInstance(String uuid, String workspaceName, String fallbackHandle, String nodeDataName, String extension, String anchor, String parameters) throws LinkException {
603 final String defaultRepository = StringUtils.defaultIfEmpty(workspaceName, RepositoryConstants.WEBSITE);
604 Link link;
605 try {
606 link = createLinkInstance(defaultRepository, uuid);
607 } catch (LinkException e) {
608 try {
609 final Node node = MgnlContext.getJCRSession(defaultRepository).getNode(fallbackHandle != null ? fallbackHandle : "");
610 link = createLinkInstance(node);
611 } catch (PathNotFoundException pnfe) {
612 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
613 link = new Link();
614 link.setUUID(uuid);
615 } catch (RepositoryException re) {
616 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
617 link = new Link();
618 link.setUUID(uuid);
619 }
620 }
621 link.setFallbackPath(fallbackHandle);
622 link.setPropertyName(nodeDataName);
623 link.setExtension(extension);
624 link.setAnchor(anchor);
625 link.setParameters(parameters);
626
627 return link;
628 }
629
630
631
632
633
634
635
636 public static Link parseUUIDLink(String uuidLink) throws LinkException {
637 Matcher matcher = UUID_PATTERN.matcher(uuidLink);
638 if (matcher.matches()) {
639 return createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
640 }
641 throw new LinkException("can't parse [ " + uuidLink + "]");
642 }
643
644
645
646
647
648
649
650 public static Link parseLink(String link) throws LinkException {
651
652 link = StringUtils.removeStart(link, MgnlContext.getContextPath());
653
654 Matcher matcher = LINK_PATTERN.matcher(link);
655 if (matcher.matches()) {
656 String orgHandle = matcher.group(1);
657 orgHandle = Components.getComponent(I18nContentSupport.class).toRawURI(orgHandle);
658 String workspaceName = getURI2RepositoryManager().getRepository(orgHandle);
659 String handle = getURI2RepositoryManager().getHandle(orgHandle);
660 return createLinkInstance(workspaceName, handle, matcher.group(3), matcher.group(5), matcher.group(7));
661 }
662 throw new LinkException("can't parse [ " + link + "]");
663 }
664
665
666
667
668
669
670
671 public static String toPattern(Link link) {
672 return "${link:{"
673 + "uuid:{" + link.getUUID() + "},"
674 + "repository:{" + link.getWorkspace() + "},"
675 + "path:{" + link.getPath() + "},"
676 + "nodeData:{" + StringUtils.defaultString(link.getNodeDataName()) + "},"
677 + "extension:{" + StringUtils.defaultString(link.getExtension()) + "}"
678 + "}}"
679 + (StringUtils.isNotEmpty(link.getAnchor()) ? "#" + link.getAnchor() : "")
680 + (StringUtils.isNotEmpty(link.getParameters()) ? "?" + link.getParameters() : "");
681 }
682
683 private static URI2RepositoryManager getURI2RepositoryManager() {
684 return Components.getComponent(URI2RepositoryManager.class);
685 }
686 }