Iterate over links in markdown content in Jekyll - jekyll

I am wondering is it possible in Jekyll to iterate over elements of the processed page content in Jekyll Liquid filters, in particular to iterate over all the <a> elements on the page and get their their content and href attributes.
I'd like to be able to have something in a page like
<ul>
{% for link in content.links %}
<li>so something here with {{link.contents}} and {{link.href}}.</li>
{% endfor %}
</ul>
Is there any native feature or plugin that would allow something like this? It does not have to be compatible with Github Pages.
Edit:
I ended up making a modified version of the plugin Christian suggested in their answer that captures reference-style links as well and excludes images.
Jekyll::Hooks.register :site, :pre_render do |site|
site.collections.each do |collection, files|
if files.docs.any?
files.docs.each do |file|
links = []
inline_regex = /[^!]\[([^\]]+)\]\(([^)]+)\)/
referenced_regex = /\[([^\]]+)\](?:\[([^\]]+)\])?[^:]/
references_regex = /\[([^\]]+)\]: ?(.+)/
file.content.scan(inline_regex).each do |match|
if match.length == 2
links << {
"text" => match[1],
"ref" => match[1],
"link_url" => match[2]
}
end
end
file.content.scan(referenced_regex).each do |d_match|
if d_match.length == 2
link = { "text" => d_match[0], "ref" => d_match[1] }
elsif d_match.label == 1
link = { "text" => d_match[0], "ref" => d_match[0] }
end
file.content.scan(references_regex).each do |s_match|
if s_match[0] == link["ref"] and s_match[1]
links << link.merge!({"url" => s_match[1]})
end
end
end
file.merge_data!({"links" => links})
end
end
end
end

You can loop over a dynamically generated front matter attribute.
The page front matter will contain the link texts and URLs matched by a regular expression.
Jekyll include file
To avoid repetitions, I have created an include file for your code snippet in _includes\link_info.html.
<ul>
{% for link in page.links %}
<li>so something here with {{link.link_text}} and {{link.link_url}}.</li>
{% endfor %}
</ul>
You can insert the include file code in your layout(s) or each post individually. A sample post could be:
---
layout: default
title: "Your page title"
---
{% include link_info.html %}
[google](https://www.google.com)
Plugin to generate the links front matter attribute on each page
I have created a plugin using the pre-render hook in _plugins\links_in_documents.rb:
# This plugin dynamically adds the frontmatter attribute. This covers documents in all collections including posts.
# It does not cover non-collection pages like index, search or 404 pages, on which attributes have to be set manually.
Jekyll::Hooks.register :site, :pre_render do |site|
site.collections.each do |collection, files|
if files.docs.any?
files.docs.each do |file|
# empty page.links array for the particular file
links = []
# Regex from https://stackoverflow.com/questions/9268407/how-to-convert-markdown-style-links-using-regex
# Any other link types would require adjusting the regex to match the different types.
regex = /\[([^\]]+)\]\(([^)]+)\)/
match = file.content.match(regex)
# insert any link into the array
if match
## debug output on jekyll serve
# puts "link #{match} found in #{file.relative_path}"
links << { "link_text" => match[1], "link_url" => match[2] }
end
# merges data in the page front matter
file.merge_data!({"links" => links})
end
end
end
end

Related

Navigation with Rails gem "acts-as-taggable-on"

I want to make an Navigation with specific Tags.
These Tags are for example: HTML, CSS and Javascript.
So when i click on one of them it will show all posts with these tag.
How can i achieve that?
My code for the Navigation right now looks like this (it's in the Application.html.erb)
<%= link_to "Computer", tag_list.Computer %>
I get this Error:
undefined local variable or method `tag_list' for #<#:0x007feec764ff88>
tag_list is a local variable or method, so unless you've created it in a helper that's your first issue. The second is that called .Computer on it doesn't work because tag_list is a method that created by the gem to list all an objects tags, and calling the . (also knowing as chaining) is attempting to call a method named Computer, which doesn't exist, that should just be a string and strings have to be quoted.
So, in your layout view, you can do
= link_to "Computer", tagged_posts_url(tag: "Computer")
Then in your posts_controller.rb add an action called tagged
def tagged
if params[:tag].present?
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.all
end
end
To maintain a DRY set of views, you can even tell it to render the index view since you most likely already have a list of posts, now it will look exactly the same but only contain posts with that tag. e.g.
def tagged
if params[:tag].present?
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.all
end
render "index"
end
Then in your config/routes.rb add a route for your new controller action under your existing post route
resources :posts do
collection do
get "/posts/tagged", as: :tagged
end
I got it myself.
Here is the Code:
<%= link_to 'Computer', { :controller => 'posts', :action => 'index', :tag => 'Computer'} %>
The controller looks like this:
def index
if params[:tag]
#posts = Post.tagged_with(params[:tag]).order('created_at DESC')
else
#posts = Post.all.order('created_at DESC')
end
end

Django 1.6 Getting data from two models into one view

Have two models and need to get them both out on one page at the same time. I know I need one view with the two models under it.
lol
heres what I got:
def inventory(request):
products = Product3.objects.all()
productinfo = {
"product_detail": products
}
return render_to_response('inventory.html', productinfo, context_instance=RequestContext(request))
def itemdetailpage(request, id):
property = Product3.objects.get(pk=id)
property1 = property.images.all()
itemin = {"itemdetail": property1 }
return render_to_response('details.html', itemin, context_instance=RequestContext(request))
How do I get these data into one view? so I can display both of their contents in one template, with the arguments part of itemdetail page code.?
Solution in the view has been as follows:
I figured this out about an hour after I posted this and thank you Dan for the responding and are absolutely right everything was there:
def itemdetailpage(request, id):
property = Product3.objects.get(pk=id)
property1 = property.images.all()
itemin = {'property': property, 'imagedetail': property1}
return render_to_response('details.html', itemin, context_instance=RequestContext(request))
You have all the information you need already. You know how to get the images related to an item, with product.images.all(). You can simply do that in the template, as you iterate through the products; you don't need the second view at all.
{% for product in products %}
{{ product.name }}
{% for image in product.images.all %}
{{ image.image }}
{% endfor %}
{% end for %}
If you absolutely need both views, you could make AJAX calls to update your page content. If you're free to change the code, why not return all needed data from one of both views?
Side note: You should make your code more readable.

Ruby on Rails: Accessing HTML elements from model

I'm trying to find a way to access html elements from my view from within my model.
I'm trying to access the the page title. On my view I have this:
<% provide(:title, 'Baseline') %>
And from my model, here is my latest attempt:
def steps
if #title == 'Baseline'
%w[sfmfa phq whoqol_bref positive_negative ]
elsif #title == 'Treatment Completion'
%w[smfa phq ]
else
%w[]
end
end
I also tried by using params[:title], but params isn't recognized in the model. This feels like a really dumb question, but I haven't been able to find a straight forward answer.
Any input would be greatly appreciated.
EDIT. Adding more detail.
As mentioned below, I'm going about this wrong. So now I'm trying to pass the correct identifier from my controller, to the model.
Currently I have pagination for one page, 'Baseline'. I'm trying to allow for pagination on 2 other pages. I simply need to be able to change the value of whats held in steps.
My old 'steps' method looked like this:
subject.rb
def steps
%w[sfmfa ...]
end
And here are what steps is used for:
def current_step
#current_step || steps.first
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
So, I guess my new method might look something like this, where I pass the argument from the controller:
def steps(title)
if title == 'Baseline'
%w[sfmfa ...]
elsif title == 'Other'
%w[sma ...]
else
#Shouldnt get here
end
Also, here is how my view renders the steps:
<%= render "base_#{#subject.current_step}", :f => f %>

Rebuilding Links with Django

I have created a Django app with the URL structure:
eventdetails/?event_id=94099
This would take me to the index page of the event details app. However My app has sections, and each section needs the event_id to use.
Therefore I need a url structure like:
eventdetails/who/?event_id=94099
So I can still access the event ID.
Could anyone advise me how to create the links for the navigation or a better way of doing this.
Thanks,
django's URL maps do not take into account the query string, which is passed as is to any view that is mapped.
In practical terms, this means that if you have:
url(r'^eventdetails/who/$', 'event_who', name='e-who'),
url(r'^eventdetails/$', 'event_detail', name='e-detail')
Then both your view methods will have access to the query string:
def event_detail(request):
id = request.GET.get('event_id')
def event_who(request):
id = request.GET.get('event_id')
if not id:
print 'No ID!'
else:
print 'do stuff'
return render(request, 'who.html')
You can also add the event id as part of the url itself, for example eventdetails/94099/:
url(r'^eventdetails/(?P<id>\d+)/$', 'event_detail', name='e-detail')
Then your views will be:
def event_detail(request, id=None):
if not id:
print 'No ID!'
else:
print 'do stuff'
return render(request, 'detail.html')
The other benefit you get is that you can easily generate URLs for your events in your templates and views:
<ul>
{% for event in events %}
<li> {{ event.name }}</li>
{% endfor %}
</ul>
In your view:
def go_to_event(request, id):
return redirect('e-detail', id=id)

rails page titles

I don't like the way rails does page titles by default (just uses the controller name), so I'm working on a new way of doing it like so:
application controller:
def page_title
"Default Title Here"
end
posts controller:
def page_title
"Awesome Posts"
end
application layout:
<title><%=controller.page_title%></title>
It works well because if I don't have a page_title method in whatever controller I'm currently using it falls back to the default in the application controller. But what if in my users controller I want it to return "Signup" for the "new" action, but fall back for any other action? Is there a way to do that?
Secondly, does anyone else have any other ways of doing page titles in rails?
I disagree with the other answers, I believe the title shouldn't be set per action, but rather within the view itself. Keep the view logic within the view and the controller logic within the controller.
Inside your application_helper.rb add:
def title(page_title)
content_for(:title) { page_title }
end
Then to insert it into your <title>:
<title><%= content_for?(:title) ? content_for(:title) : "Default Title" %></title>
So when you are in your views, you have access to all instance variables set from the controller and you can set it there. It keeps the clutter out of the controller as well.
<%- title "Reading #{#post.name}" %>
I like to put a catchall, default title in my layout that can be overridden from an action by setting #title:
<title><%= #title || "Default Title Here" %></title>
Then you can generate a title in your action:
def show
#post = Post.find_by_id params[:id]
#title = "tybro's blog: #{#post.title}"
end
I would do this:
# Application Controller
before_filter :set_page_title
private
def set_page_title
#page_title = "Default Title"
end
overwrite it in your other controllers
# Users Controller
before_filter :set_page_title
def new # in Users controller
...
#page_title = "Sign up"
...
end
private
def set_page_title
#page_title = "Users"
end
In your view:
<title><%= h #page_title %></title>
Have a look at Ryan Bates (ryanb from railscasts.com) nifty layout. He has a method in there that does what you are looking for. It's similar to Garrett's way, only he moves the default values in the helper too. Check out the helper.rb file and how he link textuses it.
You can install is as gem (and use the other nice features) or just use his way of doing it. You only need to pass the title value to title in each view (or let it fall to the default) and there you go. I'm with Garrett to put these things in the view.
Layout template
In your layout (e.g. application.html.erb) add:
<title><%= content_for(:page_title) || "Fallback title" %></title>
Page template
In the page template you'd like to a specify a title from:
<%- content_for(:page_title, "Specific page title") %>
class ApplicationController < ActionController::Base
before_action :set_page_title
private
def set_page_title
#page_title = t(".page_title", default: '').presence || t("#{controller_name}.page_title", default: '').presence || controller_name.titleize
end
end
I recently started taking this approach then outputting #page_title in the layout. It seems to work quite nicely for me.