Merge pull request #245 from fsmanuel/rss_feed + 1
This commit is contained in:
commit
69a29b8296
16 changed files with 171 additions and 6 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
= csrf_meta_tags
|
||||
= stylesheet_link_tag "application", :media => "all"
|
||||
//%link(href="images/favicon.ico" rel="shortcut icon")
|
||||
|
||||
|
||||
= yield(:head)
|
||||
|
||||
%body
|
||||
|
|
|
@ -277,6 +277,7 @@ en:
|
|||
error_denied: You are not allowed to view the requested page. If you think you should, ask an administrator to give you appropriate permissions. If you have access to multiple user accounts, you might want to %{sign_in}.
|
||||
error_denied_sign_in: sign in as another user
|
||||
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
|
||||
|
|
BIN
lib/foodsoft_wiki/app/assets/images/icons/feed-icon-14x14.png
Executable file
BIN
lib/foodsoft_wiki/app/assets/images/icons/feed-icon-14x14.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 689 B |
BIN
lib/foodsoft_wiki/app/assets/images/icons/feed-icon-28x28.png
Executable file
BIN
lib/foodsoft_wiki/app/assets/images/icons/feed-icon-28x28.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -59,4 +63,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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
19
lib/foodsoft_wiki/app/views/pages/all.rss.builder
Normal file
19
lib/foodsoft_wiki/app/views/pages/all.rss.builder
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
require 'wikicloth'
|
||||
require 'acts_as_versioned'
|
||||
require 'diffy'
|
||||
require 'content_for_in_controllers'
|
||||
require 'foodsoft_wiki/engine'
|
||||
|
||||
module FoodsoftWiki
|
||||
|
|
|
@ -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
37
lib/token_verifier.rb
Normal 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
|
44
spec/lib/token_verifier_spec.rb
Normal file
44
spec/lib/token_verifier_spec.rb
Normal 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
|
Loading…
Reference in a new issue