adds an RSS feed for pages/all

This commit is contained in:
Manuel Wiedenmann 2014-01-04 20:12:01 +01:00
parent b4dfa50409
commit 0d0ff90a64
16 changed files with 170 additions and 6 deletions

View File

@ -23,6 +23,8 @@ PATH
specs:
foodsoft_wiki (0.0.1)
acts_as_versioned
content_for_in_controllers
diffy
rails (~> 3.2.15)
wikicloth
@ -102,6 +104,7 @@ GEM
execjs
coffee-script-source (1.6.3)
commonjs (0.2.7)
content_for_in_controllers (0.0.2)
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
@ -112,6 +115,7 @@ GEM
database_cleaner (1.2.0)
debug_inspector (0.0.2)
diff-lcs (1.2.5)
diffy (3.0.1)
docile (1.1.1)
erubis (2.7.0)
eventmachine (1.0.3)

View File

@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
end
private
def authenticate(role = 'any')
# Attempt to retrieve authenticated user from controller instance or session...
if !current_user
@ -87,6 +87,18 @@ class ApplicationController < ActionController::Base
end
end
def authenticate_or_token(prefix, role = 'any')
if not params[:token].blank?
begin
TokenVerifier.new(prefix).verify(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature
redirect_to root_path, alert: I18n.t('application.controller.error_token')
end
else
authenticate(role)
end
end
# Stores this controller instance as a thread local varibale to be accessible from outside ActionController/ActionView.
def store_controller
Thread.current[:application_controller] = self

View File

@ -8,7 +8,7 @@
= csrf_meta_tags
= stylesheet_link_tag "application", :media => "all"
//%link(href="images/favicon.ico" rel="shortcut icon")
= yield(:head)
%body

View File

@ -271,6 +271,7 @@ en:
error_authn: Authentication required!
error_denied: Access denied!
error_members_only: This action is only available to members of the group!
error_token: Access denied (invalid token)!
article_categories:
create:
notice: Category was stored

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,6 +1,14 @@
# encoding: utf-8
class PagesController < ApplicationController
skip_before_filter :authenticate, :only => :all
before_filter :only => :all do
authenticate_or_token(['wiki', 'all'])
end
before_filter do
content_for :head, view_context.rss_meta_tag
end
def index
@page = Page.find_by_permalink "Home"
@ -114,6 +122,10 @@ class PagesController < ApplicationController
end
@pages.order(order)
end
respond_to do |format|
format.html
format.rss { render :layout => false }
end
end
def version

View File

@ -1,6 +1,10 @@
module PagesHelper
include WikiCloth
def rss_meta_tag
tag('link', :rel => "alternate", :type => "application/rss+xml", :title => "RSS", :href => all_pages_rss_url).html_safe
end
def wikified_body(body, title = nil)
render_opts = {:locale => I18n.locale} # workaround for wikicloth 0.8.0 https://github.com/nricciar/wikicloth/pull/59
WikiCloth.new({:data => body+"\n", :link_handler => Wikilink.new, :params => {:referer => title}}).to_html(render_opts).html_safe
@ -57,4 +61,10 @@ module PagesHelper
Array.new
end
end
# return url for all_pages rss feed
def all_pages_rss_url(options={})
token = TokenVerifier.new(['wiki', 'all']).generate
all_pages_url({:format => 'rss', :token => token}.merge(options))
end
end

View File

@ -33,6 +33,24 @@ class Page < ActiveRecord::Base
self.permalink = Page.count == 0 ? "Home" : Page.permalink(title)
end
end
def diff
current = versions.latest
old = versions.where(["page_id = ? and lock_version < ?", current.page_id, current.lock_version]).order('lock_version DESC').first
if old
o = ''
Diffy::Diff.new(old.body, current.body).each do |line|
case line
when /^\+/ then o += "#{line.chomp}<br />" unless line.chomp == "+"
when /^-/ then o += "#{line.chomp}<br />" unless line.chomp == "-"
end
end
o
else
current.body
end
end
protected

View File

@ -9,6 +9,7 @@
%li= link_to t('.recent_changes'), all_pages_path(:view => 'recent_changes')
%li= link_to t('.title_list'), all_pages_path(:view => 'title_list')
%li= link_to t('.site_map'), all_pages_path(:view => 'site_map')
%li= link_to image_tag('icons/feed-icon-14x14.png', :alt => 'RSS Feed'), all_pages_rss_url
= form_tag all_pages_path, method: :get, class: 'form-search pull-right' do
= text_field_tag :name, params[:name], class: 'input-medium search-query',
placeholder: t('.search.placeholder')

View File

@ -0,0 +1,19 @@
xml.instruct! :xml, :version => "1.0"
xml.rss :version => "2.0" do
xml.channel do
xml.title FoodsoftConfig[:name] + " Wiki"
xml.description ""
xml.link FoodsoftConfig[:homepage]
for page in @pages
xml.item do
xml.title page.title
xml.description page.diff, :type => "html"
xml.author User.find(page.updated_by).display
xml.pubDate page.updated_at.to_s(:rfc822)
xml.link wiki_page_path(page.permalink)
xml.guid page.updated_at.to_i
end
end
end
end

View File

@ -19,6 +19,7 @@ Gem::Specification.new do |s|
s.add_dependency "rails", "~> 3.2.15"
s.add_dependency 'wikicloth'
s.add_dependency 'acts_as_versioned' # need git version, make sure that is included in foodsoft's Gemfile
s.add_dependency 'diffy'
s.add_dependency 'content_for_in_controllers'
s.add_development_dependency "sqlite3"
end

View File

@ -1,5 +1,6 @@
require 'wikicloth'
require 'acts_as_versioned'
require "diffy"
require 'foodsoft_wiki/engine'
module FoodsoftWiki

View File

@ -1,3 +1,5 @@
require 'stringio'
# put in here all foodsoft tasks
# => :environment loads the environment an gives easy access to the application
@ -23,8 +25,6 @@ module Colors
end
include Colors
require 'stringio'
namespace :foodsoft do
desc "Setup foodsoft"
task :setup_development do
@ -82,10 +82,11 @@ end
def setup_app_config
file = 'config/app_config.yml'
sample = Rails.root.join("#{file}.SAMPLE")
return nil if skip?(file)
puts yellow "Copying #{file}..."
%x( cp #{Rails.root.join("#{file}.SAMPLE")} #{Rails.root.join(file)} )
%x( cp #{sample} #{Rails.root.join(file)} )
reminder(file)
end
@ -120,6 +121,8 @@ def start_server
puts blue "Start your server running 'bundle exec rails s' and visit http://localhost:3000"
end
# Helper Methods
def ask(question, answers = false)
puts question
input = STDIN.gets.chomp
@ -149,3 +152,4 @@ def capture_stdout
ensure
$stdout = STDOUT
end

37
lib/token_verifier.rb Normal file
View File

@ -0,0 +1,37 @@
class TokenVerifier < ActiveSupport::MessageVerifier
def initialize(prefix)
super(self.class.secret)
@_prefix = prefix.is_a?(Array) ? prefix.join(':') : prefix.to_s
end
def generate(message=nil)
fullmessage = [FoodsoftConfig.scope, @_prefix]
fullmessage.append(message) unless message.nil?
super(fullmessage)
end
def verify(message)
r = super(message)
raise InvalidMessage unless r.is_a?(Array) and r.length >= 2 and r.length <= 3
raise InvalidScope unless r[0] == FoodsoftConfig.scope
raise InvalidPrefix unless r[1] == @_prefix
# return original message
if r.length > 2
r[2]
else
nil
end
end
class InvalidMessage < ActiveSupport::MessageVerifier::InvalidSignature; end
class InvalidScope < ActiveSupport::MessageVerifier::InvalidSignature; end
class InvalidPrefix < ActiveSupport::MessageVerifier::InvalidSignature; end
protected
def self.secret
Foodsoft::Application.config.secret_token
end
end

View File

@ -0,0 +1,44 @@
require_relative '../spec_helper'
describe TokenVerifier do
let (:prefix) { 'xyz' }
let (:v) { TokenVerifier.new(prefix) }
let (:msg) { v.generate }
it 'validates' do
expect{ v.verify(msg) }.to_not raise_error
end
it 'validates when recreated' do
v2 = TokenVerifier.new(prefix)
expect{ v2.verify(msg) }.to_not raise_error
end
it 'does not validate with a different prefix' do
v2 = TokenVerifier.new('abc')
expect { v2.verify(msg) }.to raise_error(TokenVerifier::InvalidPrefix)
end
it 'does not validate in a different foodcoop scope' do
msg
oldscope = FoodsoftConfig.scope
begin
FoodsoftConfig.scope = Faker::Lorem.words(1)
v2 = TokenVerifier.new(prefix)
expect{ v2.verify(msg) }.to raise_error(TokenVerifier::InvalidScope)
ensure
FoodsoftConfig.scope = oldscope
end
end
it 'does not validate a random string' do
expect{ v.verify(Faker::Lorem.characters(100)) }.to raise_error(ActiveSupport::MessageVerifier::InvalidSignature)
end
it 'returns the message' do
data = [5, {'hi' => :there}, 'bye', []]
msg = v.generate(data)
expect(v.verify(msg)).to eq data
end
end