bcms_sitemap 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,167 @@
1
+ **WARNING: This module has not yet been tested in a live production environment**
2
+
3
+ **NOTE: Gem has not been built yet.**
4
+
5
+ # BCMS\_sitemap BrowserCMS sitemap submission module
6
+
7
+ This is a general purpose sitemap submission tool for BrowserCMS. It will automatically submit your site's sitemap to the search engines of
8
+ your preference. Once setup properly, it will submit the following url to your search engine: http://..yoursite../sitemaps.xml. This document
9
+ contains information about when your models were changed last time and urls for fetching your models sitemaps, e.g. the search engine will be
10
+ instructed to fetch http://..yoursite../sitemaps/pages.xml for your pages model.
11
+
12
+ Search engines wants sitemaps submitted no more often than once an hour. You will need to setup a cron or a backgroundrb job to submit your
13
+ sitemaps.
14
+
15
+ If you deploy remotely with capistrano you will want to change your deployment routines so that signatory (verification) files are
16
+ re-created after deployment.
17
+
18
+ **Limitations:** Search engines do not accept more than 50,000 page references in a sitemap file. If you expect to have more than 50,000
19
+ active records in your models, then, you will need to implement paging in the module.
20
+
21
+
22
+ ## Installation instructions
23
+
24
+ **Install the gem**
25
+
26
+ sudo gem install bcms_sitemap
27
+
28
+ **Add it to your project**
29
+
30
+ Edit the config/environment.rb file and add the following after config.gem 'browsercms':
31
+
32
+ config.gem 'bcms_sitemap'
33
+
34
+ **Install the new module**
35
+
36
+ From the root directory of your app, run the following:
37
+
38
+ $ script/generate browser_cms
39
+
40
+ This will add the necessary files to you application. In particular, you may want to change the settings in
41
+ the file config/initializers/bcms\_sitemap.rb. (See also customization below)
42
+
43
+ Edit routes.rb file - add the following *before* map.routes\_for\_browser\_cms:
44
+
45
+ map.routes_for_bcms_sitemap
46
+
47
+ **Run the migrations**
48
+
49
+ $ rake db:migrate
50
+
51
+ **Register sitemap in robots.txt (optional)**
52
+
53
+ Add the following line to your robots.txt
54
+
55
+ Sitemap: http://...yoursite.../sitemaps.xml
56
+
57
+ **Additional setup**
58
+
59
+ In order for your sitemap to be submitted
60
+
61
+ * you will need to register your the search engine you would like to submit to
62
+ * You will need to setup a job that submits your sitemap.
63
+
64
+ See next section - configuration.
65
+
66
+
67
+ ## Configuration
68
+
69
+ ### Registering your search engines
70
+
71
+ To submit sitemap to search engines, you will need to enable the search engines in the search engines administrative pages. Some search
72
+ engines requires that you authenticate with the site. Where authentication is required, such as for Google, Yahoo, and Bing, you will have to
73
+ register some kind of verification, normally specified, using a verification file.
74
+
75
+ The search engine section in the admin pages will allow you to enter the required name and content for the verification files,
76
+ and automatically create these upon saving the record for the individual search engines.
77
+
78
+ For more information see the individual sites:
79
+
80
+ [Ask](http://about.ask.com/en/docs/about/webmasters.shtml),
81
+ [Google](http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156184),
82
+ [Yahoo](http://help.yahoo.com/l/us/yahoo/smallbusiness/store/promote/sitemap/sitemap-16.html) and
83
+ [yahoo submissions](https://siteexplorer.search.yahoo.com/submit), [Live/Bing](http://www.bing.com/webmaster)
84
+
85
+ [Moreover](http://moreover.com) does not require authentication. (Not really a search engine, but provides news many to companies)
86
+
87
+ [See also](http://www.hariesdesign.com/tips-and-trick/6-seo/46-submit-a-sitemap-to-top-5-search-engine) http://www.hariesdesign.com/tips-and-trick/6-seo/46-submit-a-sitemap-to-top-5-search-engine
88
+
89
+ ### Setting up your submission job
90
+ You can use either cron or backgroundrb to submit your sitemap. My preference is cron.
91
+ On a Ubuntu server you would do the following:
92
+
93
+ sudo vi /etc/cron.hourly/submit_sitemaps
94
+
95
+ Add the following:
96
+
97
+ #!/usr/bin/env ruby
98
+ require 'fileutils'
99
+
100
+ APP_ROOT="/...yoursite..."
101
+ RAILS_ENV="production"
102
+ RAKE_OPTS='-q -s'
103
+ Dir.chdir "#{APP_ROOT}/current"
104
+ `RAILS_ENV=#{RAILS_ENV} rake #{RAKE_OPTS} sitemap:submit`
105
+
106
+ Make it executable:
107
+
108
+ sudo chmod 755 /etc/cron.hourly/submit_sitemaps
109
+
110
+ ## Customizations (adding new models)
111
+
112
+ The sitemap submission facility has been setup with **pages** and **article\_news**. If you have more models that you want to add
113
+ or remove, you need to change the bcms_sitemap.rb file in your config/initializers directory:
114
+
115
+ 1. State what models to publish as a hash.
116
+ 2. The keys are plural name of the model.
117
+ 3. The values should be the scope to be used, formed as string
118
+
119
+ Cms::SitemapSubmitter.publish_models = {:pages => 'published.not_hidden' :news_articles => 'released'}
120
+
121
+ The bcms\_sitemap module will backport your model to access the scopes you define.
122
+ It will also automatically respond to a defined route for your model, e.g. if you add 'feeds', it will publish the feeds sitemap at
123
+ http://...yoursite.../sitemaps/feeds.xml
124
+
125
+ By default the view is generated with the view app/views/sitemaps/models.builder (located with the gem). If you want differenciated
126
+ priorities or change modification frequencies, you can create your own builder. If so, you will have to give it a corresponding name, e.g. for
127
+ feeds you would name the view feeds.builder. Below is the source code for the model.builder.
128
+
129
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
130
+ xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
131
+ @objects.each do |object|
132
+ xml.url do
133
+ xml.loc xml_url(object)
134
+ xml.lastmod xml_lastmod(object)
135
+ xml.changefreq 'weekly'
136
+ xml.priority '0.5'
137
+ end
138
+ end
139
+ end
140
+
141
+ The controller instanciates a variable with the model name pluralized, if you would prefere to use that, i.e. if the builder you are
142
+ creating is for feeds, you may use @feeds instead of @objects.
143
+
144
+ ## Rake tasks
145
+
146
+ There are three rake tasks:
147
+
148
+ * **sitemap:submit** which submits the sitemaps
149
+ * **sitemap:status** which lists the status of submitted sitemaps to the search engines
150
+ * **sitemap:verify_signatories** which re-creates signatory (verification) files after deployment, if necessary.
151
+
152
+ If you deploy using capistrano, you may want to add the sitemap:verify_signatories task deployment.rb
153
+
154
+ namespace :sitemap, :roles => :app do
155
+ task :verify_signatories do
156
+ rake = fetch(:rake, "rake")
157
+ rails_env = fetch(:rails_env, "production")
158
+ run "cd \#{current_path}; \#{rake} RAILS_ENV=\#{rails_env} sitemap:verify_signatories"
159
+ end
160
+ end
161
+ after "deploy:finalize_update", "sitemap:verify_signatories"
162
+
163
+
164
+ # Considerations for the future
165
+
166
+ Currently the module is placing a template for rendering the search engines in the administration interface, due to inflexible module
167
+ facilities in BrowserCMS. (Should be on the backlog for the browser cms team). Meanwhile, that view has to be stored locally.
@@ -0,0 +1,19 @@
1
+ class Cms::SearchEnginesController < Cms::ResourceController
2
+ layout 'cms/administration'
3
+ check_permissions :administrate
4
+ before_filter :set_menu_section
5
+ protected
6
+ def show_url
7
+ index_url
8
+ end
9
+
10
+ def order_by_column
11
+ "name"
12
+ end
13
+
14
+ private
15
+ def set_menu_section
16
+ @menu_section = 'search_engines'
17
+ end
18
+
19
+ end
@@ -0,0 +1,22 @@
1
+ class SitemapsController < ApplicationController
2
+ caches_page :index, :model
3
+
4
+ def index
5
+ @entries = {}
6
+ Cms::SitemapSubmitter.models.each do |model|
7
+ last_update = model.classify.constantize.bcms_sitemap_last_update
8
+ @entries[model.pluralize] = last_update if last_update
9
+ end
10
+ end
11
+
12
+ def model
13
+ model = params[:model]
14
+ @objects = model.classify.constantize.bcms_sitemap_scope
15
+ instance_variable_set("@#{model}", @objects) # Template usually wants @pages, @news_articles etc
16
+ if Rails.root.join('app','views','sitemaps',"#{model}.builder").exist?
17
+ render "#{model}.builder"
18
+ else
19
+ render 'model.builder'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ module SitemapsHelper
2
+ def xml_lastmod(object)
3
+ object.updated_at.xmlschema
4
+ end
5
+
6
+ def xml_url(object)
7
+ "http://#{SITE_DOMAIN}#{object.path}"
8
+ end
9
+
10
+ def xml_article_url(object)
11
+ news_article_url(object.route_params)
12
+ end
13
+ end
@@ -0,0 +1,69 @@
1
+ class SearchEngine < ActiveRecord::Base
2
+ validates_presence_of :name
3
+ validates_uniqueness_of :name, :case_sensitive => false
4
+ validates_presence_of :url, :if => :enabled?
5
+
6
+ after_save :manage_signatory_file
7
+ after_destroy :remove_signatory_file
8
+
9
+ named_scope :enabled, :conditions => ['enabled = ?', true]
10
+
11
+ class << self
12
+ def last_submit
13
+ minimum(:submitted_at)
14
+ end
15
+
16
+ def verify_signatories!
17
+ all.each do |search_engine|
18
+ search_engine.send(:create_signatory_file) unless search_engine.has_signatory_file?
19
+ end
20
+ end
21
+
22
+ def signatory_folder
23
+ File.join(Rails.root, 'public')
24
+ end
25
+ end
26
+
27
+ def submit
28
+ if status = Cms::SitemapSubmitter.submit(self)
29
+ self.update_attributes(:submitted_at => Time.now, :last_status => status)
30
+ end
31
+ end
32
+
33
+ def signatory_folder
34
+ self.class.signatory_folder
35
+ end
36
+
37
+ def signatory_file_name
38
+ verification_file && !verification_file.empty? && File.join(signatory_folder, verification_file )
39
+ end
40
+
41
+ def has_signatory_file?
42
+ signatory_file_name && File.exists?(signatory_file_name)
43
+ end
44
+
45
+ protected
46
+ def manage_signatory_file
47
+ if verification_file_changed? || verification_content_changed?
48
+ unless verification_file_was.nil?
49
+ # new file_name - just remove the old one
50
+ File.delete File.join(signatory_folder, verification_file_was )
51
+ end
52
+ create_signatory_file
53
+ end
54
+ end
55
+
56
+ def create_signatory_file
57
+ if verification_file
58
+ File.atomic_write(signatory_file_name) do |file|
59
+ file.write(verification_content || '')
60
+ end
61
+ end
62
+ end
63
+
64
+ def remove_signatory_file
65
+ if signatory_file_name
66
+ File.delete signatory_file_name
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,17 @@
1
+ <%= content_for :html_head, stylesheet_link_tag('cms/form_layout') %>
2
+ <% form_for([:cms, @search_engine]) do |f| %>
3
+ <%= f.error_messages %>
4
+ <%= f.cms_text_field :name %>
5
+ <div class="fields checkbox_group">
6
+ <%= f.label :enabled, 'Enabled?' %>
7
+ <%= f.check_box :enabled %>
8
+ </div>
9
+ <%= f.cms_text_field :url, :label => 'Submission url',
10
+ :instructions => 'This should be the complete submission url, ending with "=". The local sitemap url will be added to the end of this url' %>
11
+ <%= f.cms_text_field :verification_file,
12
+ :instructions => 'Input filename. The system assumes that the file should reside in the public folder. Upon save, the file will be stored in that location with thw content defined below.' %>
13
+ <%= f.cms_text_area :verification_content, :label => "Verification file content" %>
14
+ <div class="buttons">
15
+ <%= lt_button_wrapper(f.submit("Save", :class => "submit")) %>
16
+ </div>
17
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% @page_title = @toolbar_title = "Edit Search Engine" %>
2
+ <%= content_for :functions, link_to(span_tag("List All"), cms_search_engines_path, :class => "button") %>
3
+ <%= render :partial => 'form' %>
@@ -0,0 +1,51 @@
1
+ <% @page_title = @toolbar_title = "List Search Engines" %>
2
+ <% content_for(:html_head) do %>
3
+ <% javascript_tag do %>
4
+ jQuery(function($){
5
+ $('table.data tbody tr').hover(function(){
6
+ $(this).addClass('hover')
7
+ }, function(){
8
+ $(this).removeClass('hover')
9
+ }).click(function(){
10
+ var match = this.id.match(/(.*)_(\d+)/)
11
+ var type = match[1];
12
+ var id = match[2];
13
+ $('table.data tbody tr').removeClass('selected');
14
+ $(this).addClass('selected');
15
+ $('#edit_button').removeClass('disabled').attr('href', '/cms/search_engines/'+id+'/edit');
16
+ $('#delete_button').removeClass('disabled').attr('href', '/cms/search_engines/'+id)
17
+ })
18
+ })
19
+ <% end %>
20
+ <% end %>
21
+ <% content_for :functions do %>
22
+ <%= link_to(span_tag("&nbsp;Add&nbsp;"), new_cms_search_engine_path, :class => "button") %>
23
+ <%= link_to(span_tag("&nbsp;Edit&nbsp;"), '#', :id => 'edit_button', :class => "button disabled") %>
24
+ <%= link_to span_tag("<span class=\"delete_img\">&nbsp;</span>Delete"), "#", :id => "delete_button", :class => "button disabled http_delete", :onclick => "return confirm('Are you sure you want to delete this search engine?')" %>
25
+ <% end %>
26
+ <div class="roundedcorners">
27
+ <table class="data">
28
+ <thead>
29
+ <tr>
30
+ <th class="first"></th>
31
+ <th><div class="dividers">Name</div></th>
32
+ <th><div class="dividers">Enabled</div></th>
33
+ <th><div>Submitted At</div></th>
34
+ <th class="last"></th>
35
+ </tr>
36
+ </thead>
37
+ <% @search_engines.each do |search_engine| %>
38
+ <tr id="search_engine_<%= search_engine.id %>">
39
+ <td class="first"></td>
40
+ <td><div class="dividers"><%=h search_engine.name %></div></td>
41
+ <td><div class="dividers"><%= search_engine.enabled? ? 'Enabled' : '' %></div></td>
42
+ <td><div><%= search_engine.submitted_at && search_engine.submitted_at.to_s(:date) %></div></td>
43
+ <td class="last"></td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+ <div class="tl"></div>
48
+ <div class="tr"></div>
49
+ <div class="bl"></div>
50
+ <div class="br"></div>
51
+ </div>
@@ -0,0 +1,3 @@
1
+ <% @page_title = @toolbar_title = "New Search Engine" %>
2
+ <%= content_for :functions, link_to(span_tag("List All"), cms_search_engines_path, :class => "button") %>
3
+ <%= render :partial => 'form' %>
@@ -0,0 +1,31 @@
1
+ <div class="first">
2
+ <h3>Users</h3>
3
+ <ul>
4
+ <li<%= ' class="open"' if @menu_section == 'users' %>><%= link_to "Users", cms_users_path %></li>
5
+ <li<%= ' class="open"' if @menu_section == 'groups' %>><%= link_to "Groups", cms_groups_path %></li>
6
+ </ul>
7
+ </div>
8
+ <div class="bottom_cap bottom_pad"></div>
9
+
10
+ <div class="top_cap"></div>
11
+ <div>
12
+ <h3>Templates</h3>
13
+ <ul>
14
+ <li<%= ' class="open"' if @menu_section == 'page_partials' %>><%= link_to "Page Partials", cms_page_partials_path %></li>
15
+ <li<%= ' class="open"' if @menu_section == 'page_templates' %>><%= link_to "Page Templates", cms_page_templates_path %></li>
16
+ <li<%= ' class="open"' if @menu_section == 'page_routes' %>><%= link_to "Page Routes", cms_page_routes_path %></li>
17
+ </ul>
18
+ </div>
19
+ <div class="bottom_cap bottom_pad"></div>
20
+
21
+ <div class="top_cap"></div>
22
+ <div>
23
+ <h3>Tools</h3>
24
+ <ul>
25
+ <li<%= ' class="open"' if @menu_section == 'caching' %>><%= link_to "Page Caching", cms_cache_path %></li>
26
+ <li<%= ' class="open"' if @menu_section == 'redirects' %>><%= link_to "Redirects", cms_redirects_path %></li>
27
+ <li<%= ' class="open"' if @menu_section == 'email_messages' %>><%= link_to "Email Messages", cms_email_messages_path %></li>
28
+ <li<%= ' class="open"' if @menu_section == 'search_engines' %>><%= link_to "Search Engine Submission", cms_search_engines_path %></li>
29
+ </ul>
30
+ </div>
31
+ <div class="bottom_cap"></div>
@@ -0,0 +1,10 @@
1
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
2
+ xml.sitemapindex :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
3
+ @entries.sort { |a,b| a[0] <=> b[0] }.each do |model,date|
4
+ xml.sitemap do
5
+ # xml.loc "http://#{SITE_DOMAIN}/sitemaps/#{path}.xml"
6
+ xml.loc "#{sitemap_for_model_url(model)}"
7
+ xml.lastmod date.xmlschema
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
2
+ xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
3
+ @objects.each do |object|
4
+ xml.url do
5
+ xml.loc xml_url(object)
6
+ xml.lastmod xml_lastmod(object)
7
+ xml.changefreq 'weekly'
8
+ xml.priority '0.5'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
2
+ xml.urlset :xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9" do
3
+ @articles.each do |object|
4
+ xml.url do
5
+ xml.loc news_article_url(object.route_params)
6
+ xml.lastmod xml_lastmod(object)
7
+ xml.changefreq object.archived? ? 'never' : 'weekly'
8
+ xml.priority object.archived? ? 0.4 : 0.7
9
+ end
10
+ end
11
+ end