From 0d0ff90a6419fc6d759a7c2c5bd69e51d9b70f93 Mon Sep 17 00:00:00 2001 From: Manuel Wiedenmann Date: Sat, 4 Jan 2014 20:12:01 +0100 Subject: [PATCH 1/2] adds an RSS feed for pages/all --- Gemfile.lock | 4 ++ app/controllers/application_controller.rb | 14 +++++- app/views/layouts/_header.html.haml | 2 +- config/locales/en.yml | 1 + .../assets/images/icons/feed-icon-14x14.png | Bin 0 -> 689 bytes .../assets/images/icons/feed-icon-28x28.png | Bin 0 -> 1737 bytes .../app/controllers/pages_controller.rb | 12 +++++ lib/foodsoft_wiki/app/helpers/pages_helper.rb | 10 ++++ lib/foodsoft_wiki/app/models/page.rb | 18 +++++++ .../app/views/pages/all.html.haml | 1 + .../app/views/pages/all.rss.builder | 19 ++++++++ lib/foodsoft_wiki/foodsoft_wiki.gemspec | 3 +- lib/foodsoft_wiki/lib/foodsoft_wiki.rb | 1 + lib/tasks/foodsoft_setup.rake | 10 ++-- lib/token_verifier.rb | 37 +++++++++++++++ spec/lib/token_verifier_spec.rb | 44 ++++++++++++++++++ 16 files changed, 170 insertions(+), 6 deletions(-) create mode 100755 lib/foodsoft_wiki/app/assets/images/icons/feed-icon-14x14.png create mode 100755 lib/foodsoft_wiki/app/assets/images/icons/feed-icon-28x28.png create mode 100644 lib/foodsoft_wiki/app/views/pages/all.rss.builder create mode 100644 lib/token_verifier.rb create mode 100644 spec/lib/token_verifier_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 291014eb..aec872f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9c77fe48..95d27b00 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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 diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index ab2e7d8e..06920588 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -8,7 +8,7 @@ = csrf_meta_tags = stylesheet_link_tag "application", :media => "all" //%link(href="images/favicon.ico" rel="shortcut icon") - + = yield(:head) %body diff --git a/config/locales/en.yml b/config/locales/en.yml index 9d6005bc..54d2c82f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/lib/foodsoft_wiki/app/assets/images/icons/feed-icon-14x14.png b/lib/foodsoft_wiki/app/assets/images/icons/feed-icon-14x14.png new file mode 100755 index 0000000000000000000000000000000000000000..b3c949d2244f2c0c81d65e74719af2a1b56d06a3 GIT binary patch literal 689 zcmV;i0#5yjP)(tky!*UETcH-TCU7SrqEjJM#?B`_A)!p7(kFf9-P@=@15kkTkGK zgFusyy#KECqZzRdBLb=P?$(kUP;>kYTDeG&{|a+iOiRbI6nbQ)j#7bOf>iF=C+|_py<&Fo1F5cC*iEM?zZGC{ejNg4LWYp=S$L6Qaby6y zp$+F`250{%tU{Lg$5*ROH}y!1UKJS4*xqd7P(Y3JQF?lrnf?yerr%&6yGXLG1ur*B z{$&R1@Oj)yl@%rY5rh?j(j10Yz_DBs`AKFU_QnB;)(aqQmGi&ieOS|21^NP9UMpa< zU&p!f6RZ6Owp^X!EXA=0SbN&h?CrQK%Q3(=YBqqHD^9ZUM0Hxt-6-KT;>lf@j?Z+v zHm(}`>85I&E<7e}oz?6UwjAogowzGO8kSN7+2`b^$Az9L{K5*ko87EV45LT-`_##3 z>d3AGh@>=mbg34|6}+-gT9N+6Dr@44VEl44O&{&|w=qpbzC#iWMKa?5)>tI+KLQK@ Xq0QFqn(9Yl00000NkvXXu0mjfZ8tsAF&N>2z!Fdr4Imm26om*2a>%jE0(;Kv%yf78|JB{oGfPNxX8x(_?yCCg`;V_$ zXsz+vi(@PROR2wt+9on+`tt9tz79KlSO2H>IyGO>#kRR$cU=`Hmyc#J&lVDyanqor z1tA1LcZEeQ^@U{`(^^*e%-kna7Wft+z>JVkoMv=$6&UWDkQ$<2lKHssGAf!lTvqVr4)B+FQFUkn06c#WX1OxA_5 z;;t4x27iQdkQg)F7+O{!f8h`chd+YqxfC)gbK_tz9fLAfFeIEn@r7e^t8mW`EtHtl zf~sX-Ks>w(zD$JfL<}?-ObbNSqyoSo8EhI@Me*$0_W}BkY=u=l5_at*Bqz>Bs-YQ{ zos4mvG?H^x!JY63{2$(dmAw(OgDBN4r@D?n>1$kSvMo%1n@ne~L*#Ej+@`@-enjuF z&(K|SKhjN0k!f56nXE1WtNKQG3pT>t|2pJAM+hc@!uQ#Vl>I(@D{D3pxKswlb>~tD zb^TOW4Rc`CjDzjiNDWa)=dPf5=zVl-eHJ}`?5*&!96k~1@ekpr?gI+6@IV)I-%YZ1 z=-v+aWGLp%CKUe#s$sz;wpp^;mX)Y(TnOzI;dNa_{>&cebNk>B1K00F_Tam4Z(qQO zd23_Pq2no`*2DW|3GJHdIgFd8T4NJhnrn;yjGBaL&ff6-AOJ$zP&E^gesm2|&uoXB zvj$EwiHzuj`}0P$f4kO{ObL|Si{@9rxo7|l7_S!?%oAqFYC^>-3Q9eduY7-u)FfIoCHc?ZV)@-|{<8PO#m0!q@K`h=j&~-o;Qk3aNwz=j=|n zt*@f@(}n2RJRjZ1wpX$pMoIGICP*h?K<~6}fS2ozSx$Zo7}Z$8j=^Jwto#h>^+V-5 z;CNj~*)B4qf2rJg3h)YcC8lmRCX=+ze4R)W{Rjv-OrYtP6i`tkW#ZBZo zAquzQ)1pNRc%A~zt)gVgBrSNqe~I>8uMwtO7Oxi|M?C$Fs^Ts74UpfJM^h3sV?wMW0WJehh2{~g4ogxB$XB+z3 zj>dqsQ-JDmG0PG|RvIr$F{C4kCg$jP$}HFbr*4{YjY!qNUc40!F-=kycOU+QnB?Mx z8|Kk4n-6^So&Tjm@IeU{f$7jOq0(5vX+iOD$&HEn>6Ln&9A*v|n??7|MdU7@C{vjR z$f_|A5hO^O4ut*#ZxgtJGkT`cL-*i%v!F$w>{{>2ab(X< zB7{67ze4nP(?WE=r;}pbYUptK=i7b_Vo0DObgB4h*)ZMw#4Moef;n1P)m$$SapFyk zgn28|VG$Q8#!==OS!68?6Qk&A<31f;^D`aBP}NOF+u83;43FmqXr@XA^@G5zL#R|Z z-6OEbB$o=0VUJMXVpoM`Q5^X>w^j&dHb$?C`9O1za}2k1oIVYauA>==_^=F>L6=L^ zy)3G=Vq5~I&uoz(IUI+sizYAj3Qn3MPY>NxTc2EAJ9P$ :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 diff --git a/lib/foodsoft_wiki/app/helpers/pages_helper.rb b/lib/foodsoft_wiki/app/helpers/pages_helper.rb index 552791f3..704db941 100644 --- a/lib/foodsoft_wiki/app/helpers/pages_helper.rb +++ b/lib/foodsoft_wiki/app/helpers/pages_helper.rb @@ -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 diff --git a/lib/foodsoft_wiki/app/models/page.rb b/lib/foodsoft_wiki/app/models/page.rb index 3dfba7aa..7759effe 100644 --- a/lib/foodsoft_wiki/app/models/page.rb +++ b/lib/foodsoft_wiki/app/models/page.rb @@ -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}
" unless line.chomp == "+" + when /^-/ then o += "#{line.chomp}
" unless line.chomp == "-" + end + end + o + else + current.body + end + end protected diff --git a/lib/foodsoft_wiki/app/views/pages/all.html.haml b/lib/foodsoft_wiki/app/views/pages/all.html.haml index bb57e33c..a3a5ea35 100644 --- a/lib/foodsoft_wiki/app/views/pages/all.html.haml +++ b/lib/foodsoft_wiki/app/views/pages/all.html.haml @@ -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') diff --git a/lib/foodsoft_wiki/app/views/pages/all.rss.builder b/lib/foodsoft_wiki/app/views/pages/all.rss.builder new file mode 100644 index 00000000..35581c72 --- /dev/null +++ b/lib/foodsoft_wiki/app/views/pages/all.rss.builder @@ -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 \ No newline at end of file diff --git a/lib/foodsoft_wiki/foodsoft_wiki.gemspec b/lib/foodsoft_wiki/foodsoft_wiki.gemspec index 937a887f..f3c50662 100644 --- a/lib/foodsoft_wiki/foodsoft_wiki.gemspec +++ b/lib/foodsoft_wiki/foodsoft_wiki.gemspec @@ -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 diff --git a/lib/foodsoft_wiki/lib/foodsoft_wiki.rb b/lib/foodsoft_wiki/lib/foodsoft_wiki.rb index acf5ef50..84407363 100644 --- a/lib/foodsoft_wiki/lib/foodsoft_wiki.rb +++ b/lib/foodsoft_wiki/lib/foodsoft_wiki.rb @@ -1,5 +1,6 @@ require 'wikicloth' require 'acts_as_versioned' +require "diffy" require 'foodsoft_wiki/engine' module FoodsoftWiki diff --git a/lib/tasks/foodsoft_setup.rake b/lib/tasks/foodsoft_setup.rake index 5e7ba088..13c91ac2 100644 --- a/lib/tasks/foodsoft_setup.rake +++ b/lib/tasks/foodsoft_setup.rake @@ -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 + diff --git a/lib/token_verifier.rb b/lib/token_verifier.rb new file mode 100644 index 00000000..248a3ee8 --- /dev/null +++ b/lib/token_verifier.rb @@ -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 diff --git a/spec/lib/token_verifier_spec.rb b/spec/lib/token_verifier_spec.rb new file mode 100644 index 00000000..d398a140 --- /dev/null +++ b/spec/lib/token_verifier_spec.rb @@ -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 From fe111a1422714dc2576de087caf6708e36c24fdd Mon Sep 17 00:00:00 2001 From: wvengen Date: Mon, 6 Jan 2014 13:54:56 +0100 Subject: [PATCH 2/2] require gem in plugin --- lib/foodsoft_wiki/lib/foodsoft_wiki.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/foodsoft_wiki/lib/foodsoft_wiki.rb b/lib/foodsoft_wiki/lib/foodsoft_wiki.rb index 84407363..ddcb0f83 100644 --- a/lib/foodsoft_wiki/lib/foodsoft_wiki.rb +++ b/lib/foodsoft_wiki/lib/foodsoft_wiki.rb @@ -1,6 +1,7 @@ require 'wikicloth' require 'acts_as_versioned' -require "diffy" +require 'diffy' +require 'content_for_in_controllers' require 'foodsoft_wiki/engine' module FoodsoftWiki