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