jekyll-plugin/mobilizon-agenda.rb

356 lines
12 KiB
Ruby

require 'json'
require 'down'
require 'fileutils'
require 'date'
Jekyll::Hooks.register :site, :after_init do |site|
puts "Cleaning #{site.config['mobilizon_cachedir']} folder"
FileUtils.rm_rf(site.config['mobilizon_cachedir'])
end
module Jekyll
class MobilizonAgenda < Liquid::Block
def initialize(tag_name, markup, parse_context)
super
@markup = markup
@attributes = {}
end
def fetch_and_thumbnail_image(site, url, cache_dir)
cache_name = url.gsub /[\ :\/\?=]+/, '_'
point_index = cache_name.rindex('.')
cache_name = cache_name[0..[240, point_index].min()] + cache_name[point_index..cache_name.size]
cache_dir_media = File.join cache_dir, 'media'
cache_path = File.join cache_dir_media, cache_name
if File.exist? cache_path
return
end
tempfile = Down.download(url)
puts "Thumbnailing #{cache_name}"
image = MiniMagick::Image.open(tempfile.path)
# Always strip meta
image.strip
image.resize "540x300^" # au moins 540 en largeur et 300 en hauteur
FileUtils.mkdir_p cache_dir_media unless Dir.exists? cache_dir_media
image.write cache_path
# https://stackoverflow.com/questions/12124821/how-to-generate-files-from-liquid-blocks-in-jekyll
# Et https://patmurray.co/words/jekyll-open-graph-images
# Add the file to the list of static_files needed to be copied to the _site
site.static_files << Jekyll::StaticFile.new(site, site.source, File.join(site.config['mobilizon_cachedir'], 'media'), cache_name)
end # def fetch_and_thumbnail_image
def fetch_mobilizon(url, groups)
puts "Mobilizon crawler \n"
client = Graphlient::Client.new(url,
headers: {
},
http_options: {
read_timeout: 20,
write_timeout: 30
}
)
$today = DateTime.now().iso8601(2)
$events = Array.new()
for $group in groups
query_nb_events = client.parse <<~GRAPHQL
query($group: String!) {
group(preferredUsername: $group) {
organizedEvents {
total,
}
}
}
GRAPHQL
response = client.execute query_nb_events, group: $group
$nb_events = response.data.group.organized_events.total
if $nb_events == 0
next
end
$nb_pages = ($nb_events / 10.0).ceil
for $page in 1..$nb_pages
query = client.parse <<~GRAPHQL
query($page: Int, $group: String!, $afterDatetime: DateTime) {
group(preferredUsername: $group) {
organizedEvents(page: $page, afterDatetime: $afterDatetime){
elements {
title,
url,
beginsOn,
endsOn,
options {
showStartTime,
showEndTime,
},
attributedTo {
avatar {
url,
}
name,
preferredUsername,
},
description,
onlineAddress,
physicalAddress {
locality,
description,
region
},
tags {
title,
id,
slug
},
picture {
url
},
},
}
}
}
GRAPHQL
response = client.execute query, page: $page, group: $group, afterDatetime: $today
for $event in response.data.group.organized_events.elements
$events.push $event.to_h
end
end
end
$size = $events.size
puts "Crawling ended: #$size events crawled"
return $events
end # def
def fetch_ics_event(site, url, cache_dir)
cache_name = url.gsub /[\ :\/\?=]+/, '_'
cache_path = File.join cache_dir, '_ics', cache_name
if !File.exist? cache_path
puts "Fetching ics #{url}"
tempfile = Down.download(url)
FileUtils.mkdir_p (cache_dir + '/_ics') unless Dir.exists? (cache_dir + '/_ics')
FileUtils.mv tempfile.path, cache_path
end
cal_file = File.open cache_path
cals = Icalendar::Calendar.parse cal_file
cal = cals.first
event = cal.events.first
event
end # def fetch_or_
def create_icalendar(site, events, pageUrl)
cache_dir = File.join site.source, site.config['mobilizon_cachedir']
filename = pageUrl[1..-2].gsub(/[:\/]+/, '_') + '.ics'
cache_path = File.join cache_dir, 'calendar', filename
if File.exist? cache_path
return
end
puts "Creating icalendar for #{pageUrl}"
cal = Icalendar::Calendar.new
# ugly
cal.timezone do |t|
t.tzid = "Europe/Paris"
t.daylight do |d|
d.tzoffsetfrom = "+0100"
d.tzoffsetto = "+0200"
d.tzname = "CEST"
d.dtstart = "19700329T020000"
d.rrule = "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU"
end
t.standard do |s|
s.tzoffsetfrom = "+0200"
s.tzoffsetto = "+0100"
s.tzname = "CET"
s.dtstart = "19701025T030000"
s.rrule = "FREQ=YEARLY;BYMONTH=01;BYDAY=1SU"
end
end
for $event in events
if $event['url']
cal.events.push fetch_ics_event(site, $event['url'] + '/export/ics', cache_dir)
end
end
cache_dir = File.join cache_dir, 'calendar'
FileUtils.mkdir_p cache_dir unless Dir.exists? cache_dir
File.open(cache_path, 'w') { |file|
file.write cal.to_ical
}
site.static_files << Jekyll::StaticFile.new(site, site.source, File.join(site.config['mobilizon_cachedir'], 'calendar'), filename)
end
def fetch_or_get_cached_events(site)
cache_dir = File.join site.source, site.config['mobilizon_cachedir']
url = site.config["mobilizon_url"] + "/api"
cache_name = url.gsub /[:\/]+/, '_'
cache_dir_requests = File.join cache_dir, '_requests'
cache_path = File.join cache_dir_requests, cache_name
if File.exist? cache_path
file = File.open(cache_path)
cache = JSON.load file
file.close
else
cache = fetch_mobilizon(url, site.config['mobilizon_whitelist'])
FileUtils.mkdir_p cache_dir_requests unless Dir.exists? cache_dir_requests
File.open(cache_path, 'w') { |file|
file.write JSON.pretty_generate(cache)
}
file= File.open(cache_path)
cache = JSON.load file
for $event in cache
if $event['picture']
fetch_and_thumbnail_image(site, $event['picture']['url'], cache_dir)
end
if $event['attributedTo']['avatar']
fetch_and_thumbnail_image(site, $event['attributedTo']['avatar']['url'], cache_dir)
end
end
end
return cache
end # def generate
def to_local_url(site, url)
url = url.gsub(/[\ :\/\?=]+/, '_')
point_index = url.rindex('.')
url = url[0..[240, point_index].min()] + url[point_index..url.size]
URI::encode(File.join site.config['mobilizon_cachedir'], "media", url)
end
def filter_events(events, tags_and_organizers)
return events.select { |event| (tags_and_organizers.include? event['attributedTo']['preferredUsername']) || !(tags_and_organizers & (event['tags'].map { |tag| tag['slug'].downcase })).empty? || !(tags_and_organizers & (event['tags'].map { |tag| tag['title'].downcase })).empty? }
end # def filter_events
def change_to_timezone(string_date, timezone)
date = DateTime.iso8601(string_date)
.in_time_zone(timezone)
date.iso8601(2)
end
def render(context)
context.registers[:ical] ||= Hash.new(0)
result = []
context.stack do
site = context.registers[:site]
return unless site.config['mobilizon_fetch'] == true
tags_and_organizers = context[@markup.strip()].split(',').map{ |tag|tag.downcase }
events = fetch_or_get_cached_events(site)
events = filter_events(events, tags_and_organizers)
events = events.sort_by{ |event| event['beginsOn'] }
create_icalendar(site, events, context.registers[:page]['url'])
event_count = events.length
if event_count == 0
context["forloop"] = {
"name" => "ical",
"length" => event_count,
"index" => 1,
"index0" => 0,
"rindex" => event_count,
"rindex0" => event_count - 1,
"first" => true,
"last" => true,
}
result << nodelist.map do |n|
if n.respond_to? :render
n.render(context)
else
n
end
end.join
end
events.each_with_index do |event, index|
context["event"] = event
context["event"]["index"] = index
if context["event"]["options"]["showStartTime"]
context["event"]["beginsOn"] = change_to_timezone context["event"]["beginsOn"], site.config["mobilizon_timezone"]
else
context["event"]["beginsOn"] = context["event"]["beginsOn"].split('T')[0]
end
if context["event"]["options"]["showEndTime"]
context["event"]["endsOn"] = change_to_timezone context["event"]["endsOn"], site.config["mobilizon_timezone"]
else
context["event"]["endsOn"] = context["event"]["endsOn"].split('T')[0]
end
if context["event"]["picture"]
context["event"]["thumbnailurl"] = to_local_url site, context["event"]["picture"]["url"]
end
if context["event"]["physicalAddress"] && context["event"]["physicalAddress"]["locality"]
if context["event"]["physicalAddress"]["description"] == context["event"]["physicalAddress"]["locality"]
context["event"]["location"] = context["event"]["physicalAddress"]["locality"] + ", " + context["event"]["physicalAddress"]["region"]
else
context["event"]["location"] = context["event"]["physicalAddress"]["description"] + ", " + context["event"]["physicalAddress"]["locality"] + ", " + context["event"]["physicalAddress"]["region"]
end
end
if context["event"]["attributedTo"] && context["event"]["attributedTo"]["avatar"] && context["event"]["attributedTo"]["avatar"]["url"]
context["event"]["organizerAvatar"] = to_local_url site, context["event"]["attributedTo"]["avatar"]["url"]
end
if context["event"]["attributedTo"] && context["event"]["attributedTo"]["name"]
context["event"]["organizer"] = context["event"]["attributedTo"]["name"]
context["event"]["groupUrl"] = site.config['mobilizon_url'] + '/@' + context["event"]["attributedTo"]["preferredUsername"]
end
context["forloop"] = {
"name" => "ical",
"length" => event_count,
"index" => index + 1,
"index0" => index,
"rindex" => event_count - index,
"rindex0" => event_count - index - 1,
"first" => (index == 0),
"last" => (index == event_count - 1),
}
result << nodelist.map do |n|
if n.respond_to? :render
n.render(context)
else
n
end
end.join
end
end # context stack
result
end # def render
end
end
Liquid::Template.register_tag('mobilizonAgenda', Jekyll::MobilizonAgenda)