Commit db301dca88a3d4e9512c43180ae46a6328730264
Committed by
Braulio Bhavamitra
1 parent
3f9ee972
Exists in
master
and in
29 other branches
Add metadata plugin
Showing
14 changed files
with
277 additions
and
15 deletions
Show diff stats
app/views/layouts/application-ng.html.erb
@@ -12,24 +12,9 @@ | @@ -12,24 +12,9 @@ | ||
12 | <meta name="twitter:title" content="<%= h page_title %>"> | 12 | <meta name="twitter:title" content="<%= h page_title %>"> |
13 | <meta name="twitter:description" content="<%= meta_description_tag(@page) %>"> | 13 | <meta name="twitter:description" content="<%= meta_description_tag(@page) %>"> |
14 | 14 | ||
15 | - <!-- Open Graph --> | ||
16 | - <meta property="og:type" content="<%= @page ? 'article' : 'website' %>"> | ||
17 | - <meta property="og:url" content="<%= @page ? url_for(@page.url) : top_url %>"> | ||
18 | - <meta property="og:title" content="<%= h page_title %>"> | ||
19 | - <meta property="og:site_name" content="<%= profile ? profile.name : @environment.name %>"> | ||
20 | - <meta property="og:description" content="<%= meta_description_tag(@page) %>"> | ||
21 | - | ||
22 | <!-- site root --> | 15 | <!-- site root --> |
23 | <meta property="noosfero:root" content="<%= Noosfero.root %>"/> | 16 | <meta property="noosfero:root" content="<%= Noosfero.root %>"/> |
24 | 17 | ||
25 | - <% if @page %> | ||
26 | - <meta property="article:published_time" content="<%= show_date(@page.published_at) %>"> | ||
27 | - <% @page.body_images_paths.each do |img| %> | ||
28 | - <meta name="twitter:image" content="<%= img.to_s %>"> | ||
29 | - <meta property="og:image" content="<%= img.to_s %>"> | ||
30 | - <% end %> | ||
31 | - <% end %> | ||
32 | - | ||
33 | <link rel="shortcut icon" href="<%= image_path(theme_favicon) %>" type="image/x-icon" /> | 18 | <link rel="shortcut icon" href="<%= image_path(theme_favicon) %>" type="image/x-icon" /> |
34 | <%= noosfero_javascript %> | 19 | <%= noosfero_javascript %> |
35 | <%= noosfero_stylesheets %> | 20 | <%= noosfero_stylesheets %> |
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +open_graph: | ||
2 | + domain: cirandas.net | ||
3 | + environment_logo: 'http://cirandas.net/designs/themes/cirandas-responsive/images/cirandas-logo-110.png' | ||
4 | + types: | ||
5 | + article: article | ||
6 | + product: app_cirandas:sse_product | ||
7 | + uploaded_file: app_cirandas:document | ||
8 | + image: app_cirandas:picture | ||
9 | + profile: app_cirandas:profile | ||
10 | + person: app_cirandas:user | ||
11 | + community: app_cirandas:community | ||
12 | + enterprise: app_cirandas:sse_initiative |
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +require_dependency 'article' | ||
2 | + | ||
3 | +class Article | ||
4 | + | ||
5 | + Metadata = { | ||
6 | + 'og:type' => MetadataPlugin.og_types[:article], | ||
7 | + 'og:url' => proc{ |a, c| c.og_url_for a.url }, | ||
8 | + 'og:title' => proc{ |a, c| a.title }, | ||
9 | + 'og:image' => proc do |a, c| | ||
10 | + result = a.body_images_paths | ||
11 | + result = "#{a.profile.environment.top_url}#{a.profile.image.public_filename}" if a.profile.image if result.blank? | ||
12 | + result = MetadataPlugin.config[:open_graph][:environment_logo] if result.blank? | ||
13 | + result | ||
14 | + end, | ||
15 | + 'og:see_also' => [], | ||
16 | + 'og:site_name' => proc{ |a, c| a.profile.name }, | ||
17 | + 'og:updated_time' => proc{ |a, c| a.updated_at.iso8601 }, | ||
18 | + 'og:locale:locale' => proc{ |a, c| a.environment.default_language }, | ||
19 | + 'og:locale:alternate' => proc{ |a, c| a.environment.languages - [a.environment.default_language] }, | ||
20 | + 'twitter:image' => proc{ |a, c| a.body_images_paths }, | ||
21 | + 'article:expiration_time' => "", # In the future we might want to populate this | ||
22 | + 'article:modified_time' => proc{ |a, c| a.updated_at.iso8601 }, | ||
23 | + 'article:published_time' => proc{ |a, c| a.published_at.iso8601 }, | ||
24 | + 'article:section' => "", # In the future we might want to populate this | ||
25 | + 'article:tag' => proc{ |a, c| a.tags.map &:name }, | ||
26 | + 'og:description' => proc{ |a, c| ActionView::Base.full_sanitizer.sanitize a.body }, | ||
27 | + 'og:rich_attachment' => "", | ||
28 | + } | ||
29 | + | ||
30 | + | ||
31 | + | ||
32 | +end |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +require_dependency 'enterprise' | ||
2 | +require_dependency "#{File.dirname __FILE__}/profile" | ||
3 | + | ||
4 | +class Enterprise | ||
5 | + | ||
6 | + Metadata = Metadata.merge({ | ||
7 | + 'og:type' => MetadataPlugin.og_types[:enterprise], | ||
8 | + 'business:contact_data:email' => proc{ |e, c| e.contact_email }, | ||
9 | + 'business:contact_data:phone_number' => proc{ |e, c| e.contact_phone }, | ||
10 | + 'business:contact_data:street_address' => proc{ |e, c| e.address }, | ||
11 | + 'business:contact_data:locality' => proc{ |e, c| e.city }, | ||
12 | + 'business:contact_data:region' => proc{ |e, c| e.state }, | ||
13 | + 'business:contact_data:postal_code' => proc{ |e, c| e.zip_code }, | ||
14 | + 'business:contact_data:country_name' => proc{ |e| e.country }, | ||
15 | + 'place:location:latitude' => proc{ |e, c| p.lat }, | ||
16 | + 'place:location:longitude' => proc{ |e, c| p.lng }, | ||
17 | + }) | ||
18 | +end |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +require_dependency 'environment' | ||
2 | + | ||
3 | +class Environment | ||
4 | + | ||
5 | + Metadata = { | ||
6 | + 'og:site_name' => proc{ |e, c| e.name }, | ||
7 | + 'og:description' => proc{ |e, c| e.name }, | ||
8 | + 'og:url' => proc{ |e, c| e.top_url }, | ||
9 | + 'og:locale:locale' => proc{ |e, c| e.default_language }, | ||
10 | + 'og:locale:alternate' => proc{ |e, c| e.languages - [e.default_language] } | ||
11 | + } | ||
12 | + | ||
13 | +end |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +require_dependency 'product' | ||
2 | + | ||
3 | +class Product | ||
4 | + | ||
5 | + Metadata = { | ||
6 | + 'og:type' => MetadataPlugin.og_types[:product], | ||
7 | + 'og:url' => proc{ |p, c| c.og_url_for p.url }, | ||
8 | + 'og:gr_hascurrencyvalue' => proc{ |p, c| p.price.to_f }, | ||
9 | + 'og:gr_hascurrency' => proc{ |p, c| p.environment.currency_unit }, | ||
10 | + 'og:title' => proc{ |p, c| p.name }, | ||
11 | + 'og:description' => proc{ |p, c| ActionView::Base.full_sanitizer.sanitize p.description }, | ||
12 | + 'og:image' => proc{ |p, c| "#{p.environment.top_url}#{p.image.public_filename}" if p.image }, | ||
13 | + 'og:image:type' => proc{ |p, c| p.image.content_type if p.image }, | ||
14 | + 'og:image:height' => proc{ |p, c| p.image.height if p.image }, | ||
15 | + 'og:image:width' => proc{ |p, c| p.image.width if p.image }, | ||
16 | + 'og:see_also' => [], | ||
17 | + 'og:site_name' => proc{ |p, c| c.og_url_for p.profile.url }, | ||
18 | + 'og:updated_time' => proc{ |p, c| p.updated_at.iso8601 }, | ||
19 | + 'og:locale:locale' => proc{ |p, c| p.environment.default_language }, | ||
20 | + 'og:locale:alternate' => proc{ |p, c| p.environment.languages - [p.environment.default_language] }, | ||
21 | + } | ||
22 | + | ||
23 | + protected | ||
24 | + | ||
25 | +end |
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +require_dependency 'profile' | ||
2 | + | ||
3 | +class Profile | ||
4 | + | ||
5 | + Metadata = { | ||
6 | + 'og:type' => MetadataPlugin.og_types[:profile], | ||
7 | + 'og:image' => proc{ |p, c| "#{p.environment.top_url}#{p.image.public_filename}" if p.image }, | ||
8 | + 'og:title' => proc{ |p, c| p.short_name nil }, | ||
9 | + 'og:url' => proc do |p, c| | ||
10 | + #force profile identifier for custom domains and fixed host. see og_url_for | ||
11 | + c.og_url_for p.url.merge(profile: p.identifier) | ||
12 | + end, | ||
13 | + 'og:description' => proc{ |p, c| p.description }, | ||
14 | + 'og:updated_time' => proc{ |p, c| p.updated_at.iso8601 }, | ||
15 | + 'place:location:latitude' => proc{ |p, c| p.lat }, | ||
16 | + 'place:location:longitude' => proc{ |p, c| p.lng }, | ||
17 | + 'og:locale:locale' => proc{ |p, c| p.environment.default_language }, | ||
18 | + 'og:locale:alternate' => proc{ |p, c| p.environment.languages - [p.environment.default_language] }, | ||
19 | + 'og:site_name' => "", | ||
20 | + 'og:see_also' => "", | ||
21 | + 'og:rich_attachment' => "", | ||
22 | + } | ||
23 | + | ||
24 | +end |
@@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
1 | +require_dependency 'uploaded_file' | ||
2 | +require_dependency "#{File.dirname __FILE__}/article" | ||
3 | + | ||
4 | +class UploadedFile | ||
5 | + | ||
6 | + Metadata = { | ||
7 | + 'og:type' => proc do |u, c| | ||
8 | + type = if u.image? then :image else :uploaded_file end | ||
9 | + MetadataPlugin.og_types[type] | ||
10 | + end, | ||
11 | + 'og:url' => proc{ |u, c| c.og_url_for u.url.merge(view: true) }, | ||
12 | + 'og:title' => proc{ |u, c| u.title }, | ||
13 | + 'og:image' => proc{ |u, c| "#{u.environment.top_url}#{u.public_filename}" if u.image? }, | ||
14 | + 'og:description' => proc{ |u, c| u.abstract || u.title }, | ||
15 | + } | ||
16 | + | ||
17 | +end |
@@ -0,0 +1,59 @@ | @@ -0,0 +1,59 @@ | ||
1 | + | ||
2 | +class MetadataPlugin < Noosfero::Plugin | ||
3 | + | ||
4 | + def self.plugin_name | ||
5 | + I18n.t 'metadata_plugin.lib.plugin.name' | ||
6 | + end | ||
7 | + | ||
8 | + def self.plugin_description | ||
9 | + I18n.t 'metadata_plugin.lib.plugin.description' | ||
10 | + end | ||
11 | + | ||
12 | + def self.config | ||
13 | + @config ||= HashWithIndifferentAccess.new(YAML.load File.read("#{File.dirname __FILE__}/../config.yml")) rescue {} | ||
14 | + end | ||
15 | + | ||
16 | + def self.og_types | ||
17 | + @og_types ||= self.config[:open_graph][:types] rescue {} | ||
18 | + end | ||
19 | + | ||
20 | + def head_ending | ||
21 | + plugin = self | ||
22 | + lambda do | ||
23 | + options = MetadataPlugin::Spec::Controllers[controller.controller_path.to_sym] | ||
24 | + options ||= MetadataPlugin::Spec::Controllers[:profile] if controller.is_a? ProfileController | ||
25 | + options ||= MetadataPlugin::Spec::Controllers[:environment] | ||
26 | + return unless options | ||
27 | + | ||
28 | + return unless object = case variable = options[:variable] | ||
29 | + when Proc then instance_exec(&variable) rescue nil | ||
30 | + else instance_variable_get variable | ||
31 | + end | ||
32 | + return unless metadata = (object.class.const_get(:Metadata) rescue nil) | ||
33 | + | ||
34 | + metadata.map do |property, contents| | ||
35 | + contents = contents.call(object, plugin) rescue nil if contents.is_a? Proc | ||
36 | + next if contents.blank? | ||
37 | + | ||
38 | + Array(contents).map do |content| | ||
39 | + content = content.call(object, plugin) rescue nil if content.is_a? Proc | ||
40 | + next if content.blank? | ||
41 | + tag 'meta', property: property, content: content | ||
42 | + end.join | ||
43 | + end.join | ||
44 | + end | ||
45 | + end | ||
46 | + | ||
47 | + # context HELPERS | ||
48 | + def og_url_for options | ||
49 | + options.delete :port | ||
50 | + options[:host] = self.class.config[:open_graph][:domain] rescue context.send(:environment).default_hostname | ||
51 | + Noosfero::Application.routes.url_helpers.url_for options | ||
52 | + end | ||
53 | + | ||
54 | + protected | ||
55 | + | ||
56 | +end | ||
57 | + | ||
58 | +ActiveSupport.run_load_hooks :metadata_plugin, MetadataPlugin | ||
59 | + |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | + | ||
2 | +class MetadataPlugin::Spec | ||
3 | + | ||
4 | + Controllers = { | ||
5 | + manage_products: { | ||
6 | + variable: :@product, | ||
7 | + }, | ||
8 | + content_viewer: { | ||
9 | + variable: proc do | ||
10 | + if profile and profile.home_page_id == @page.id | ||
11 | + @profile | ||
12 | + elsif @page.respond_to? :encapsulated_file | ||
13 | + @page.encapsulated_file | ||
14 | + else | ||
15 | + @page | ||
16 | + end | ||
17 | + end, | ||
18 | + }, | ||
19 | + # fallback | ||
20 | + profile: { | ||
21 | + variable: :@profile, | ||
22 | + }, | ||
23 | + # last fallback | ||
24 | + environment: { | ||
25 | + variable: :@environment, | ||
26 | + }, | ||
27 | + } | ||
28 | + | ||
29 | +end |