Skip to content

Commit

Permalink
Extended ShortCodes (#304)
Browse files Browse the repository at this point in the history
* add jexl for expressions

* remove error code

* current

* first jexl and antlr experiements

* update antlr grammar

* update antlr grammar

* update antlr grammar

* update antlr grammar

* new short code tag parser with expression support

* fix feature test project

* remove unused code
example for an expression

* add render model to shortcode processing

* add more tests

* update markdown renderer to use new TagParser

* fix cachehandler

---------
  • Loading branch information
thmarx authored Oct 11, 2024
1 parent 3117946 commit 611ebc3
Show file tree
Hide file tree
Showing 50 changed files with 2,628 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ private List<ConfigurationResource> getConfigurations () {


public void update(CronJobContext jobContext) {
System.out.println("update");
log.trace("check for modified configurations {}", db.getFileSystem().resolve(".").toString());
getConfigurations().forEach(config -> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class AuthShortCodes extends RegisterShortCodesExtensionPoint {
@Override
public Map<String, Function<Parameter, String>> shortCodes() {
return Map.of(
"username", this::getUserName
"username", this::getUserName,
"cms:username", this::getUserName
);
}

Expand Down
4 changes: 4 additions & 0 deletions cms-content/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,7 @@ public String renderView(final ReadOnlyFile viewFile, final View view, final Con
});
}

private <F extends Feature> Object getFeatureValueOrDefault(RequestContext context,
Class<F> feature, Function<F, Object> valueFunction, Object defaultValue) {
if (context.has(feature)) {
return valueFunction.apply(context.get(feature));
}
return defaultValue;
}

private String renderMarkdown(final String rawContent, final RequestContext context, final TemplateEngine.Model model) {
private String renderContent(final String rawContent, final RequestContext context, final TemplateEngine.Model model) {
var pipeline = ContentPipelineFactory.create(context, model);

return pipeline.process(rawContent);
Expand Down Expand Up @@ -190,7 +182,7 @@ public String render(final ReadOnlyFile contentFile, final RequestContext contex
extendModel(model);

model.values.put("content",
renderMarkdown(rawContent, context, model)
renderContent(rawContent, context, model)
);

return templates.get().render((String) meta.get("template"), model);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,70 +21,90 @@
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/


import com.condation.cms.content.markdown.Block;
import com.condation.cms.content.markdown.BlockElementRule;
import com.condation.cms.content.markdown.InlineRenderer;
import com.condation.cms.content.shortcodes.ShortCodeParser;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.condation.cms.content.shortcodes.TagMap;
import com.condation.cms.content.shortcodes.TagParser;
import java.util.List;

/**
*
* @author t.marx
*/
public class ShortCodeBlockRule implements BlockElementRule {

public static final Pattern TAG_PARAMS_PATTERN_SHORT = Pattern.compile("^(\\[{2})(?<tag>[a-z_A-Z0-9]+)( (?<params>.*?))?\\p{Blank}*/\\]{2}",
Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES);
public static final Pattern TAG_PARAMS_PATTERN_LONG = Pattern.compile("^(\\[{2})(?<tag>[a-z_A-Z0-9]+)( (?<params>.*?))?\\]{2}(?<content>.*)\\[{2}/\\k<tag>\\]{2}",
Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES);

public static final Pattern SHORTCODE_PATTERN = Pattern.compile("^" + ShortCodeParser.SHORTCODE_REGEX,
Pattern.DOTALL | Pattern.MULTILINE);

private static final TagParser tagParser = new TagParser(null);

@Override
public Block next(final String md) {
/*
Matcher matcher = TAG_PARAMS_PATTERN_SHORT.matcher(md);
if (matcher.find()) {
return new ShortCodeBlock(matcher.start(), matcher.end(),
matcher.group("tag"), matcher.group("params"), ""
);
}
matcher = TAG_PARAMS_PATTERN_LONG.matcher(md);
if (matcher.find()) {
return new ShortCodeBlock(matcher.start(), matcher.end(),
matcher.group("tag"), matcher.group("params"), matcher.group("content")
);
}
*/
Matcher matcher = SHORTCODE_PATTERN.matcher(md);
if (matcher.matches()) {
String name = matcher.group(1) != null ? matcher.group(1) : matcher.group(4);
String params = matcher.group(2) != null ? matcher.group(2).trim() : matcher.group(5).trim();
String content = matcher.group(3) != null ? matcher.group(3).trim() : "";

ShortCodeParser.Match match = new ShortCodeParser.Match(name, matcher.start(), matcher.end());
match.setContent(content);
match.getParameters().put("content", content);

return new ShortCodeBlock(matcher.start(), matcher.end(), name, params, content);
List<TagParser.TagInfo> tags = tagParser.findTags(md, new TagMap() {
@Override
public boolean has(String codeName) {
return true;
}
}).stream()
.filter(tag -> isStandaloneInLine(md, tag))
.toList();
if (tags.isEmpty()) {
return null;
}

return null;
var tag = tags.getFirst();
return new ShortCodeBlock(
tag.startIndex(),
tag.endIndex(),
tag);

}


public static record ShortCodeBlock (int start, int end, String tag, String params, String content) implements Block {
public static record ShortCodeBlock(int start, int end, TagParser.TagInfo tagInfo) implements Block {

@Override
public String render(InlineRenderer inlineRenderer) {
return "[[%s %s]]%s[[/%s]]".formatted(tag, params, content, tag);
List<String> params = tagInfo.rawAttributes()
.entrySet().stream()
.filter(entry -> !entry.getKey().equals("_content"))
.sorted((entry1, entry2) -> entry1.getKey().compareTo(entry2.getKey()))
.map(entry -> {
return "%s=%s".formatted(entry.getKey(), parseValue((String) entry.getValue()));
}).toList();
return "[[%s %s]]%s[[/%s]]"
.formatted(
tagInfo.name(),
String.join(" ", params),
tagInfo.rawAttributes().getOrDefault("_content", ""),
tagInfo.name()
);
}
}

private static Object parseValue(String value) {
if (value.matches("\\d+")) {
return value;
} else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
return value;
}
return "\"" + value + "\"";
}

public boolean isStandaloneInLine(String text, TagParser.TagInfo tag) {
var startIndex = tag.startIndex();
var endIndex = tag.endIndex();
// Prüfe, ob die Indizes gültig sind
if (startIndex < 0 || endIndex > text.length() || startIndex >= endIndex) {
throw new IllegalArgumentException("Ungültige Indizes");
}



// Finde die Position des Textausschnitts
String before = text.substring(0, startIndex);
String after = text.substring(endIndex);

// Prüfe, ob vor und nach dem Ausschnitt ein Zeilenumbruch oder nichts steht
boolean beforeIsLineBreak = before.isEmpty() || before.endsWith("\n") || before.endsWith("\r\n");
boolean afterIsLineBreak = after.isEmpty() || after.startsWith("\n") || after.startsWith("\r\n");

return beforeIsLineBreak && afterIsLineBreak;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,80 +21,67 @@
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/


import com.condation.cms.content.markdown.InlineBlock;
import com.condation.cms.content.markdown.InlineElementRule;
import com.condation.cms.content.shortcodes.ShortCodeParser;
import static com.condation.cms.content.shortcodes.ShortCodeParser.PARAM_PATTERN;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.condation.cms.content.shortcodes.TagMap;
import com.condation.cms.content.shortcodes.TagParser;
import java.util.List;

/**
*
* @author t.marx
*/
public class ShortCodeInlineBlockRule implements InlineElementRule {

public static final Pattern TAG_PARAMS_PATTERN_SHORT = Pattern.compile("(\\[{2})(?<tag>[a-z_A-Z0-9]+)( (?<params>.*?))?\\p{Blank}*/\\]{2}",
Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES);
public static final Pattern TAG_PARAMS_PATTERN_LONG = Pattern.compile("^(\\[{2})(?<tag>[a-z_A-Z0-9]+)( (?<params>.*?))?\\]{2}(?<content>.*)\\[{2}/\\k<tag>\\]{2}",
Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES);
private static final TagParser tagParser = new TagParser(null);

@Override
public InlineBlock next(final String md) {
/*
Matcher matcher = TAG_PARAMS_PATTERN_SHORT.matcher(md);
if (matcher.find()) {
return new ShortCodeInlineBlock(matcher.start(), matcher.end(),
matcher.group("tag"), matcher.group("params"), ""
);
}
matcher = TAG_PARAMS_PATTERN_LONG.matcher(md);
if (matcher.find()) {
return new ShortCodeInlineBlock(matcher.start(), matcher.end(),
matcher.group("tag"), matcher.group("params"), matcher.group("content")
);
}
*/

Matcher matcher = ShortCodeParser.SHORTCODE_PATTERN.matcher(md);
if (matcher.find()) {
String name = matcher.group(1) != null ? matcher.group(1) : matcher.group(4);
String params = matcher.group(2) != null ? matcher.group(2).trim() : matcher.group(5).trim();
String content = matcher.group(3) != null ? matcher.group(3).trim() : "";

ShortCodeParser.Match match = new ShortCodeParser.Match(name, matcher.start(), matcher.end());
match.setContent(content);
match.getParameters().put("content", content);

/*
Matcher paramMatcher = PARAM_PATTERN.matcher(params);

while (paramMatcher.find()) {
String key = paramMatcher.group(1);
String value = paramMatcher.group(2);
// Remove the surrounding quotes
value = value.substring(1, value.length() - 1);
match.getParameters().put(key, value);
List<TagParser.TagInfo> tags = tagParser.findTags(md, new TagMap() {
@Override
public boolean has(String codeName) {
return true;
}
*/
return new ShortCodeInlineBlock(matcher.start(), matcher.end(), name, params, content);
}).stream().toList();
if (tags.isEmpty()) {
return null;
}

return null;
var tag = tags.getFirst();
return new ShortCodeInlineBlock(
tag.startIndex(),
tag.endIndex(),
tag);
}


public static record ShortCodeInlineBlock (int start, int end, String tag, String params, String content) implements InlineBlock {
public static record ShortCodeInlineBlock(int start, int end, TagParser.TagInfo tagInfo) implements InlineBlock {

@Override
public String render() {
return "[[%s %s]]%s[[/%s]]".formatted(tag, params, content, tag);
List<String> params = tagInfo.rawAttributes()
.entrySet().stream()
.filter(entry -> !entry.getKey().equals("_content"))
.sorted((entry1, entry2) -> entry1.getKey().compareTo(entry2.getKey()))
.map(entry -> {
return "%s=%s".formatted(entry.getKey(), parseValue((String) entry.getValue()));
}).toList();
return "[[%s %s]]%s[[/%s]]"
.formatted(
tagInfo.name(),
String.join(" ", params),
tagInfo.rawAttributes().getOrDefault("_content", ""),
tagInfo.name()
);
}




}

private static Object parseValue(String value) {
if (value.matches("\\d+")) {
return value;
} else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
return value;
}
return "\"" + value + "\"";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,14 @@
* @author thmar
*/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@RequiredArgsConstructor
public class ContentPipeline {

private final HookSystem hookSystem;
private final RequestContext requestContext;

private final TemplateEngine.Model model;

void init() {
protected void init() {

List<String> pipeline = requestContext.get(ConfigurationFeature.class)
.configuration().get(SiteConfiguration.class)
Expand All @@ -78,7 +77,7 @@ private String processMarkdown(FilterContext<String> context) {
}

private String processShortCodes(FilterContext<String> context) {
return requestContext.get(RenderContext.class).shortCodes().replace(context.value());
return requestContext.get(RenderContext.class).shortCodes().replace(context.value(), model.values);
}

private String processTemplate(FilterContext<String> context) {
Expand All @@ -90,12 +89,4 @@ private String processTemplate(FilterContext<String> context) {
return context.value();
}
}

private <F extends Feature> Object getFeatureValueOrDefault(RequestContext context,
Class<F> feature, Function<F, Object> valueFunction, Object defaultValue) {
if (context.has(feature)) {
return valueFunction.apply(context.get(feature));
}
return defaultValue;
}
}
Loading

0 comments on commit 611ebc3

Please sign in to comment.