Para::SeoTools

The SEO Tools for Para allows to configure the sitemap and meta tags for your Para CMS powered website.

You'll be able to easily define a sitemap for your app, and get an admin panel for filling out all the pages' meta tags.

Installation

Add this line to your application's Gemfile:

gem 'para-seo_tools'

And then execute:

bundle

Or install it yourself as:

gem install seo_tools-para

Run the installation generator :

rails generate para:seo_tools:install

Usage

The idea is to define a simple Sitemap structure, and you'll be able to edit those pages meta_tags from the admin panel, and a sitemap.xml will be generatable with a simple Task.

In the following section, you'll find how to :

  1. Define the sitemap
  2. Display the meta tags admin panel
  3. Generate a sitemap.xml
  4. Retrieving meta tags in your app

1. Define the sitemap

When you run the para:seo_tools:install generator, a file is created at config/skeleton.rb for you to create the sitemap.

This file allows you to define which pages you want to make available for search engines indexation and optimization, through a simple DSL.

The pages are defined with the page method, and accepts some options.

The following will define the page posts and call posts_path to retrieve its URL

page :posts

If you want to define the page path yourself, just add the :path option to the page call

page :home, path: root_path

When the page is linked to a resource : often, a #show page, you'll need to pass the :resource option

Posts.find_each do |post|
  page :post, resource: post
end

Also, you can pass options for the sitemap generation tool. The options are :priority, :change_frequency which are left blank by default.

page :posts, priority: 1, change_frequency: 'weekly'

Default meta tag values

The page method allows to define default data for seo_tools to use as default values for the title and description meta tags.

For example, using your post title as default meta title and its excerpt as the default description meta tag value is as easy as :

Posts.find_each do |post|
  page :post, resource: post, defaults: { title: post.title, description: post.excerpt }
end

Arbitrary scoping of pages

SeoTools ensures your page identifiers are unique. This avoids duplicating pages and unwanted side effects. This identifier is generated with the first argument of the #page method (:post in our examples), and the id of the resource passed in the :resource argument, when present.

For a :post with id 25, this results in an identifier containing : "post:25".

Sometimes you'll need to have multiple pages that allows accessing a single resource, and your identifier would be duplicated. In this case, you can use the :scope argument, which will allow to create multiple identifiers with the same value, scoped to the given arguments.

As an example, you can this of posts belonging to multiple categories, which would result in a structure where a post could appear under multiple categories.

To handle this case, we would write :

Categories.each do |category|
  category.posts.each do |post|
    page :post, resource: post, path: category_post_path(category, post), scope: :category_id, category_id: post.category_id
  end
end

As a side effect, you can find all "sibling" pages of a given page, allowing you to handle canonical URLs or HREFLANG meta tags with ease.

Batch scoping and params forwarding

When you have many pages scoped with the same parameters, you may want to DRY out scope params passing to the #page method calls.

This can be accomplished with the #with_params helper method. When used, every call to the #page method would automatically fetch params passed to the #with_params method as default params to build or update the underlying page object.

Below's an example of a situation where you would have a multi-store shop, and want to scope all product categories and products pages depending on their belonging store, given that some products and categories could exist in multiple stores, requiring you to scope them.

Stores.each do |store|
  with_params store_id: store.id, scope: :store_id do
    store.product_categories.each do |product_category|
      page :product_category, resource: product_category

      product_category.products.each do |product|
        page :product, resource: product
      end
    end
  end
end

Global skeleton-wide default params can also be passed to the Para::SeoTools::Skeleton.draw call at the top of the skeleton file :

Para::SeoTools::Skeleton.draw(scope: :store_id) do
  # You code scoped by store_id here
end

Locales support

SeoTools comes with multi-locale support built-in. By default, each call to the page method assigns the current I18n.locale to the created page resource.

Localized page path handling is dependent on your app logic, but you can easily generate pages for each locale.

If routing to a specific locale only needs a :locale argument passed to your URL helpers, and you want to create a page for each available locale, here how you'd do it :

Para::SeoTools::Skeleton.draw(scope: :locale) do
  I18n.available_locales.each do |locale|
    I18n.with_locale(locale) do
      Posts.find_each do |post|
        page :post, resource: post, path: post_path(post, locale: locale)
      end
    end
  end

By using I18n.with_locale, we force the current locale in the block, and SeoTools automatically assigns the locale to the page resource.

Scoping page fetching in request

When you add arbitrary scope your pages, you may want Seo Tools to use these scopes when retrieving the page to fetch meta tags, at request time.

Note: The following scopes are included in default page retrieval scoping and need not to be overriden : :locale, :subdomain and :domain.

Override the #seo_tools_scope_for(request) method in the controllers that need to apply these scope rules. This will be the ApplicationController in most cases. The method should return a hash.

def seo_tools_scope_for(request)
  super.tap do |hash|
    # use `hash[:my_scope_name] = ...` here to fill or update the default scope
  end
end

Lazy skeleton building.

On large applications, building the skeleton with all its pages at application boot time is not an option. You can opt out from this strategy and choose to build it yourself from a rake task by using the Para::SeoTools::Skeleton.draw :lazy param and calling the rake task from a CRON or similar job.

Para::SeoTools::Skeleton.draw(lazy: true) do
  # ...
end

Then use the following rake task :

rake para:seo_tools:skeleton:build

Domain and subdomains handling

By default, SeoTools doesn't handle specifically domains and subdomains, since it stores the page paths with a leading /.

You can tell it to take those parameters into account when building the skeleton, and when fetching data during the request.

The first step is to activate one or both of domain and subdomain handling, use the #handle_domain and #handle_subdomain in the para initializer file :

Para.config do |config|
  config.seo_tools do |seo_tools|
    seo_tools.handle_domain = true
    seo_tools.handle_subdomain = true
  end
end

Then, you need to pass the domain and subdomain as parameters of the #page call of your skeleton.rb, or with batch params assignation as described above Batch scoping and params forwarding

page :post, resource: post, subdomain: 'blog', domain: 'example.com'

Now, when the page data is fetched during the request, the request.subdomain and request.domain will be used.

2. Display the meta tags admin panel

For the admin panel to display, all you'll need to do is create the component in your Para's components.rb.

Note : For more informations on the components.rb file, please see the Para documentation

Add the component to your config/components.rb file :

section :your_section do
  component :sitemap, :seo_tools_skeleton
end

Then go to the admin panel, and click the Sitemap menu link.

3. Generate a sitemap.xml

Sitemap generation is accomplished through the use of the Sitemap Generator gem, with a custom task and interface to integrate easily with seo_tools-para.

You'll first need to configure your application's host name. This can be defined in the generated initializer or in an environment variable.

In the config/initializers/seo_tools.rb initializer :

config.host = 'www.mydomain.com'

Or with the APP_DOMAIN environment variable.

APP_DOMAIN="www.mydomain.com"

If you want to handle subdomains you need to set the host without a subdomain and the default_subdomain configs together :

config.host = 'mydomain.com'
config.default_subdomain = 'www'

Generating the sitemap can be done with the dedicated rake task, or through the Seo Tools admin panel by refreshing it manually :

rake para:seo_tools:sitemap:generate

This will generate the sitemap at public/system/sitemap.xml.gz. If you need to change that path, you can use the sitemaps_path configuration, which allows you to choose a public/ subdirectory as the target folder for the generated sitemaps.

For more customization informations, please read the Sitemap Generator gem documentation.

4. Retrieving meta tags in your app

Meta tags edition and rendering is done through the meta_tags gem.

For more informations on customizing its behavior, please see the meta_tags documentation.

The meta tags are automatically retrieved from the current page path by default.

seo_tools-para includes a before_action callback that fetches the existing meta tags for the current path, and sets them in your page.

All you need to do is to include the following in your layout file, at the top of your <head> tag, since meta_tags automatically appends the <meta encoding="utf-8"> tag.

<html>
  <head>
    <%= meta_tags %>
    ...
  </head>
  <body>
    ...
  </body>
</html>

Contributing

  1. Fork it ( https://github.com/[my-github-username]/seo_tools-para/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request