I have a document which contains some metadata in the form of small markdown snippets.
Inside of a layout, I want to grab them, render them from markdown to HTML, and then print out the results. (I’m using DocPad’s default template engine, “Eco”.)
Is this possible?
The following assumes you are using marked as your Markdown engine. If you are using RoboSkirt or something else, you can do something similar, just adapt to use their module instead.
First, make sure you have the marked node module in your top level project:
npm install --save marked
Then add a helper function to your docpad.coffee that makes the marked function available in templates:
docpadConfig = {
templateData:
# Specify some site properties
marked: require('marked')
}
Now you can use this in your .eco files:
<div>
<%- #marked(#document.someMetaProperty) %>
</div>
Another way to Erv's answer is to use the Docpad Api which let's you pass the text through its render workflow (this way you can use other installed template engines too)
var renderOpts = {
text: 'here is some **markdown**',
filename:'markdown',
renderSingleExtensions:true
};
docpadInstance.action('render', renderOpts, function(err,result){
console.log(result);
});
source for above snippet: http://docpad.org/docs/api#rendering-individual-files
Related
I need to get the headline and the text separately out of a text content element. The reason is, to give the editor a simple way to add a content for a complicated section in my html theme.
I am new to TYPO3 an we run V11.5.16! I read and watched some tutorials and I got most of my site already working! Contents are dynamic and multilinguale so far.
To get contents from backend, I use Backend Layouts and copy the content from styles.content.get inside my setup.typoscript. I think this is the common way to do it, and as I said, it works. I output them using {contentXY->f:transform.html()} or {contentXY->f:format.raw()}.
For a text content element, I get something like:
<div id="c270" class="frame frame-default frame-type-text frame-layout-0">
<header>
<h2 class="">Headline</h2>
</header>
<p>Some Text</p>
</div>
Is it possible to get only "Headline"? And if so, it hopefully works also for getting out separately "Some Text"
Something like: {contentXY->f:transform.html(filterBy('h2'))}
Thanks in Advance!
EDIT:
According to Peter Krause's answer: I know, there is an extra content element for headers. But I need the text content element, because for the places in the html, I need header AND text. And the editors are technically not savy enough to fill in different content elements. Please don't ask in more detail. ):
You can handle header and body of an CE seperately, but not in a page context.
In page context you get the result from rendering the CEs, which is a string (with HTML).
For each CE there is a rendering information, which nowadays is also FLUID.
Depending on your installation it probably is FSC (ext:fluid_styled_content) or a Bootstrap extension.
This means: there are FLUID templates which can be overriden and modified.
In these templates you have access to each field of a CE separately.
Look for the templates stored in the defined paths (in TSOB) and add your own path for overides:
lib.contentElement {
templateRootPaths {
1 = ...
2 = ...
3 = ...your path...
}
partialRootPaths {
1 = ...
2 = ...
3 = ...your path...
}
layoutRootPaths {
1 = ...
2 = ...
3 = ...your path...
}
}
Thanks for all hints! I think, for my requirement, there is no solution out of the box. So i made a custom CE with Mask and edited the template html. For non-technical editors, it is the best solution in terms of data input. I hope this stands for future upgrades...
On the main page of every repository in GitHub or BitBucket it shows the Readme.md in a very pretty format.
Is there a way to make the same thing with ruby? I have already found some gems like Redcarpet, but it never looks pretty. I've followed this instructions for Redcarpet.
Edit:
After I tried Github's markup ruby gem, the same thing is happening.
What is shown is this:
And what I want is this:
And I'm sure it's not only css missing, because after 3 backquotes (```) I write the syntax like json or bash and in the first image it is written.
Edit2:
This code here:
renderer = Redcarpet::Render::HTML.new(prettify: true)
markdown = Redcarpet::Markdown.new(renderer, fenced_code_blocks: true)
html = markdown.render(source_text)
'<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>'+html
Generated this:
Github provides its own ruby gem to do so: https://github.com/github/markup.
You just need to install the right dependencies and you're good to go.
You need to enable a few nonstandard features.
Fenced code blocks
Fenced code blocks are nonstandard and are not enabled by default on most Markdown parsers (some older ones don't support them at all). According to Redcarpet's docs, you want to enable the fenced_code_blocks extension:
:fenced_code_blocks: parse fenced code blocks, PHP-Markdown style. Blocks delimited with 3 or more ~ or backticks will be considered as code, without the need to be indented. An optional language name may be added at the end of the opening fence for the code block.
Syntax Highlighting
Most Markdown parsers to not do syntax highlighting of code blocks. And those that do always do it as an option. Even then, you will still need to provide your own CSS styles to have the code blocks styled properly. As it turns out, Redcarpet does include support for a prettify option to the HTML renderer:
:prettify: add prettyprint classes to <code> tags for google-code-prettify.
You will need to get the Javascript and CSS from the google-code-prettify project to include in your pages.
Solution
In the end you'll need something like this:
renderer = Redcarpet::Render::HTML.new(prettify: true)
markdown = Redcarpet::Markdown.new(renderer, fenced_code_blocks: true)
html = markdown.render(source_text)
As #yoones said Github shares their way to do it but to be more precise they use the gem "commonmarker" for markdown. Though as far as I can tell this thing does not give the full formatted HTML file but only a piece that you insert into <body>. So you can do it like I did:
require "commonmarker"
puts <<~HEREDOC
<!DOCTYPE html>
<html>
<head>
<style>#{File.read "markdown.css"}</style>
</head>
<body class="markdown-body Box-body">
#{CommonMarker.render_html ARGF.read, %i{ DEFAULT UNSAFE }, %i{ table }}
</body>
</html>
HEREDOC
Where did I get the markdown.css? I just stole the CSS files from an arbitrary Github page with README rendered and applied UNCSS to it -- resulted in a 26kb file, you can find it in the same repo I just linked.
Why the table and UNSAFE? I need this to render an index.html for Github Pages because their markdown renderer can't newlines within table cells, etc. so instead of asking it to render my README.md I make the index.html myself.
How can I programmatically add script or stylesheet tag to a page specified in page's YAML front matter (meta)?
Assuming there is src/documents/posts/a.html.eco with following contents:
---
layout: default
scripts: ['a.js']
---
Blog post that requires a special javascript
and layout src/layouts/default.html.eco with following contents:
...
#getBlock('scripts').toHTML()
</body>
...
The final result for posts/a.html should be:
...
<!-- some extra stuff that was added when processing script tag -->
<script scr="/scripts/a.js"></script>
</body>
...
..while other pages shouldn't have a reference to /scripts/a.js
The comment above tag is just to show that there may be some processing envolved before injecting the tag.
I tried many approaches using different events in docpad.coffee file (including approach taken from docpad-plugin-livereload plugin) but every time I was facing the same problem - script tag was applied to all pages instead of being applied to a.html only. Here is one of my tries:
renderDocument: (opts) ->
{extension,templateData,file,content} = opts
if extension == 'html' and scripts = file.get('scripts')
if typeof scripts != 'undefined'
scripts.forEach (scriptName) ->
#docpad.getBlock('scripts').add('<!-- custom script tag here -->')
I've also tried render event, populateCollections (which is not documented however I found it in docpad-plugin-livereload plugin) and even extendTemplateData events and no luck so far.
I know there is a method of doing this right inside a layout:
#getBlock('scripts').add(#document.scripts or [])
..which is totally fine and it really works as expected however it doesn't seem to provide enough freedom for me to manipulate the content before it's injected to a page.. And even if it's possible I won't like the idea of having some heavy logic inside layout template, I want it to be in a plugin/docpad.coffee
Hopefully that makes sense
Try templateData.getBlock('scripts').add instead of docpad.getBlock('scripts').add
Note: This is a very strange and unique use case so I apologise in advance if it seems a bit ass-backwards.
I have a haml file content.haml and a coffeescript file main.coffee.
I wish to somehow get the html resulting from rendering content.haml into a variable in the coffeescript/resulting javascript.
The end result should be a javascript file rendered to the browser.
let's say they look like this:
# content.haml
.container
.some_content
blah blah blah
-
# main.coffee
html_content = ???
do_something_with_html_content(html_content)
I know, this sounds ridiculous, 'use templates', 'fetch the HTML via ajax' etc. In this instance however, it's not possible, everything needs to be served via one JS file and I cannot fetch other resources from the server. Weird, I know.
Short of manually reconstructing the haml in the coffeescript file by joining an array of strings like this:
html_content = [
'<div class"container">',
'<div class"some_content">',
'blah blah blah',
'</div>',
'</div>',
]
I'm not sure the best way of doing this.
Another way I though of was to put something like this in the coffee file:
html_content = '###CONTENT###'
Then render the haml to html in ruby, render the coffeescript to js and then replace ###CONTENT### with the rendered html before serving to the client. However the html is a multi-line string so it completely destroys the javascript.
I'm convinced there must be some other nice way of rendering the haml into html in a variable such that it forms valid javascript, but my brain has gone blank.
Perhaps you can try something like this in one of your views:
:javascript
html_content = <%= escape_javascript(render partial: "content")%>
## your own logic follows here....
Wouldn't it be better to use a custom html data attribute and then fetch the content of it in js?
<div data-mycontent="YOUR CONTENT GOES HERE"></div>
And then in coffee, use the dataset attribute / data via jquery, if it is available.
If you set a var via writing the file directly it will render your js file uncacheable, among other drawbacks.
You can do that by using the sprockets gem, like Rails does. You just need to rename your CoffeeScript file to main.coffee.erb and use it as you would e.g. a haml template. Pass in your rendered html with an instance variable:
html_content = '<%= #html_content %>'
Edit: Added missing quotes.
I hava an editor template for, let's say, date:
#model DateTime
#section AdditionalJavaScript2
{
/* some js code */
}
#Html.TextBox("", Model.ToString("d.M.yyyy"), new { #class = "date" })
Now, I would like to put some js code into the HEAD section, but this doesn't work.
Of course, I have a this section in my layout.cshtml:
<head>
...
#RenderSection("AdditionalJavaScript2", required: false)
</head>
It works from the plain view, but not from partial view (editor template).
Why?
And, is there a workaround?
Thanks,
Igor
A partial-view does not use a template, it returns "raw" html to be included in your page (by Javascript). It does not have access to anything but the stream it returns itself.
Think of it like this: You typically call a partial view from Javascript/AJAX to get some new html. You get the return, and replace some DIV-tag. How can the system (FireFox, Chrome, ...) know, that there is some extra section of data that needs to replace something in the HEAD tag.
There are some workarounds:
Don't put the script in the HEAD
Add a parameter switch betweed the html and the script. You need to client-side calls, one to get the html, and one for the script. You include the calls to the partial-view on two locations on your page.
Separate the script and the html using some pre-defined tag like <!-- SEPERATOR -->, and let the calling code split the result, and put it in the correct position.