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
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
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 workspaceName) throws LinkException {
127 return createLinkInstance(workspaceName, uuid).getPath();
128 }
129
130
131
132
133
134 public static String convertUUIDtoURI(String uuid, String workspaceName) throws LinkException {
135 return LinkTransformerManager.getInstance().getAbsolute(false).transform(createLinkInstance(workspaceName, 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
205 public static boolean isInternalRelativeLink(String href) {
206
207 return !isExternalLinkOrAnchor(href) && !href.startsWith("/");
208 }
209
210
211
212
213 public static boolean isExternalLinkOrAnchor(String href) {
214 return LinkUtil.EXTERNAL_LINK_PATTERN.matcher(href).matches() || href.startsWith("#");
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] + "/");
247 }
248
249 rel.append(StringUtils.substringAfterLast(absolutePath, "/"));
250
251 return rel.toString();
252 }
253
254
255
256
257
258
259 public static String mapPathToRepository(String path) {
260 String workspaceName = getURI2RepositoryManager().getRepository(path);
261 if(StringUtils.isEmpty(workspaceName)){
262 workspaceName = DEFAULT_REPOSITORY;
263 }
264 return workspaceName;
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
295 public static String createAbsoluteLink(NodeData nodedata) throws LinkException {
296 if(nodedata == null || !nodedata.isExist()){
297 return null;
298 }
299 try {
300 if(nodedata.getType() != PropertyType.BINARY){
301 return createAbsoluteLink(nodedata.getJCRProperty());
302 }
303 return createAbsoluteLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
304 } catch (RepositoryException e) {
305 throw new LinkException(e);
306 }
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
331
332 public static String createAbsoluteLink(String workspaceName, String uuid) throws RepositoryException {
333 Node jcrNode = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
334 return createAbsoluteLink(jcrNode);
335 }
336
337
338
339
340
341
342
343
344
345
346 public static String createAbsoluteLink(Content content) {
347 if(content == null){
348 return null;
349 }
350 return createAbsoluteLink(content.getJCRNode());
351 }
352
353
354
355
356
357
358 public static String createAbsoluteLink(Node node) {
359 if(node == null){
360 return null;
361 }
362 return LinkTransformerManager.getInstance().getAbsolute().transform(createLinkInstance(node));
363 }
364
365
366
367
368
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
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
430 public static String createLink(NodeData nodedata) throws LinkException {
431 if(nodedata == null || !nodedata.isExist()){
432 return null;
433 }
434 try {
435 if(nodedata.getType() != PropertyType.BINARY){
436 return createLink(nodedata.getJCRProperty());
437 }
438 return createLink(MgnlContext.getJCRSession(nodedata.getHierarchyManager().getWorkspace().getName()).getNode(nodedata.getHandle()));
439 } catch (RepositoryException e) {
440 throw new LinkException(e.getMessage(), e);
441 }
442 }
443
444
445
446
447
448
449
450
451 public static String createLink(Property property) throws LinkException {
452 if(property == null){
453 return null;
454 }
455 try {
456 return LinkTransformerManager.getInstance().getBrowserLink(property.getParent().getPath()).transform(createLinkInstance(property));
457 } catch (RepositoryException e) {
458 throw new LinkException(e.getMessage(), e);
459 }
460 }
461
462
463
464
465
466
467
468
469
470
471
472 public static String createLink(String workspaceName, String uuid) throws RepositoryException {
473 Node node = MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid);
474 return createLink(node);
475 }
476
477
478
479
480
481
482
483 public static Link createLinkInstance(Content node) {
484 return createLinkInstance(node.getJCRNode());
485 }
486
487
488
489
490
491
492 public static Link createLinkInstance(Node node) {
493 return new Link(node);
494 }
495
496
497
498
499
500
501
502
503 public static Link createLinkInstance(NodeData nodeData) throws LinkException{
504 try {
505 if(nodeData.getType() != PropertyType.BINARY){
506 return createLinkInstance(nodeData.getJCRProperty());
507 }
508 return createLinkInstance(MgnlContext.getJCRSession(nodeData.getHierarchyManager().getWorkspace().getName()).getNode(nodeData.getHandle()));
509 } catch (RepositoryException e) {
510 throw new LinkException("can't find node " + nodeData , e);
511 }
512 }
513
514 public static Link createLinkInstance(Property property) throws LinkException{
515 return new Link(property);
516 }
517
518
519
520
521
522
523
524 public static Link createLinkInstance(String workspaceName, String uuid) throws LinkException {
525 try {
526 return new Link(MgnlContext.getJCRSession(workspaceName).getNodeByIdentifier(uuid));
527 } catch (RepositoryException e) {
528 throw new LinkException("can't get node with uuid " + uuid + " and repository " + workspaceName);
529 }
530 }
531
532
533
534
535
536
537
538
539
540
541
542 public static Link createLinkInstance(String workspaceName, String path, String extension, String anchor, String parameters) throws LinkException {
543 Node node = null;
544 String fileName = null;
545 String nodeDataName = null;
546 Property property = null;
547 try {
548 Session session = MgnlContext.getJCRSession(workspaceName);
549
550 boolean exists = false;
551 try {
552 PathParser.checkFormat(path);
553 } catch (MalformedPathException e) {
554
555 }
556 exists = session.itemExists(path) && !session.propertyExists(path);
557 if (exists) {
558 node = session.getNode(path);
559 }
560 if (node == null) {
561 if(session.nodeExists(path)){
562 node = session.getNode(path);
563 }
564 if (node != null && node.isNodeType(NodeTypes.Resource.NAME) && node.hasProperty("fileName")) {
565 fileName = node.getProperty("fileName").getString();
566 }
567 if (session.propertyExists(path)) {
568 nodeDataName = StringUtils.substringAfterLast(path, "/");
569 path = StringUtils.substringBeforeLast(path, "/");
570 property = node.getProperty(nodeDataName);
571 }
572 }
573 if (node == null) {
574 throw new LinkException("can't find node " + path + " in repository " + workspaceName);
575 }
576 } catch (RepositoryException e) {
577 throw new LinkException("can't get node with path " + path + " from repository " + workspaceName);
578 }
579
580 Link link = new Link(node);
581 link.setAnchor(anchor);
582 link.setExtension(extension);
583 link.setParameters(parameters);
584 link.setFileName(fileName);
585 link.setPropertyName(nodeDataName);
586 link.setProperty(property);
587 link.setPath(path);
588 return link;
589 }
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604 public static Link createLinkInstance(String uuid, String workspaceName, String fallbackHandle, String nodeDataName, String extension, String anchor, String parameters) throws LinkException {
605 final String defaultRepository = StringUtils.defaultIfEmpty(workspaceName, RepositoryConstants.WEBSITE);
606 Link link;
607 try {
608 link = createLinkInstance(defaultRepository, uuid);
609 } catch (LinkException e) {
610 try {
611 final Node node = MgnlContext.getJCRSession(defaultRepository).getNode(fallbackHandle != null? fallbackHandle:"");
612 link = createLinkInstance(node);
613 } catch (PathNotFoundException pnfe) {
614 log.warn("Can't find node with uuid {} or handle {} in repository {}", new Object[]{ uuid, fallbackHandle, defaultRepository});
615 link = new Link();
616 link.setUUID(uuid);
617 } catch (RepositoryException re) {
618 log.warn("Can't find node with uuid {} or handle {} in repository {}", new Object[]{ uuid, fallbackHandle, defaultRepository});
619 link = new Link();
620 link.setUUID(uuid);
621 }
622 }
623 link.setFallbackPath(fallbackHandle);
624 link.setPropertyName(nodeDataName);
625 link.setExtension(extension);
626 link.setAnchor(anchor);
627 link.setParameters(parameters);
628
629 return link;
630 }
631
632
633
634
635
636
637 public static Link parseUUIDLink(String uuidLink) throws LinkException{
638 Matcher matcher = UUID_PATTERN.matcher(uuidLink);
639 if(matcher.matches()){
640 return createLinkInstance(matcher.group(1), matcher.group(2), matcher.group(5), matcher.group(7), matcher.group(8), matcher.group(10), matcher.group(12));
641 }
642 throw new LinkException("can't parse [ " + uuidLink + "]");
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 public static String toPattern(Link link) {
671 return "${link:{"
672 + "uuid:{" + link.getUUID() + "},"
673 + "repository:{" + link.getWorkspace() + "},"
674 + "path:{" + link.getPath() + "},"
675 + "nodeData:{" + StringUtils.defaultString(link.getNodeDataName()) + "},"
676 + "extension:{" + StringUtils.defaultString(link.getExtension()) + "}"
677 + "}}"
678 + (StringUtils.isNotEmpty(link.getAnchor())? "#" + link.getAnchor():"")
679 + (StringUtils.isNotEmpty(link.getParameters())? "?" + link.getParameters() : "");
680 }
681
682 private static URI2RepositoryManager getURI2RepositoryManager(){
683 return Components.getComponent(URI2RepositoryManager.class);
684 }
685 }