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 public static String convertUUIDtoHandle(String uuid, String workspaceName) throws LinkException {
124 return createLinkInstance(workspaceName, uuid).getPath();
125 }
126
127
128
129
130
131 public static String convertUUIDtoURI(String uuid, String workspaceName) throws LinkException {
132 return LinkTransformerManager.getInstance().getAbsolute(false).transform(createLinkInstance(workspaceName, uuid));
133 }
134
135
136
137
138
139
140
141 public static String convertAbsoluteLinksToUUIDs(String html) {
142 if (html == null) {
143 return null;
144 }
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 if (str == null) {
184 return null;
185 }
186 Matcher matcher = UUID_PATTERN.matcher(str);
187 StringBuffer res = new StringBuffer();
188 while (matcher.find()) {
189 Link link = createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
190 String replacement = transformer.transform(link);
191
192 replacement = StringUtils.replace(replacement, "\\", "\\\\");
193 replacement = StringUtils.replace(replacement,"$", "\\$");
194 matcher.appendReplacement(res, replacement);
195 }
196 matcher.appendTail(res);
197 return res.toString();
198 }
199
200 public static String convertLinksFromUUIDPattern(String str) throws LinkException {
201 LinkTransformer transformer = LinkTransformerManager.getInstance().getBrowserLink(null);
202 return convertLinksFromUUIDPattern(str, transformer);
203 }
204
205
206
207
208 public static boolean isInternalRelativeLink(String href) {
209
210 return !isExternalLinkOrAnchor(href) && !href.startsWith("/");
211 }
212
213
214
215
216 public static boolean isExternalLinkOrAnchor(String href) {
217 return LinkUtil.EXTERNAL_LINK_PATTERN.matcher(href).matches() || href.startsWith("#");
218 }
219
220
221
222
223
224
225
226 public static String makePathRelative(String url, String absolutePath){
227 String fromPath = StringUtils.substringBeforeLast(url, "/");
228 String toPath = StringUtils.substringBeforeLast(absolutePath, "/");
229
230
231 if (StringUtils.equals(fromPath, toPath) && StringUtils.endsWith(absolutePath, "/")) {
232 return ".";
233 }
234
235 String[] fromDirectories = StringUtils.split(fromPath, "/");
236 String[] toDirectories = StringUtils.split(toPath, "/");
237
238 int pos=0;
239 while(pos < fromDirectories.length && pos < toDirectories.length && fromDirectories[pos].equals(toDirectories[pos])){
240 pos++;
241 }
242
243 StringBuilder rel = new StringBuilder();
244 for(int i=pos; i < fromDirectories.length; i++ ){
245 rel.append("../");
246 }
247
248 for(int i=pos; i < toDirectories.length; i++ ){
249 rel.append(toDirectories[i] + "/");
250 }
251
252 rel.append(StringUtils.substringAfterLast(absolutePath, "/"));
253
254 return rel.toString();
255 }
256
257
258
259
260
261
262 public static String mapPathToRepository(String path) {
263 String workspaceName = getURI2RepositoryManager().getRepository(path);
264 if(StringUtils.isEmpty(workspaceName)){
265 workspaceName = DEFAULT_REPOSITORY;
266 }
267 return workspaceName;
268 }
269
270
271
272
273
274
275 public static void addParameter(StringBuffer uri, String name, String value) {
276 if (uri.indexOf("?") < 0) {
277 uri.append('?');
278 } else {
279 uri.append('&');
280 }
281 uri.append(name).append('=');
282 try {
283 uri.append(URLEncoder.encode(value, "UTF-8"));
284 } catch (UnsupportedEncodingException e) {
285 throw new RuntimeException("It seems your system does not support UTF-8 !?", e);
286 }
287 }
288
289
290
291
292
293
294
295
296
297
298 public static String createAbsoluteLink(NodeData nodedata) throws LinkException {
299 if(nodedata == null || !nodedata.isExist()){
300 return null;
301 }
302 try {
303 if(nodedata.getType() != PropertyType.BINARY){
304 return createAbsoluteLink(nodedata.getJCRProperty());
305 }
306 return createAbsoluteLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
307 } catch (RepositoryException e) {
308 throw new LinkException(e);
309 }
310 }
311
312
313
314
315
316
317 public static String createAbsoluteLink(Property property) throws LinkException {
318 if(property == null){
319 return null;
320 }
321 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(property));
322 }
323
324
325
326
327
328
329
330
331
332
333 public static String createAbsoluteLink(String workspaceName, String uuid) throws RepositoryException {
334 Node jcrNode = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
335 return createAbsoluteLink(jcrNode);
336 }
337
338
339
340
341
342
343
344
345
346
347 public static String createAbsoluteLink(Content content) {
348 if(content == null){
349 return null;
350 }
351 return createAbsoluteLink(content.getJCRNode());
352 }
353
354
355
356
357
358
359 public static String createAbsoluteLink(Node node) {
360 if(node == null){
361 return null;
362 }
363 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(node));
364 }
365
366
367
368
369
370 public static String createExternalLink(Content content) {
371 if(content == null){
372 return null;
373 }
374 return createExternalLink(content.getJCRNode());
375 }
376
377
378
379
380 public static String createExternalLink(Node node) {
381 if(node == null){
382 return null;
383 }
384 return LinkTransformerManager.getInstance().getCompleteUrl().transform(createLinkInstance(node));
385 }
386
387
388
389
390
391
392
393
394
395
396 public static String createLink(Content node) {
397 if(node == null){
398 return null;
399 }
400 return createLink(node.getJCRNode());
401 }
402
403
404
405
406
407
408
409 public static String createLink(Node node) {
410 if(node == null){
411 return null;
412 }
413 try {
414 return LinkTransformerManager.getInstance().getBrowserLink(node.getPath()).transform(createLinkInstance(node));
415 } catch (RepositoryException e) {
416 log.debug(e.getMessage(), e);
417 }
418 return null;
419 }
420
421
422
423
424
425
426
427
428
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
469 public static String createLink(String workspaceName, String uuid) throws RepositoryException {
470 Node node = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
471 return createLink(node);
472 }
473
474
475
476
477
478
479
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
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 public static Link createLinkInstance(String workspaceName, String uuid) throws LinkException {
522 try {
523 return new Link(MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid));
524 } catch (RepositoryException e) {
525 throw new LinkException("can't get node with uuid " + uuid + " and repository " + workspaceName);
526 }
527 }
528
529
530
531
532
533
534
535
536
537
538 public static Link createLinkInstance(String workspaceName, String path, String extension, String anchor, String parameters) throws LinkException {
539 Node node = null;
540 String fileName = null;
541 String nodeDataName = null;
542 Property property = null;
543 try {
544 Session session = MgnlContext.getJCRSession(workspaceName);
545
546 boolean exists = false;
547 try {
548 PathParser.checkFormat(path);
549 } catch (MalformedPathException e) {
550
551 }
552 exists = session.itemExists(path) && !session.propertyExists(path);
553 if (exists) {
554 node = session.getNode(path);
555 }
556 if (node == null) {
557 if(session.nodeExists(path)){
558 node = session.getNode(path);
559 }
560 if (node != null && node.isNodeType(NodeTypes.Resource.NAME) && node.hasProperty("fileName")) {
561 fileName = node.getProperty("fileName").getString();
562 }
563 if (session.propertyExists(path)) {
564 nodeDataName = StringUtils.substringAfterLast(path, "/");
565 path = StringUtils.substringBeforeLast(path, "/");
566 property = node.getProperty(nodeDataName);
567 }
568 }
569 if (node == null) {
570 throw new LinkException("can't find node " + path + " in repository " + workspaceName);
571 }
572 } catch (RepositoryException e) {
573 throw new LinkException("can't get node with path " + path + " from repository " + workspaceName);
574 }
575
576 Link link = new Link(node);
577 link.setAnchor(anchor);
578 link.setExtension(extension);
579 link.setParameters(parameters);
580 link.setFileName(fileName);
581 link.setPropertyName(nodeDataName);
582 link.setProperty(property);
583 link.setPath(path);
584 return link;
585 }
586
587
588
589
590
591
592
593
594
595
596
597
598
599 public static Link createLinkInstance(String uuid, String workspaceName, String fallbackHandle, String nodeDataName, String extension, String anchor, String parameters) throws LinkException {
600 final String defaultRepository = StringUtils.defaultIfEmpty(workspaceName, RepositoryConstants.WEBSITE);
601 Link link;
602 try {
603 link = createLinkInstance(defaultRepository, uuid);
604 } catch (LinkException e) {
605 try {
606 final Node node = MgnlContext.getJCRSession(defaultRepository).getNode(fallbackHandle != null? fallbackHandle:"");
607 link = createLinkInstance(node);
608 } catch (PathNotFoundException pnfe) {
609 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
610 link = new Link();
611 link.setUUID(uuid);
612 } catch (RepositoryException re) {
613 log.warn("Can't find node with uuid {} or handle {} in repository {}", uuid, fallbackHandle, defaultRepository);
614 link = new Link();
615 link.setUUID(uuid);
616 }
617 }
618 link.setFallbackPath(fallbackHandle);
619 link.setPropertyName(nodeDataName);
620 link.setExtension(extension);
621 link.setAnchor(anchor);
622 link.setParameters(parameters);
623
624 return link;
625 }
626
627
628
629
630
631
632 public static Link parseUUIDLink(String uuidLink) throws LinkException{
633 Matcher matcher = UUID_PATTERN.matcher(uuidLink);
634 if(matcher.matches()){
635 return createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
636 }
637 throw new LinkException("can't parse [ " + uuidLink + "]");
638 }
639
640
641
642
643
644
645 public static Link parseLink(String link) throws LinkException{
646
647 link = StringUtils.removeStart(link, MgnlContext.getContextPath());
648
649 Matcher matcher = LINK_PATTERN.matcher(link);
650 if(matcher.matches()){
651 String orgHandle = matcher.group(1);
652 orgHandle = Components.getComponent(I18nContentSupport.class).toRawURI(orgHandle);
653 String workspaceName = getURI2RepositoryManager().getRepository(orgHandle);
654 String handle = getURI2RepositoryManager().getHandle(orgHandle);
655 return createLinkInstance(workspaceName, handle, matcher.group(3),matcher.group(5),matcher.group(7));
656 }
657 throw new LinkException("can't parse [ " + link + "]");
658 }
659
660
661
662
663
664
665 public static String toPattern(Link link) {
666 return "${link:{"
667 + "uuid:{" + link.getUUID() + "},"
668 + "repository:{" + link.getWorkspace() + "},"
669 + "path:{" + link.getPath() + "},"
670 + "nodeData:{" + StringUtils.defaultString(link.getNodeDataName()) + "},"
671 + "extension:{" + StringUtils.defaultString(link.getExtension()) + "}"
672 + "}}"
673 + (StringUtils.isNotEmpty(link.getAnchor())? "#" + link.getAnchor():"")
674 + (StringUtils.isNotEmpty(link.getParameters())? "?" + link.getParameters() : "");
675 }
676
677 private static URI2RepositoryManager getURI2RepositoryManager(){
678 return Components.getComponent(URI2RepositoryManager.class);
679 }
680 }