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.lang.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 public class LinkUtil {
67
68
69
70
71 public static final Pattern EXTERNAL_LINK_PATTERN = Pattern.compile("^(\\w*://|mailto:|javascript:|tel:).*");
72
73 public static final String DEFAULT_EXTENSION = "html";
74
75 public static final String DEFAULT_REPOSITORY = RepositoryConstants.WEBSITE;
76
77
78
79
80 public static final Pattern LINK_OR_IMAGE_PATTERN = Pattern.compile(
81 "(<(a|img|embed) " +
82 "[^>]*" +
83 "(href|src)[ ]*=[ ]*\")" +
84 "([^\"]*)" +
85 "(\"" +
86 "[^>]*" +
87 ">)");
88
89
90
91
92 public static Pattern UUID_PATTERN = Pattern.compile(
93 "\\$\\{link:\\{uuid:\\{([^\\}]*)\\},"
94 + "repository:\\{([^\\}]*)\\},"
95 + "(workspace:\\{[^\\}]*\\},)?"
96 + "(path|handle):\\{([^\\}]*)\\}"
97 + "(,nodeData:\\{([^\\}]*)\\},"
98 + "extension:\\{([^\\}]*)\\})?"
99 + "\\}\\}"
100 + "(#([^\\?\"]*))?"
101 + "(\\?([^\"]*))?");
102
103
104
105
106 public static final Pattern LINK_PATTERN = Pattern.compile(
107 "(/[^\\.\"#\\?]*)" +
108 "(\\.([\\w[^#\\?]]+))?" +
109 "(#([^\\?\"]*))?" +
110 "(\\?([^\"]*))?"
111 );
112
113
114
115
116 private static final Logger log = LoggerFactory.getLogger(LinkUtil.class);
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 public static String convertAbsoluteLinksToUUIDs(String html) {
143
144 Matcher matcher = LINK_OR_IMAGE_PATTERN.matcher(html);
145 StringBuffer res = new StringBuffer();
146 while (matcher.find()) {
147 final String href = matcher.group(4);
148 if (!isExternalLinkOrAnchor(href)) {
149 try {
150 Link link = parseLink(href);
151 String linkStr = toPattern(link);
152 linkStr = StringUtils.replace(linkStr, "\\", "\\\\");
153 linkStr = StringUtils.replace(linkStr, "$", "\\$");
154 matcher.appendReplacement(res, "$1" + linkStr + "$5");
155 }
156 catch (LinkException e) {
157
158
159 matcher.appendReplacement(res, "$0");
160 log.debug("can't parse link", e);
161 }
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 public static String convertLinksFromUUIDPattern(String str, LinkTransformer transformer) throws LinkException {
181 Matcher matcher = UUID_PATTERN.matcher(str);
182 StringBuffer res = new StringBuffer();
183 while (matcher.find()) {
184 Link link = createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
185 String replacement = transformer.transform(link);
186
187 replacement = StringUtils.replace(replacement, "\\", "\\\\");
188 replacement = StringUtils.replace(replacement,"$", "\\$");
189 matcher.appendReplacement(res, replacement);
190 }
191 matcher.appendTail(res);
192 return res.toString();
193 }
194
195 public static String convertLinksFromUUIDPattern(String str) throws LinkException {
196 LinkTransformer transformer = LinkTransformerManager.getInstance().getBrowserLink(null);
197 return convertLinksFromUUIDPattern(str, transformer);
198 }
199
200
201
202
203 public static boolean isInternalRelativeLink(String href) {
204
205 return !isExternalLinkOrAnchor(href) && !href.startsWith("/");
206 }
207
208
209
210
211 public static boolean isExternalLinkOrAnchor(String href) {
212 return LinkUtil.EXTERNAL_LINK_PATTERN.matcher(href).matches() || href.startsWith("#");
213 }
214
215
216
217
218
219
220
221 public static String makePathRelative(String url, String absolutePath){
222 String fromPath = StringUtils.substringBeforeLast(url, "/");
223 String toPath = StringUtils.substringBeforeLast(absolutePath, "/");
224
225
226 if (StringUtils.equals(fromPath, toPath) && StringUtils.endsWith(absolutePath, "/")) {
227 return ".";
228 }
229
230 String[] fromDirectories = StringUtils.split(fromPath, "/");
231 String[] toDirectories = StringUtils.split(toPath, "/");
232
233 int pos=0;
234 while(pos < fromDirectories.length && pos < toDirectories.length && fromDirectories[pos].equals(toDirectories[pos])){
235 pos++;
236 }
237
238 StringBuilder rel = new StringBuilder();
239 for(int i=pos; i < fromDirectories.length; i++ ){
240 rel.append("../");
241 }
242
243 for(int i=pos; i < toDirectories.length; i++ ){
244 rel.append(toDirectories[i] + "/");
245 }
246
247 rel.append(StringUtils.substringAfterLast(absolutePath, "/"));
248
249 return rel.toString();
250 }
251
252
253
254
255
256
257 public static String mapPathToRepository(String path) {
258 String workspaceName = getURI2RepositoryManager().getRepository(path);
259 if(StringUtils.isEmpty(workspaceName)){
260 workspaceName = DEFAULT_REPOSITORY;
261 }
262 return workspaceName;
263 }
264
265
266
267
268
269
270 public static void addParameter(StringBuffer uri, String name, String value) {
271 if (uri.indexOf("?") < 0) {
272 uri.append('?');
273 } else {
274 uri.append('&');
275 }
276 uri.append(name).append('=');
277 try {
278 uri.append(URLEncoder.encode(value, "UTF-8"));
279 } catch (UnsupportedEncodingException e) {
280 throw new RuntimeException("It seems your system does not support UTF-8 !?", e);
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293 public static String createAbsoluteLink(NodeData nodedata) throws LinkException {
294 if(nodedata == null || !nodedata.isExist()){
295 return null;
296 }
297 try {
298 if(nodedata.getType() != PropertyType.BINARY){
299 return createAbsoluteLink(nodedata.getJCRProperty());
300 }
301 return createAbsoluteLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
302 } catch (RepositoryException e) {
303 throw new LinkException(e);
304 }
305 }
306
307
308
309
310
311
312
313 public static String createAbsoluteLink(Property property) throws LinkException {
314 if(property == null){
315 return null;
316 }
317 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(property));
318 }
319
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
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 public static String createExternalLink(Content content) {
368 if(content == null){
369 return null;
370 }
371 return createExternalLink(content.getJCRNode());
372 }
373
374
375
376
377
378 public static String createExternalLink(Node node) {
379 if(node == null){
380 return null;
381 }
382 return LinkTransformerManager.getInstance().getCompleteUrl().transform(createLinkInstance(node));
383 }
384
385
386
387
388
389
390
391
392
393
394 public static String createLink(Content node) {
395 if(node == null){
396 return null;
397 }
398 return createLink(node.getJCRNode());
399 }
400
401
402
403
404
405
406
407 public static String createLink(Node node) {
408 if(node == null){
409 return null;
410 }
411 try {
412 return LinkTransformerManager.getInstance().getBrowserLink(node.getPath()).transform(createLinkInstance(node));
413 } catch (RepositoryException e) {
414 log.debug(e.getMessage(), e);
415 }
416 return null;
417 }
418
419
420
421
422
423
424
425
426
427
428 public static String createLink(NodeData nodedata) throws LinkException {
429 if(nodedata == null || !nodedata.isExist()){
430 return null;
431 }
432 try {
433 if(nodedata.getType() != PropertyType.BINARY){
434 return createLink(nodedata.getJCRProperty());
435 }
436 return createLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
437 } catch (RepositoryException e) {
438 throw new LinkException(e.getMessage(), e);
439 }
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
470 public static String createLink(String workspaceName, String uuid) throws RepositoryException {
471 Node node = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
472 return createLink(node);
473 }
474
475
476
477
478
479
480
481 public static Link createLinkInstance(Content node) {
482 return createLinkInstance(node.getJCRNode());
483 }
484
485
486
487
488
489
490 protected static Link createLinkInstance(Node node) {
491 return new Link(node);
492 }
493
494
495
496
497
498
499
500
501 public static Link createLinkInstance(NodeData nodeData) throws LinkException{
502 try {
503 if(nodeData.getType() != PropertyType.BINARY){
504 return createLinkInstance(nodeData.getJCRProperty());
505 }
506 return createLinkInstance(MgnlContext.getJCRSession(nodeData.getHierarchyManager().getWorkspace().getName()).getNode(nodeData.getHandle()));
507 } catch (RepositoryException e) {
508 throw new LinkException("can't find node " + nodeData , e);
509 }
510 }
511
512 public static Link createLinkInstance(Property property) throws LinkException{
513 return new Link(property);
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 {}", new Object[]{ 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 {}", new Object[]{ 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 public static Link parseUUIDLink(String uuidLink) throws LinkException{
636 Matcher matcher = UUID_PATTERN.matcher(uuidLink);
637 if(matcher.matches()){
638 return createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
639 }
640 throw new LinkException("can't parse [ " + uuidLink + "]");
641 }
642
643
644
645
646
647
648 public static Link parseLink(String link) throws LinkException{
649
650 link = StringUtils.removeStart(link, MgnlContext.getContextPath());
651
652 Matcher matcher = LINK_PATTERN.matcher(link);
653 if(matcher.matches()){
654 String orgHandle = matcher.group(1);
655 orgHandle = Components.getComponent(I18nContentSupport.class).toRawURI(orgHandle);
656 String workspaceName = getURI2RepositoryManager().getRepository(orgHandle);
657 String handle = getURI2RepositoryManager().getHandle(orgHandle);
658 return createLinkInstance(workspaceName, handle, matcher.group(3),matcher.group(5),matcher.group(7));
659 }
660 throw new LinkException("can't parse [ " + link + "]");
661 }
662
663
664
665
666
667
668 public static String toPattern(Link link) {
669 return "${link:{"
670 + "uuid:{" + link.getUUID() + "},"
671 + "repository:{" + link.getWorkspace() + "},"
672 + "path:{" + link.getPath() + "},"
673 + "nodeData:{" + StringUtils.defaultString(link.getNodeDataName()) + "},"
674 + "extension:{" + StringUtils.defaultString(link.getExtension()) + "}"
675 + "}}"
676 + (StringUtils.isNotEmpty(link.getAnchor())? "#" + link.getAnchor():"")
677 + (StringUtils.isNotEmpty(link.getParameters())? "?" + link.getParameters() : "");
678 }
679
680 private static URI2RepositoryManager getURI2RepositoryManager(){
681 return Components.getComponent(URI2RepositoryManager.class);
682 }
683 }