Added markdown parser param to disable link shredding for announcements. Added markdown support to deck notes

This commit is contained in:
Christian 'ketura' McCarty
2024-11-30 21:22:40 -06:00
parent 0e0dc5c148
commit 8b4bf99e54
6 changed files with 51 additions and 21 deletions

View File

@@ -66,7 +66,7 @@ public class ChatRequestHandler extends LotroServerRequestHandler implements Uri
final boolean admin = resourceOwner.hasType(Player.Type.ADMIN); final boolean admin = resourceOwner.hasType(Player.Type.ADMIN);
final boolean leagueAdmin = resourceOwner.hasType(Player.Type.LEAGUE_ADMIN); final boolean leagueAdmin = resourceOwner.hasType(Player.Type.LEAGUE_ADMIN);
if (message != null && !message.trim().isEmpty()) { if (message != null && !message.trim().isEmpty()) {
String newMsg = _markdownParser.renderMarkdown(message); String newMsg = _markdownParser.renderMarkdown(message, true);
chatRoom.sendMessage(resourceOwner.getName(), newMsg, admin); chatRoom.sendMessage(resourceOwner.getName(), newMsg, admin);
responseWriter.writeXmlResponse(null); responseWriter.writeXmlResponse(null);
} else { } else {

View File

@@ -2,6 +2,7 @@ package com.gempukku.lotro.async.handler;
import com.gempukku.lotro.async.HttpProcessingException; import com.gempukku.lotro.async.HttpProcessingException;
import com.gempukku.lotro.async.ResponseWriter; import com.gempukku.lotro.async.ResponseWriter;
import com.gempukku.lotro.chat.MarkdownParser;
import com.gempukku.lotro.common.JSONDefs; import com.gempukku.lotro.common.JSONDefs;
import com.gempukku.lotro.common.Side; import com.gempukku.lotro.common.Side;
import com.gempukku.lotro.db.DeckDAO; import com.gempukku.lotro.db.DeckDAO;
@@ -39,6 +40,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri
private final LotroFormatLibrary _formatLibrary; private final LotroFormatLibrary _formatLibrary;
private final SoloDraftDefinitions _draftLibrary; private final SoloDraftDefinitions _draftLibrary;
private final LotroServer _lotroServer; private final LotroServer _lotroServer;
private final MarkdownParser _markdownParser;
private static final Logger _log = LogManager.getLogger(DeckRequestHandler.class); 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); _formatLibrary = extractObject(context, LotroFormatLibrary.class);
_lotroServer = extractObject(context, LotroServer.class); _lotroServer = extractObject(context, LotroServer.class);
_draftLibrary = extractObject(context, SoloDraftDefinitions.class); _draftLibrary = extractObject(context, SoloDraftDefinitions.class);
_markdownParser = extractObject(context, MarkdownParser.class);
} }
@Override @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)) for (CardCollection.Item item : _sortAndFilterCards.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), _library, _formatLibrary))
result.append(item.getCount() + "x " + generateCardTooltip(item) + "<br/>"); result.append(item.getCount() + "x " + generateCardTooltip(item) + "<br/>");
result.append("<h3>Notes</h3><br>" + deck.getNotes().replace("\n", "<br/>")); result.append("<h3>Notes</h3><br>" + _markdownParser.renderMarkdown(deck.getNotes(), true));
result.append("</body></html>"); result.append("</body></html>");

View File

@@ -114,7 +114,8 @@ public class ServerBuilder {
extract(objectMap, DeckDAO.class), extract(objectMap, DeckDAO.class),
extract(objectMap, LotroCardBlueprintLibrary.class), extract(objectMap, LotroCardBlueprintLibrary.class),
extract(objectMap, ChatServer.class), extract(objectMap, ChatServer.class),
extract(objectMap, GameRecorder.class))); extract(objectMap, GameRecorder.class),
extract(objectMap, MarkdownParser.class)));
objectMap.put(HallServer.class, objectMap.put(HallServer.class,
new HallServer( new HallServer(

View File

@@ -20,24 +20,33 @@ public class MarkdownParser {
private final Parser _markdownParser; private final Parser _markdownParser;
private final HtmlRenderer _markdownRenderer; private final HtmlRenderer _markdownRenderer;
private final HtmlRenderer _noHiddenLinksRenderer;
private static Pattern QuoteExtender = Pattern.compile("^([ \t]*>[ \t]*.+)(?=\n[ \t]*[^>])", Pattern.MULTILINE); private static Pattern QuoteExtender = Pattern.compile("^([ \t]*>[ \t]*.+)(?=\n[ \t]*[^>])", Pattern.MULTILINE);
public MarkdownParser() { public MarkdownParser() {
List<Extension> adminExt = Arrays.asList(StrikethroughExtension.create(), AutolinkExtension.create()); List<Extension> extensions = Arrays.asList(StrikethroughExtension.create(), AutolinkExtension.create());
_markdownParser = Parser.builder() _markdownParser = Parser.builder()
.extensions(adminExt) .extensions(extensions)
.build(); .build();
_markdownRenderer = HtmlRenderer.builder() _markdownRenderer = HtmlRenderer.builder()
.nodeRendererFactory(htmlContext -> new LinkShredder(htmlContext)) .nodeRendererFactory(htmlContext -> new LinkShredder(htmlContext))
.extensions(adminExt) .extensions(extensions)
.escapeHtml(true)
.sanitizeUrls(true)
.softbreak("<br />")
.build();
_noHiddenLinksRenderer = HtmlRenderer.builder()
.extensions(extensions)
.escapeHtml(true) .escapeHtml(true)
.sanitizeUrls(true) .sanitizeUrls(true)
.softbreak("<br />") .softbreak("<br />")
.build(); .build();
} }
public String renderMarkdown(String markdown) { public String renderMarkdown(String markdown, boolean shredLinks) {
String newMsg = markdown.trim().replaceAll("\n\n\n+", "\n\n\n"); String newMsg = markdown.trim().replaceAll("\n\n\n+", "\n\n\n");
newMsg = QuoteExtender.matcher(newMsg).replaceAll("$1\n"); newMsg = QuoteExtender.matcher(newMsg).replaceAll("$1\n");
//Escaping underscores so that URLs with lots of underscores (i.e. wiki links) aren't mangled //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("_", "\\_"); newMsg = newMsg.replace("_", "\\_");
//Need to preserve any commands being made //Need to preserve any commands being made
if(!newMsg.startsWith("/")) { if(newMsg.startsWith("/"))
newMsg = _markdownRenderer.render(_markdownParser.parse(newMsg)); return newMsg;
// Prevent quotes with newlines from displaying side-by-side
newMsg = newMsg.replaceAll("</blockquote>[\n \t]*<blockquote>", "</blockquote><br /><blockquote>");
//Make all links open in a new tab if(shredLinks) {
newMsg = newMsg.replaceAll("<(a href=\".*?\")>", "<$1 target=\"_blank\">"); 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("</blockquote>[\n \t]*<blockquote>", "</blockquote><br /><blockquote>");
//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", "<br/>");
return newMsg; return newMsg;
} }

View File

@@ -3,6 +3,7 @@ package com.gempukku.lotro.game;
import com.gempukku.lotro.PrivateInformationException; import com.gempukku.lotro.PrivateInformationException;
import com.gempukku.lotro.SubscriptionConflictException; import com.gempukku.lotro.SubscriptionConflictException;
import com.gempukku.lotro.SubscriptionExpiredException; import com.gempukku.lotro.SubscriptionExpiredException;
import com.gempukku.lotro.chat.MarkdownParser;
import com.gempukku.lotro.common.*; import com.gempukku.lotro.common.*;
import com.gempukku.lotro.communication.GameStateListener; import com.gempukku.lotro.communication.GameStateListener;
import com.gempukku.lotro.filters.Filters; import com.gempukku.lotro.filters.Filters;
@@ -48,7 +49,7 @@ public class LotroGameMediator {
public LotroGameMediator(String gameId, LotroFormat lotroFormat, LotroGameParticipant[] participants, LotroCardBlueprintLibrary library, public LotroGameMediator(String gameId, LotroFormat lotroFormat, LotroGameParticipant[] participants, LotroCardBlueprintLibrary library,
GameTimer gameTimer, boolean allowSpectators, boolean cancellable, boolean showInGameHall, GameTimer gameTimer, boolean allowSpectators, boolean cancellable, boolean showInGameHall,
String tournamentName) { String tournamentName, MarkdownParser parser) {
_gameId = gameId; _gameId = gameId;
_timeSettings = gameTimer; _timeSettings = gameTimer;
_allowSpectators = allowSpectators; _allowSpectators = allowSpectators;
@@ -59,14 +60,16 @@ public class LotroGameMediator {
for (LotroGameParticipant participant : participants) { for (LotroGameParticipant participant : participants) {
String participantId = participant.getPlayerId(); 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); _playerClocks.put(participantId, 0);
_playersPlaying.add(participantId); _playersPlaying.add(participantId);
} }
_userFeedback = new DefaultUserFeedback(); _userFeedback = new DefaultUserFeedback();
_lotroGame = new DefaultLotroGame(lotroFormat, _playerDecks, _userFeedback, library, _timeSettings.toString(), _allowSpectators, _lotroGame = new DefaultLotroGame(lotroFormat, _playerDecks, _userFeedback, library, _timeSettings.toString(),
tournamentName); _allowSpectators, tournamentName);
_userFeedback.setGame(_lotroGame); _userFeedback.setGame(_lotroGame);
} }

View File

@@ -4,6 +4,7 @@ import com.gempukku.lotro.AbstractServer;
import com.gempukku.lotro.PrivateInformationException; import com.gempukku.lotro.PrivateInformationException;
import com.gempukku.lotro.chat.ChatCommandErrorException; import com.gempukku.lotro.chat.ChatCommandErrorException;
import com.gempukku.lotro.chat.ChatServer; import com.gempukku.lotro.chat.ChatServer;
import com.gempukku.lotro.chat.MarkdownParser;
import com.gempukku.lotro.db.DeckDAO; import com.gempukku.lotro.db.DeckDAO;
import com.gempukku.lotro.hall.GameSettings; import com.gempukku.lotro.hall.GameSettings;
import com.gempukku.lotro.logic.timing.GameResultListener; import com.gempukku.lotro.logic.timing.GameResultListener;
@@ -33,14 +34,17 @@ public class LotroServer extends AbstractServer {
private final ChatServer _chatServer; private final ChatServer _chatServer;
private final GameRecorder _gameRecorder; private final GameRecorder _gameRecorder;
private final MarkdownParser _markdownParser;
private final ReadWriteLock _lock = new ReentrantReadWriteLock(); 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; _deckDao = deckDao;
_lotroCardBlueprintLibrary = library; _lotroCardBlueprintLibrary = library;
_chatServer = chatServer; _chatServer = chatServer;
_gameRecorder = gameRecorder; _gameRecorder = gameRecorder;
_markdownParser = parser;
} }
protected void cleanup() { protected void cleanup() {
@@ -110,9 +114,9 @@ public class LotroServer extends AbstractServer {
spectate = false; spectate = false;
} }
LotroGameMediator lotroGameMediator = new LotroGameMediator(gameId, gameSettings.format(), participants, _lotroCardBlueprintLibrary, LotroGameMediator lotroGameMediator = new LotroGameMediator(gameId, gameSettings.format(), participants,
gameSettings.timeSettings(), _lotroCardBlueprintLibrary, gameSettings.timeSettings(), spectate, !gameSettings.competitive(),
spectate, !gameSettings.competitive(), gameSettings.hiddenGame(), tournamentName); gameSettings.hiddenGame(), tournamentName, _markdownParser);
lotroGameMediator.addGameResultListener( lotroGameMediator.addGameResultListener(
new GameResultListener() { new GameResultListener() {
@Override @Override