Merge pull request #245 from fsmanuel/rss_feed + 1

This commit is contained in:
wvengen 2014-01-06 13:55:32 +01:00
commit 69a29b8296
16 changed files with 171 additions and 6 deletions

View file

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

View file

@ -87,6 +87,18 @@ class ApplicationController < ActionController::Base
end end
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. # Stores this controller instance as a thread local varibale to be accessible from outside ActionController/ActionView.
def store_controller def store_controller
Thread.current[:application_controller] = self Thread.current[:application_controller] = self

View file

@ -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: 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_denied_sign_in: sign in as another user
error_members_only: This action is only available to members of the group! error_members_only: This action is only available to members of the group!
error_token: Access denied (invalid token)!
article_categories: article_categories:
create: create:
notice: Category was stored 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 # encoding: utf-8
class PagesController < ApplicationController 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 def index
@page = Page.find_by_permalink "Home" @page = Page.find_by_permalink "Home"
@ -114,6 +122,10 @@ class PagesController < ApplicationController
end end
@pages.order(order) @pages.order(order)
end end
respond_to do |format|
format.html
format.rss { render :layout => false }
end
end end
def version def version

View file

@ -1,6 +1,10 @@
module PagesHelper module PagesHelper
include WikiCloth 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) def wikified_body(body, title = nil)
render_opts = {:locale => I18n.locale} # workaround for wikicloth 0.8.0 https://github.com/nricciar/wikicloth/pull/59 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 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 Array.new
end end
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 end

View file

@ -34,6 +34,24 @@ class Page < ActiveRecord::Base
end end
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 protected
def update_permalink def update_permalink

View file

@ -9,6 +9,7 @@
%li= link_to t('.recent_changes'), all_pages_path(:view => 'recent_changes') %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('.title_list'), all_pages_path(:view => 'title_list')
%li= link_to t('.site_map'), all_pages_path(:view => 'site_map') %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 = form_tag all_pages_path, method: :get, class: 'form-search pull-right' do
= text_field_tag :name, params[:name], class: 'input-medium search-query', = text_field_tag :name, params[:name], class: 'input-medium search-query',
placeholder: t('.search.placeholder') 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 "rails", "~> 3.2.15"
s.add_dependency 'wikicloth' 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 '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" s.add_development_dependency "sqlite3"
end end

View file

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

View file

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