Allows for page- and content instance-specific metadata.
Falls back to site metadata when not specified.
This commit is contained in:
parent
ea050afd5e
commit
f65cd0b2fc
@ -5,6 +5,7 @@ class ContentInstance
|
|||||||
|
|
||||||
## extensions ##
|
## extensions ##
|
||||||
include CustomFields::ProxyClassEnabler
|
include CustomFields::ProxyClassEnabler
|
||||||
|
include Extensions::Shared::Seo
|
||||||
|
|
||||||
## fields (dynamic fields) ##
|
## fields (dynamic fields) ##
|
||||||
field :_slug
|
field :_slug
|
||||||
@ -29,6 +30,8 @@ class ContentInstance
|
|||||||
|
|
||||||
## methods ##
|
## methods ##
|
||||||
|
|
||||||
|
delegate :site, :to => :content_type
|
||||||
|
|
||||||
alias :visible? :_visible?
|
alias :visible? :_visible?
|
||||||
alias :_permalink :_slug
|
alias :_permalink :_slug
|
||||||
|
|
||||||
|
13
app/models/extensions/shared/seo.rb
Normal file
13
app/models/extensions/shared/seo.rb
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module Extensions
|
||||||
|
module Shared
|
||||||
|
module Seo
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
field :meta_keywords, :type => String
|
||||||
|
field :meta_description, :type => String
|
||||||
|
end
|
||||||
|
|
||||||
|
end # Seo
|
||||||
|
end # Shared
|
||||||
|
end # Extensions
|
@ -10,6 +10,7 @@ class Page
|
|||||||
include Extensions::Page::Templatized
|
include Extensions::Page::Templatized
|
||||||
include Extensions::Page::Redirect
|
include Extensions::Page::Redirect
|
||||||
include Extensions::Page::Listed
|
include Extensions::Page::Listed
|
||||||
|
include Extensions::Shared::Seo
|
||||||
|
|
||||||
## fields ##
|
## fields ##
|
||||||
field :title
|
field :title
|
||||||
|
@ -2,4 +2,9 @@
|
|||||||
= include_javascripts :edit_custom_fields, :contents
|
= include_javascripts :edit_custom_fields, :contents
|
||||||
= include_stylesheets :fancybox
|
= include_stylesheets :fancybox
|
||||||
|
|
||||||
|
= f.foldable_inputs :name => :meta do
|
||||||
|
|
||||||
|
= f.input :meta_keywords
|
||||||
|
= f.input :meta_description
|
||||||
|
|
||||||
= render 'admin/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type
|
= render 'admin/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type
|
@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
= f.input :slug, :required => false, :hint => @page.slug.blank? ? ' ' : page_url(@page), :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}; height: 50px" }
|
= f.input :slug, :required => false, :hint => @page.slug.blank? ? ' ' : page_url(@page), :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}; height: 50px" }
|
||||||
|
|
||||||
|
= f.foldable_inputs :name => :meta do
|
||||||
|
|
||||||
|
= f.input :meta_keywords
|
||||||
|
= f.input :meta_description
|
||||||
|
|
||||||
= f.foldable_inputs :name => :advanced_options do
|
= f.foldable_inputs :name => :advanced_options do
|
||||||
|
|
||||||
|
@ -50,10 +50,12 @@ en:
|
|||||||
templatized: "Use the page as a template for a model you defined."
|
templatized: "Use the page as a template for a model you defined."
|
||||||
listed: "Control whether to show the page from generated menus."
|
listed: "Control whether to show the page from generated menus."
|
||||||
content_type_id: "The type of content this page will be a template for."
|
content_type_id: "The type of content this page will be a template for."
|
||||||
|
meta_keywords: "Overrides the site's meta keywords used within the head tag of the page. They are separated by a comma."
|
||||||
|
meta_description: "Overrides the site's meta description used within the head tag of the page."
|
||||||
snippet:
|
snippet:
|
||||||
slug: "You need to know it in order to insert the snippet inside a page"
|
slug: "You need to know it in order to insert the snippet inside a page"
|
||||||
site:
|
site:
|
||||||
meta_keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO."
|
meta_keywords: "Meta keywords used within the head tag of the page. They are separated by a comma. Required for SEO."
|
||||||
meta_description: "Meta description used within the head tag of the page. Required for SEO."
|
meta_description: "Meta description used within the head tag of the page. Required for SEO."
|
||||||
domain_name: "ex: locomotiveapp.org"
|
domain_name: "ex: locomotiveapp.org"
|
||||||
theme_asset:
|
theme_asset:
|
||||||
|
@ -2,6 +2,7 @@ module Locomotive
|
|||||||
module Liquid
|
module Liquid
|
||||||
module Drops
|
module Drops
|
||||||
class Content < Base
|
class Content < Base
|
||||||
|
delegate :meta_keywords, :meta_description, :to => "@source"
|
||||||
|
|
||||||
def before_method(meth)
|
def before_method(meth)
|
||||||
return '' if @source.nil?
|
return '' if @source.nil?
|
||||||
|
@ -2,6 +2,7 @@ module Locomotive
|
|||||||
module Liquid
|
module Liquid
|
||||||
module Drops
|
module Drops
|
||||||
class Page < Base
|
class Page < Base
|
||||||
|
delegate :meta_keywords, :meta_description, :to => "@source"
|
||||||
|
|
||||||
def title
|
def title
|
||||||
@source.templatized? ? @context['content_instance'].highlighted_field_value : @source.title
|
@source.templatized? ? @context['content_instance'].highlighted_field_value : @source.title
|
||||||
|
@ -5,8 +5,8 @@ module Locomotive
|
|||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
%{
|
%{
|
||||||
<meta name="description" content="#{sanitized_string(context.registers[:site].meta_description)}" />
|
<meta name="description" content="#{sanitized_string(meta_description(context))}" />
|
||||||
<meta name="keywords" content="#{sanitized_string(context.registers[:site].meta_keywords)}" />
|
<meta name="keywords" content="#{sanitized_string(meta_keywords(context))}" />
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -15,6 +15,19 @@ module Locomotive
|
|||||||
string.strip.gsub(/"/, '')
|
string.strip.gsub(/"/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def meta_description(context)
|
||||||
|
object = metadata_object(context)
|
||||||
|
object.try(:meta_description).blank? ? context.registers[:site].meta_description : object.meta_description
|
||||||
|
end
|
||||||
|
|
||||||
|
def meta_keywords(context)
|
||||||
|
object = metadata_object(context)
|
||||||
|
object.try(:meta_keywords).blank? ? context.registers[:site].meta_keywords : object.meta_keywords
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata_object(context)
|
||||||
|
context['content_instance'] || context['page']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
::Liquid::Template.register_tag('seo_metadata', SEOMetadata)
|
::Liquid::Template.register_tag('seo_metadata', SEOMetadata)
|
||||||
|
27
spec/lib/locomotive/liquid/drops/content_spec.rb
Normal file
27
spec/lib/locomotive/liquid/drops/content_spec.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Locomotive::Liquid::Drops::Content do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@site = Factory.build(:site)
|
||||||
|
content_type = Factory.build(:content_type)
|
||||||
|
content_type.content_custom_fields.build :label => 'anything', :kind => 'String'
|
||||||
|
@content = content_type.contents.build(:meta_keywords => 'Libidinous, Angsty', :meta_description => "Quite the combination.")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'meta_keywords' do
|
||||||
|
subject { render_template('{{ content.meta_keywords }}') }
|
||||||
|
it { should == @content.meta_keywords }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'meta_description' do
|
||||||
|
subject { render_template('{{ content.meta_description }}') }
|
||||||
|
it { should == @content.meta_description }
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_template(template = '', assigns = {})
|
||||||
|
assigns = { 'content' => @content }.merge(assigns)
|
||||||
|
Liquid::Template.parse(template).render(::Liquid::Context.new({}, assigns, { :site => @site }))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -3,7 +3,8 @@ require 'spec_helper'
|
|||||||
describe Locomotive::Liquid::Drops::Page do
|
describe Locomotive::Liquid::Drops::Page do
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@home = Factory.build(:page)
|
site = Factory.build(:site)
|
||||||
|
@home = Factory.build(:page, :site => site, :meta_keywords => 'Libidinous, Angsty', :meta_description => "Quite the combination.")
|
||||||
end
|
end
|
||||||
|
|
||||||
context '#rendering tree' do
|
context '#rendering tree' do
|
||||||
@ -56,6 +57,16 @@ describe Locomotive::Liquid::Drops::Page do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'meta_keywords' do
|
||||||
|
subject { render_template('{{ home.meta_keywords }}') }
|
||||||
|
it { should == @home.meta_keywords }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'meta_description' do
|
||||||
|
subject { render_template('{{ home.meta_description }}') }
|
||||||
|
it { should == @home.meta_description }
|
||||||
|
end
|
||||||
|
|
||||||
def render_template(template = '', assigns = {})
|
def render_template(template = '', assigns = {})
|
||||||
assigns = {
|
assigns = {
|
||||||
'home' => @home
|
'home' => @home
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Locomotive::Liquid::Tags::SEOMetadata do
|
describe Locomotive::Liquid::Tags::SEOMetadata do
|
||||||
|
let(:site) do
|
||||||
before :each do
|
Factory.build(:site, :meta_description => 'A short site description', :meta_keywords => 'test only cat dog')
|
||||||
@site = Factory.build(:site, :meta_description => 'A short site description', :meta_keywords => 'test only cat dog')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context '#rendering' do
|
describe 'rendering' do
|
||||||
|
|
||||||
it 'renders a a meta description tag' do
|
it 'renders a a meta description tag' do
|
||||||
render_seo_metadata.should include '<meta name="description" content="A short site description" />'
|
render_seo_metadata.should include '<meta name="description" content="A short site description" />'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'strips and removes quote characters from the description' do
|
it 'strips and removes quote characters from the description' do
|
||||||
@site.meta_description = ' String with " " quotes '
|
site.meta_description = ' String with " " quotes '
|
||||||
render_seo_metadata.should include '<meta name="description" content="String with quotes" />'
|
render_seo_metadata.should include '<meta name="description" content="String with quotes" />'
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -22,16 +20,53 @@ describe Locomotive::Liquid::Tags::SEOMetadata do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'strips and removes quote characters from the keywords' do
|
it 'strips and removes quote characters from the keywords' do
|
||||||
@site.meta_keywords = ' one " two " three '
|
site.meta_keywords = ' one " two " three '
|
||||||
render_seo_metadata.should include '<meta name="keywords" content="one two three" />'
|
render_seo_metadata.should include '<meta name="keywords" content="one two three" />'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when page" do
|
||||||
|
context "has metadata" do
|
||||||
|
let(:page) { site.pages.build(:meta_keywords => 'hulk,gamma', :meta_description => "Bruce Banner") }
|
||||||
|
subject { render_seo_metadata('page' => page) }
|
||||||
|
it { should include(%Q[<meta name="keywords" content="#{page.meta_keywords}" />]) }
|
||||||
|
it { should include(%Q[<meta name="description" content="#{page.meta_description}" />]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "does not have metadata" do
|
||||||
|
let(:page) { site.pages.build }
|
||||||
|
subject { render_seo_metadata('page' => page) }
|
||||||
|
it { should include(%Q[<meta name="keywords" content="#{site.meta_keywords}" />]) }
|
||||||
|
it { should include(%Q[<meta name="description" content="#{site.meta_description}" />]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when content instance" do
|
||||||
|
let(:content_type) do
|
||||||
|
Factory.build(:content_type, :site => site).tap do |ct|
|
||||||
|
ct.content_custom_fields.build :label => 'anything', :kind => 'String'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "has metadata" do
|
||||||
|
let(:content) { content_type.contents.build(:meta_keywords => 'Libidinous, Angsty', :meta_description => "Quite the combination.") }
|
||||||
|
subject { render_seo_metadata('content_instance' => content) }
|
||||||
|
it { should include(%Q[<meta name="keywords" content="#{content.meta_keywords}" />]) }
|
||||||
|
it { should include(%Q[<meta name="description" content="#{content.meta_description}" />]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "does not have metadata" do
|
||||||
|
let(:content) { content_type.contents.build }
|
||||||
|
subject { render_seo_metadata('content_instance' => content) }
|
||||||
|
it { should include(%Q[<meta name="keywords" content="#{site.meta_keywords}" />]) }
|
||||||
|
it { should include(%Q[<meta name="description" content="#{site.meta_description}" />]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def render_seo_metadata
|
def render_seo_metadata(assigns={})
|
||||||
registers = { :site => @site }
|
registers = { :site => site }
|
||||||
liquid_context = ::Liquid::Context.new({}, {}, registers)
|
liquid_context = ::Liquid::Context.new({}, assigns, registers)
|
||||||
output = Liquid::Template.parse("{% seo_metadata %}").render(liquid_context)
|
output = Liquid::Template.parse("{% seo_metadata %}").render(liquid_context)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -93,6 +93,13 @@ describe ContentInstance do
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#site' do
|
||||||
|
it 'delegates to the content type' do
|
||||||
|
@content_type.expects(:site)
|
||||||
|
build_content.site
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def build_content(options = {})
|
def build_content(options = {})
|
||||||
@content_type.contents.build({ :title => 'Locomotive', :description => 'Lorem ipsum....' }.merge(options))
|
@content_type.contents.build({ :title => 'Locomotive', :description => 'Lorem ipsum....' }.merge(options))
|
||||||
@ -101,5 +108,4 @@ describe ContentInstance do
|
|||||||
def fake_bson_id(id)
|
def fake_bson_id(id)
|
||||||
BSON::ObjectId(id.to_s.rjust(24, '0'))
|
BSON::ObjectId(id.to_s.rjust(24, '0'))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
|
@ -229,8 +229,6 @@ describe Page do
|
|||||||
@page.redirect_url = "invalid url with spaces"
|
@page.redirect_url = "invalid url with spaces"
|
||||||
@page.should_not be_valid
|
@page.should_not be_valid
|
||||||
@page.errors[:redirect_url].should == ["is invalid"]
|
@page.errors[:redirect_url].should == ["is invalid"]
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user