-
Notifications
You must be signed in to change notification settings - Fork 332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Markdown support in listings templates #11760
Changes from 11 commits
f9a5250
4b4c36b
0f779cf
911207e
8463bc7
0af2dd7
5d1ef85
0aafe42
43ff451
f03ee1e
d52e2ba
2b73ea2
61094dd
9c43d63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
<div class="listing-no-matching d-none"><%- listing.utilities.localizedString("listing-page-no-matches")%></div> | ||
<% if (listing["page-size"] < items.length) { %> | ||
```{=html} | ||
<div class="listing-no-matching d-none"><%= listing.utilities.localizedString("listing-page-no-matches") %></div> | ||
<% if (listing["page-size"] < items.length) { %> | ||
mcanouil marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<nav id="<%- listing.id %>-pagination" class="listing-pagination" aria-label="Page Navigation"> | ||
<ul class="pagination"></ul> | ||
</nav> | ||
``` | ||
<% } %> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,83 +37,147 @@ return !["title", "image", "image-alt", "date", "author", "subtitle", "descripti | |
}); | ||
%> | ||
|
||
<div class="g-col-1" <%= listing.utilities.metadataAttrs(item) %>> | ||
::: {.g-col-1 <%= listing.utilities.metadataAttrs(item) %> } | ||
mcanouil marked this conversation as resolved.
Show resolved
Hide resolved
cscheid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```{=html} | ||
<a href="<%- item.path %>" class="quarto-grid-link"> | ||
<div class="quarto-grid-item card h-100 <%-`card-${align}`%><%= hideBorders ? ' borderless' : '' %>"> | ||
<div class="quarto-grid-item card h-100 <%= `card-${align}` %><%= hideBorders ? ' borderless' : '' %>"> | ||
``` | ||
|
||
<% if (fields.includes('image')) { %> | ||
|
||
<% if (item.image) { %> | ||
|
||
```{=html} | ||
<p class="card-img-top"> | ||
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image card-img", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']) %> | ||
</p> | ||
``` | ||
|
||
<% } else { %> | ||
|
||
```{=html} | ||
<%= listing.utilities.imgPlaceholder(listing.id, itemNumber, item.outputHref) %> | ||
<% } %> | ||
``` | ||
|
||
<% } %> | ||
<% } %> | ||
|
||
<% if (showField('title') || showField('subtitle') || showField('description') || showField('author') || showField('date') || otherFields.length > 0) { %> | ||
|
||
<div class="card-body post-contents"> | ||
<% if (showField('title')) { %><h5 class="no-anchor card-title listing-title"><%= item.title %></h5><% } %> | ||
<% if (showField('subtitle')) { %><div class="card-subtitle listing-subtitle"><%= item.subtitle %></div><% } %> | ||
<% if (showField('reading-time')) { %><div class="listing-reading-time card-text text-muted"><%= item['reading-time'] %></div> <% } %> | ||
::: {.card-body .post-contents} | ||
|
||
<% if (showField('title')) { %> | ||
<h5 class="no-anchor card-title listing-title"><%= item.title %></h5> | ||
cderv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<% } %> | ||
|
||
<% if (showField('subtitle')) { %> | ||
<div class="card-subtitle listing-subtitle"><%= item.subtitle %></div> | ||
cderv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<% } %> | ||
|
||
<% if (showField('reading-time')) { %> | ||
|
||
```{=html} | ||
<div class="listing-reading-time card-text text-muted"><%= item['reading-time'] %></div> | ||
``` | ||
|
||
<% } %> | ||
|
||
<% if (fields.includes('categories') && item.categories) { %> | ||
|
||
```{=html} | ||
<div class="listing-categories"> | ||
<% for (const category of item.categories) { %> | ||
<div class="listing-category" onclick="window.quartoListingCategory('<%=utils.b64encode(category)%>'); return false;"><%= category %></div> | ||
<% } %> | ||
<% for (const category of item.categories) { %> | ||
<div class="listing-category" onclick="window.quartoListingCategory('<%= utils.b64encode(category ) %>'); return false;"><%= category %></div> | ||
<% } %> | ||
</div> | ||
``` | ||
|
||
<% } %> | ||
|
||
<% if (showField('description')) { %> | ||
|
||
<div class="card-text listing-description delink"><%= item.description %></div> | ||
```{=html} | ||
<div class="card-text listing-description delink"> | ||
``` | ||
|
||
<%= item.description %> | ||
|
||
```{=html} | ||
</div> | ||
``` | ||
|
||
<% } %> | ||
<% | ||
|
||
<% | ||
const flexJustify = showField('author') && showField('date') ? "justify" : showField('author') ? "start" : "end"; | ||
%> | ||
|
||
<% if (showField('author') || showField('date')) { %> | ||
<div class="card-attribution card-text-small <%-flexJustify%>"> | ||
<% if (showField('author')) { %><div class="listing-author"><%= item.author %></div><% } %> | ||
<% if (showField('date')) { %><div class="listing-date"><%= item.date %></div><% } %> | ||
|
||
```{=html} | ||
<div class="card-attribution card-text-small <%- flexJustify %>"> | ||
``` | ||
|
||
<% if (showField('author')) { %> | ||
<div class="listing-author"><%= item.author %></div> | ||
cderv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<% } %> | ||
|
||
<% if (showField('date')) { %> | ||
<div class="listing-date"><%= item.date %></div> | ||
cderv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<% } %> | ||
|
||
```{=html} | ||
</div> | ||
``` | ||
|
||
<% } %> | ||
|
||
<% if (otherFields.length > 0) { %> | ||
|
||
```{=html} | ||
<table class="card-other-values"> | ||
<% for (const field of otherFields) { | ||
let value = readField(item, field); | ||
<% for (const field of otherFields) { | ||
let value = readField(item, field); | ||
%> | ||
<tr> | ||
<td><%= listing.utilities.fieldName(field) %></td> | ||
<td class="<%-field%>"><%= listing.utilities.outputLink(item, field, value) %></td> | ||
<td class="<%- field %>"><%= listing.utilities.outputLink(item, field, value) %></td> | ||
</tr> | ||
<% } %> | ||
</table> | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No markdown supports in metadata with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency until there is a decision, I switched to using raw blocks except for the lines with the variables to allow markdown. This means, there is still inline HTML. |
||
|
||
<% } %> | ||
|
||
</div> | ||
::: | ||
<% } %> | ||
|
||
<% if (fields.includes('filename') || fields.includes('file-modified')) { %> | ||
|
||
<div class="card-footer"> | ||
::: {.card-footer} | ||
|
||
<% if (fields.includes('filename')) { %> | ||
<div class="card-filename listing-filename"> | ||
<%= item.filename ? item.filename : " " %> | ||
</div> | ||
|
||
```{=html} | ||
<div class="card-filename listing-filename"><%= item.filename ? item.filename : " " %></div> | ||
``` | ||
|
||
<% } %> | ||
|
||
<% if (fields.includes('file-modified')) { %> | ||
<div class="card-file-modified listing-file-modified"> | ||
<%= item['file-modified'] ? item['file-modified'] : " "%> | ||
</div> | ||
|
||
```{=html} | ||
<div class="card-file-modified listing-file-modified"><%= item['file-modified'] ? item['file-modified'] : " " %></div> | ||
``` | ||
|
||
<% } %> | ||
</div> | ||
|
||
::: | ||
<% } %> | ||
</div></a></div> | ||
|
||
```{=html} | ||
</div></a> | ||
``` | ||
|
||
::: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
:::{.list .quarto-listing-default} | ||
``````{=html} | ||
::: {.list .quarto-listing-default} | ||
|
||
<% for (const item of items) { %> | ||
<% partial('item-default.ejs.md', {listing, item, utils }) %> | ||
<% } %> | ||
`````` | ||
|
||
::: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
localizedString()
usage here will insert a user provided value. I think it is safer to keep using<%-
here, especially when used in HTML attribute, as we can't know which character would be used.Maybe there won't be any
<
used forlisting-page-order-by
but if so, then I think it could break HTML.That is my thinking. If there is a specific reason, I am missing it, so I would really like to understand and learn something new. Why replacing ?
My first though is that using
%<-
for escaping is safer.Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value is used inside text attribute or inside div which means the content is directly displayed to the users.
Escaping what's supposed to be a string would lead to weird
placeholder
,aria-label
, andoption
text display.Why would the text need escaping? I might have missed something but to me, there are no reasons to escape those values.
Both "work" and both can lead to unexpected outputs when non ASCII string provided.
I don't think there is a perfect solution. The rational being the use of
=
was the same as the overall PR, i.e., allow Markdown/HTML (of course no markdown evaluation is possible anyway when inside raw block).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding of this
<%-
vs<%=
in template is thatMaybe this won't ever happen but I was thinking it is safer to always HTML escape
<%-
when we know the content provided shouldn't be interpreted as HTML structure.Example in lodash doc #11787 is quite explicite about escaping
If you want to write
<script>
as such in strong case, then you need the template to HTML escape the value.So for language customizable value, I am thinking it is better to protect if a user wants to use something like quotes (
"
) in the value.Maybe I am wrong about this but I think my thinking sum up
<%-
would be safer as this is why it was used before. Maybe it is not useful but it does not harm to use it<%=
, especially if it could potentially be opened to regression.🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As all the occurrences are in an HTML raw block, I think we can go (back) with
<%-
as no markdown will ever work there.Should I do the PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep on the purpose of learning, as this is still unclear, can you explain to me
Or rather you'll confirm if I understand correctly
I think this is what I was missing, right ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably update the documentation about all this story:
https://quarto.org/docs/websites/website-listings-custom.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead and made a PR:
<%-
) forlisting.utilities.localizedString()
#11817There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also opened a documentation issue: