diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ChatRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ChatRequestHandler.java index 7baffb262..c1258bfd4 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ChatRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ChatRequestHandler.java @@ -66,7 +66,7 @@ public class ChatRequestHandler extends LotroServerRequestHandler implements Uri final boolean admin = resourceOwner.hasType(Player.Type.ADMIN); final boolean leagueAdmin = resourceOwner.hasType(Player.Type.LEAGUE_ADMIN); if (message != null && !message.trim().isEmpty()) { - String newMsg = _markdownParser.renderMarkdown(message); + String newMsg = _markdownParser.renderMarkdown(message, true); chatRoom.sendMessage(resourceOwner.getName(), newMsg, admin); responseWriter.writeXmlResponse(null); } else { diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/DeckRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/DeckRequestHandler.java index b281a4f45..0f7f3a796 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/DeckRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/DeckRequestHandler.java @@ -2,6 +2,7 @@ package com.gempukku.lotro.async.handler; import com.gempukku.lotro.async.HttpProcessingException; import com.gempukku.lotro.async.ResponseWriter; +import com.gempukku.lotro.chat.MarkdownParser; import com.gempukku.lotro.common.JSONDefs; import com.gempukku.lotro.common.Side; import com.gempukku.lotro.db.DeckDAO; @@ -39,6 +40,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri private final LotroFormatLibrary _formatLibrary; private final SoloDraftDefinitions _draftLibrary; private final LotroServer _lotroServer; + private final MarkdownParser _markdownParser; private static final Logger _log = LogManager.getLogger(DeckRequestHandler.class); @@ -50,6 +52,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri _formatLibrary = extractObject(context, LotroFormatLibrary.class); _lotroServer = extractObject(context, LotroServer.class); _draftLibrary = extractObject(context, SoloDraftDefinitions.class); + _markdownParser = extractObject(context, MarkdownParser.class); } @Override @@ -395,7 +398,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri for (CardCollection.Item item : _sortAndFilterCards.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), _library, _formatLibrary)) result.append(item.getCount() + "x " + generateCardTooltip(item) + "
"); - result.append("

Notes


" + deck.getNotes().replace("\n", "
")); + result.append("

Notes


" + _markdownParser.renderMarkdown(deck.getNotes(), true)); result.append(""); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/builder/ServerBuilder.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/builder/ServerBuilder.java index a700f2e4a..388290f61 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/builder/ServerBuilder.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/builder/ServerBuilder.java @@ -114,7 +114,8 @@ public class ServerBuilder { extract(objectMap, DeckDAO.class), extract(objectMap, LotroCardBlueprintLibrary.class), extract(objectMap, ChatServer.class), - extract(objectMap, GameRecorder.class))); + extract(objectMap, GameRecorder.class), + extract(objectMap, MarkdownParser.class))); objectMap.put(HallServer.class, new HallServer( diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/MarkdownParser.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/MarkdownParser.java index 80be5c294..b45ac67d3 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/MarkdownParser.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/MarkdownParser.java @@ -20,24 +20,33 @@ public class MarkdownParser { private final Parser _markdownParser; private final HtmlRenderer _markdownRenderer; + private final HtmlRenderer _noHiddenLinksRenderer; private static Pattern QuoteExtender = Pattern.compile("^([ \t]*>[ \t]*.+)(?=\n[ \t]*[^>])", Pattern.MULTILINE); public MarkdownParser() { - List adminExt = Arrays.asList(StrikethroughExtension.create(), AutolinkExtension.create()); + List extensions = Arrays.asList(StrikethroughExtension.create(), AutolinkExtension.create()); _markdownParser = Parser.builder() - .extensions(adminExt) + .extensions(extensions) .build(); + _markdownRenderer = HtmlRenderer.builder() .nodeRendererFactory(htmlContext -> new LinkShredder(htmlContext)) - .extensions(adminExt) + .extensions(extensions) + .escapeHtml(true) + .sanitizeUrls(true) + .softbreak("
") + .build(); + + _noHiddenLinksRenderer = HtmlRenderer.builder() + .extensions(extensions) .escapeHtml(true) .sanitizeUrls(true) .softbreak("
") .build(); } - public String renderMarkdown(String markdown) { + public String renderMarkdown(String markdown, boolean shredLinks) { String newMsg = markdown.trim().replaceAll("\n\n\n+", "\n\n\n"); newMsg = QuoteExtender.matcher(newMsg).replaceAll("$1\n"); //Escaping underscores so that URLs with lots of underscores (i.e. wiki links) aren't mangled @@ -45,13 +54,23 @@ public class MarkdownParser { newMsg = newMsg.replace("_", "\\_"); //Need to preserve any commands being made - if(!newMsg.startsWith("/")) { - newMsg = _markdownRenderer.render(_markdownParser.parse(newMsg)); - // Prevent quotes with newlines from displaying side-by-side - newMsg = newMsg.replaceAll("[\n \t]*
", "

"); - //Make all links open in a new tab - newMsg = newMsg.replaceAll("<(a href=\".*?\")>", "<$1 target=\"_blank\">"); + if(newMsg.startsWith("/")) + return newMsg; + + + if(shredLinks) { + newMsg = _noHiddenLinksRenderer.render(_markdownParser.parse(newMsg)); } + else { + newMsg = _markdownRenderer.render(_markdownParser.parse(newMsg)); + } + + // Prevent quotes with newlines from displaying side-by-side + newMsg = newMsg.replaceAll("
[\n \t]*
", "

"); + //Make all links open in a new tab + newMsg = newMsg.replaceAll("<(a href=\".*?\")>", "<$1 target=\"_blank\">"); + //Finally, make all newlines into html breaks + newMsg = newMsg.replaceAll("\n", "
"); return newMsg; } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameMediator.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameMediator.java index 2f0082e61..ee4c6cc3b 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameMediator.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameMediator.java @@ -3,6 +3,7 @@ package com.gempukku.lotro.game; import com.gempukku.lotro.PrivateInformationException; import com.gempukku.lotro.SubscriptionConflictException; import com.gempukku.lotro.SubscriptionExpiredException; +import com.gempukku.lotro.chat.MarkdownParser; import com.gempukku.lotro.common.*; import com.gempukku.lotro.communication.GameStateListener; import com.gempukku.lotro.filters.Filters; @@ -48,7 +49,7 @@ public class LotroGameMediator { public LotroGameMediator(String gameId, LotroFormat lotroFormat, LotroGameParticipant[] participants, LotroCardBlueprintLibrary library, GameTimer gameTimer, boolean allowSpectators, boolean cancellable, boolean showInGameHall, - String tournamentName) { + String tournamentName, MarkdownParser parser) { _gameId = gameId; _timeSettings = gameTimer; _allowSpectators = allowSpectators; @@ -59,14 +60,16 @@ public class LotroGameMediator { for (LotroGameParticipant participant : participants) { String participantId = participant.getPlayerId(); - _playerDecks.put(participantId, participant.getDeck()); + var deck = participant.getDeck(); + deck.setNotes(parser.renderMarkdown(deck.getNotes(), true)); + _playerDecks.put(participantId, deck); _playerClocks.put(participantId, 0); _playersPlaying.add(participantId); } _userFeedback = new DefaultUserFeedback(); - _lotroGame = new DefaultLotroGame(lotroFormat, _playerDecks, _userFeedback, library, _timeSettings.toString(), _allowSpectators, - tournamentName); + _lotroGame = new DefaultLotroGame(lotroFormat, _playerDecks, _userFeedback, library, _timeSettings.toString(), + _allowSpectators, tournamentName); _userFeedback.setGame(_lotroGame); } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroServer.java index c0434f9fb..f683d9b55 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroServer.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroServer.java @@ -4,6 +4,7 @@ import com.gempukku.lotro.AbstractServer; import com.gempukku.lotro.PrivateInformationException; import com.gempukku.lotro.chat.ChatCommandErrorException; import com.gempukku.lotro.chat.ChatServer; +import com.gempukku.lotro.chat.MarkdownParser; import com.gempukku.lotro.db.DeckDAO; import com.gempukku.lotro.hall.GameSettings; import com.gempukku.lotro.logic.timing.GameResultListener; @@ -33,14 +34,17 @@ public class LotroServer extends AbstractServer { private final ChatServer _chatServer; private final GameRecorder _gameRecorder; + private final MarkdownParser _markdownParser; private final ReadWriteLock _lock = new ReentrantReadWriteLock(); - public LotroServer(DeckDAO deckDao, LotroCardBlueprintLibrary library, ChatServer chatServer, GameRecorder gameRecorder) { + public LotroServer(DeckDAO deckDao, LotroCardBlueprintLibrary library, ChatServer chatServer, + GameRecorder gameRecorder, MarkdownParser parser) { _deckDao = deckDao; _lotroCardBlueprintLibrary = library; _chatServer = chatServer; _gameRecorder = gameRecorder; + _markdownParser = parser; } protected void cleanup() { @@ -110,9 +114,9 @@ public class LotroServer extends AbstractServer { spectate = false; } - LotroGameMediator lotroGameMediator = new LotroGameMediator(gameId, gameSettings.format(), participants, _lotroCardBlueprintLibrary, - gameSettings.timeSettings(), - spectate, !gameSettings.competitive(), gameSettings.hiddenGame(), tournamentName); + LotroGameMediator lotroGameMediator = new LotroGameMediator(gameId, gameSettings.format(), participants, + _lotroCardBlueprintLibrary, gameSettings.timeSettings(), spectate, !gameSettings.competitive(), + gameSettings.hiddenGame(), tournamentName, _markdownParser); lotroGameMediator.addGameResultListener( new GameResultListener() { @Override