How can I use 11ty to generate one page per json file in _data subfolders? - eleventy

I'm trying to use 11ty to generate pages for a movie review site. The structure of my _data folder is as follows.
_data
movies
2017
title1.json
title2.json
2018
2019
I'm hoping for output like this.
_site
movies
2017
title-1.html
title-2.html
2018
I would settle for output like this.
_site
movies
title-1.html
title-2.html
But I can't work out how to get anything close! Any ideas? Here's my nunjucks template. TitleWithYear is a property in each .json file.
---
pagination:
data: movies
size: 1
alias: movie
resolve: keys
permalink: "movies/{{ year??? }}/{{ movie.TitleWithYear | slug }}/index.html"
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<h1>{{ movie.TitleWithYear }}</h1>
</body>
</html>
Edit: here's a repo showing the problem.
https://github.com/edmondbramhall/11ty-test1
Without the permalink line in, it does work although not with my desired folder structure in the output.
Edit: with Luke's help here's the pagination property I ended up with.
permalink: "movies/{{ movie.ReleaseYear }}/{{ movie.Id }}-{{ movie.Title | slug }}/index.html"
I also created a global filter for slugify to supply a couple of settings by creating a file .eleventy.js in the root folder of my project, with the following content.
const slugify = require('slugify');
module.exports = function(eleventyConfig) {
eleventyConfig.addFilter("slug", function(value) {
return slugify(value, { strict: true, lower: true });
});
};

You are using data: movies in your pagination, but the way that eleventy will try to deal with your given data folder structure is to make a list of objects per year folder, then each json file as a child JS object, with the filename as the key:
> console.log(data.movies)
[
{
'An-American-Tail_1986_4978': {
...
TitleWithYear: 'An American Tail (1986)',
Tagline: 'Meet Fievel. In his search to find his family, he discovered America.',
...
},
'Barbra-Streisand-One-Voice_1986_31683': {
...
TitleWithYear: 'Barbra Streisand: One Voice (1986)',
Tagline: 'Barbra sings in her backyard for charity!',
...
}
},
{
'Alien-Predators_1985_52318': {
...
TitleWithYear: 'Alien Predators (1985)',
...
}
}
]
To get the data into a format such that eleventy can paginate it properly, you need to munge it into a single large array of objects. The before callback functionality of eleventy is by far the easiest way of doing that (also possible via changing your data file structure, or by creating a custom collection in config).
This does require your frontmatter to be in a js format instead, but allows you to map for the actual movie objects and then flatten the resultant array.
Below is a full working example of the movie.njk file from your example repo.
---js
{
pagination:{
data: "movies",
before: (data) => data.map(year => Object.values(year)).flat(),
size: 1,
alias: "movie",
},
permalink: "movies/{{ movie.Id }}/",
}
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ movie.TitleWithYear }}</h1>
<p> {{ movie.Tagline}}
</body>
</html>

Another workaround for structure:
_data
movies.js
movies
title1.json
title2.json
title3.json
movie.njk
use javascript data files
_data/movies.js:
const fs = require("fs");
const path = require("path");
const moviesFolder = path.resolve(__dirname, "../movies");
const movies = fs
.readdirSync(moviesFolder)
.filter(name => path.extname(name) === ".json")
.map(name => ({
key: path.parse(name).name,
...require(path.join(moviesFolder, name)),
}));
module.exports = movies;
movie.njk:
---
pagination:
data: "movies"
size: 1
alias: "movie"
permalink: "{{ movie.key }}/"
---
<h1>{{ movie.title }}</h1>

Related

f:table incompatible with unique constraint?

I'm working on a very basic Grails application in order to dip my toes into web development.
Unfortunately, I'm stuck at trying to display a collection within my MongoDB database using the <f:table>-field tag.... Because the tag somehow seems to be incompatible with the unique constraint of my domain class? Is there anything I can do?
Here's the error message:
There was an unexpected error (type=Internal Server Error, status=500).
Error processing GroovyPageView: [views/teaser/index.gsp:17] Error executing tag <f:table>: Exception thrown applying constraint [unique] to class [class com.test.site.Teaser] for value [true]: Cannot invoke "grails.gorm.validation.Constraint.isValid()" because "c" is null
My BootStrap contains two object instances for testing purposes; they seem to be working fine, since they appear in my MongoDB. Just to be safe, here is how I stored them:
new Teaser(imageUrl: "", teaserUrl: "", name: "mainTeaser", text: "").save()
new Teaser(imageUrl: "", teaserUrl: "", name: "footerTeaser", text: "").save()
Here's my domain class:
package com.test.site
class Teaser {
String imageUrl
String teaserUrl
String name
String text
static mapWith = "mongo"
static constraints = {
name blank: false
name unique: true
}
Here's the current state of my index.gsp (linked to my controller):
<!doctype html>
<html>
<head>
<meta name="layout" content="main">
<asset:stylesheet href="styles.css"/>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Teaser Details</title>
</head>
<body id="body">
<header class="header">
</header>
<main class="main">
<div class="container">
<section>
<f:table collection="${teaserList}"/>
</section>
</div>
</main>
<footer class="footer">
</footer>
</body>
</html>
Finally, here's my controller:
package com.test.site
class TeaserController {
static scaffold = Teaser
def index() {
def teaserList = Teaser.list()
render view: "index", model: [teaserList : teaserList]
}
I'm grateful for any help :)

Can't Render script in razor template

I have creating my website using composite c1 .net cms with razor master template.
but I can't able to render the script in the masterlayout.Here is my razor files.
1.MaserLayout.cshtml
#inherits CompositeC1WebPage
#{
string websiteTitle = HomePageNode.Title;
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" class="no-js" lang="#Lang">
<head>
<title>#CurrentPageNode.Title</title>
#if (!string.IsNullOrEmpty(CurrentPageNode.Description))
{
<meta name="description" content="#CurrentPageNode.Description" />
}
#PageTemplateFeature("Descriptive HTML head elements")
</head>
<body>
<div class="container-fluid main-container">
#* renders the sub template: *#
#RenderBody()
</div>
</body>
</html>
above is my master template and here is my home template
#inherits RazorPageTemplate
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
#*you can add template specific head elements here*#
</head>
<body class="frontpage">
<div>Test template</div>
<script>
$(document).ready(function () {
$('.test').click(function () {
alert("test");
})
});
</script>
</body>
</html>
But I can't able to run the script in the masterpage.Cn anyone help.
Thanks in advance for help.
If Composit-c1 uses some special razor functionality/flow other than default then please ignore this comment
I think it's not related to composit-c1 razor, you should define layout in home page to use master page. & also any script is not defined in you master pages.
in derived pages you don't create complete DOM structure (html, head & body etc. tags)

How to downcase an entire HTML document parsed with Nokogiri

I need to downcase all text in an HTML document that has been parsed with Nokogiri. Here my code:
agent = Mechanize.new
page = agent.get('http://www.example.com').parser.search('//*[translate(text(),"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz") = *]').to_html
There is not error as such in the code; it executes without an error. If I go in and check a random tag in the document, however, the case is still the same as before. Is there another/better way to downcase all text in a document?
You could use traverse to downcase all text nodes:
require 'open-uri'
require 'nokogiri'
doc = Nokogiri::HTML(open("http://www.example.com/"))
doc.traverse do |node|
node.content = node.content.downcase if node.text?
end
puts doc.to_html
Output:
<!DOCTYPE html>
<html>
<head>
<title>example domain</title>
<meta charset="utf-8">
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
body { ... }
</style>
</head>
<body>
<div>
<h1>example domain</h1>
<p>this domain is established to be used for illustrative examples in documents. you may use this
domain in examples without prior coordination or asking for permission.</p>
<p>more information...</p>
</div>
</body>
</html>

How do I omit script elements from HTML using XPath in Nokogiri in Ruby on Rails?

Say I start with everything inside the body element:
Nokogiri::HTML( doc ).xpath( "/html/body/node()" ).to_html
which contains some <script> and <noscript>. How do I get rid of these?
You might want to change your XPath expression to:
Nokogiri::HTML( doc ).xpath( "/html/body/node()[not(self::script or self::noscript)]" ).to_html
#!/usr/bin/env ruby
require 'nokogiri'
html = <<EOT
<html>
<head>
<script>
<!-- dummy script !>
</script>
</head>
<body>
<script><!-- dummy script !></script>
<noscript>dummy script</noscript>
</body>
</head>
EOT
doc = Nokogiri::HTML(html)
Here's the gist of it:
doc.at('body').search('script,noscript').remove
puts doc.to_xml
>> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
>> <html>
>> <head>
>> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
>> <script>
>> <!-- dummy script !>
>> </script>
>> </head>
>> <body>
>>
>> </body>
>> </html>
For simplicity, I'm using Nokogiri's ability to use CSS accessors, rather than XPath.
doc.at('body').search('script,noscript').remove
looks for the first occurrence of the <body> tag, then looks inside for all <script> and <noscript> tags, removing them.
The gap between the resulting <body> tags are the result of the carriage returns in text nodes that trailed the actual target tags.

Hpricot search all the tags under one specific namespace

For example I have the following code:
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><io:content part="title" /></title>
<link rel="icon" href="/document/7e9f29e2-cdee-4f85-ba25-132fa867aa90/latest" type="image/x-icon" />
<n1:content description="Standard CSS" uuid="d069071c-3534-4945-9fb6-2d7be35a165e" />
<n1:term>Content Development</n1:term>
</head>
This XHTML snippet is not strictly legal because there is no namespace declared before so I cannot use Nokogiri which has better namespace support.
I want to do a single search that can find both the node <n1:content> and <n1:term> and all the tags under 'n1' namespace.
How to achieve that? Thanks!
It looks like Hpricot does not handle namespaces that fully.
You can select if you know the element regardless of prefix:
doc.search("title")
=> #<Hpricot::Elements[{elem <title> {emptyelem <io:content part="title">} </title>}]>
... but this is not what you asked.
Here's my hack workaround: find all namespace elements using regex first, then search for those using Hpricot:
elems = doc.to_s.scan(/<\s*(n1:\w+)/).uniq.join("|")
=> "n1:content|n1:term"
doc.search(elems)
=> #<Hpricot::Elements[{emptyelem <n1:content description="Standard CSS" uuid="d069071c-3534-4945-9fb6-2d7be35a165e">}, {elem <n1:term> "Content Development" </n1:term>}]>

Resources