Merge branch 'master' into feature-receive
Conflicts: app/helpers/finance/order_articles_helper.rb
This commit is contained in:
commit
b30b424540
26 changed files with 247 additions and 69 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -18,3 +18,4 @@ Capfile
|
||||||
config/deploy.rb
|
config/deploy.rb
|
||||||
config/deploy/*
|
config/deploy/*
|
||||||
.localeapp
|
.localeapp
|
||||||
|
.bundle
|
30
Gemfile.lock
30
Gemfile.lock
|
@ -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
|
||||||
|
|
||||||
|
@ -61,12 +63,12 @@ GEM
|
||||||
activerecord (>= 3.0.0)
|
activerecord (>= 3.0.0)
|
||||||
afm (0.2.0)
|
afm (0.2.0)
|
||||||
arel (3.0.3)
|
arel (3.0.3)
|
||||||
better_errors (1.0.1)
|
better_errors (1.1.0)
|
||||||
coderay (>= 1.0.0)
|
coderay (>= 1.0.0)
|
||||||
erubis (>= 2.6.6)
|
erubis (>= 2.6.6)
|
||||||
binding_of_caller (0.7.2)
|
binding_of_caller (0.7.2)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
bootstrap-datepicker-rails (1.1.1.10)
|
bootstrap-datepicker-rails (1.1.1.11)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
builder (3.0.4)
|
builder (3.0.4)
|
||||||
bullet (4.7.1)
|
bullet (4.7.1)
|
||||||
|
@ -80,7 +82,7 @@ GEM
|
||||||
net-ssh-gateway (>= 1.1.0)
|
net-ssh-gateway (>= 1.1.0)
|
||||||
capistrano-ext (1.2.1)
|
capistrano-ext (1.2.1)
|
||||||
capistrano (>= 1.0.0)
|
capistrano (>= 1.0.0)
|
||||||
capybara (2.2.0)
|
capybara (2.2.1)
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
nokogiri (>= 1.3.3)
|
nokogiri (>= 1.3.3)
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
|
@ -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)
|
||||||
|
@ -128,7 +132,7 @@ GEM
|
||||||
faker (1.2.0)
|
faker (1.2.0)
|
||||||
i18n (~> 0.5)
|
i18n (~> 0.5)
|
||||||
ffi (1.9.3)
|
ffi (1.9.3)
|
||||||
haml (4.0.4)
|
haml (4.0.5)
|
||||||
tilt
|
tilt
|
||||||
haml-rails (0.4)
|
haml-rails (0.4)
|
||||||
actionpack (>= 3.1, < 4.1)
|
actionpack (>= 3.1, < 4.1)
|
||||||
|
@ -203,8 +207,7 @@ GEM
|
||||||
polyamorous (0.5.0)
|
polyamorous (0.5.0)
|
||||||
activerecord (~> 3.0)
|
activerecord (~> 3.0)
|
||||||
polyglot (0.3.3)
|
polyglot (0.3.3)
|
||||||
prawn (0.13.0)
|
prawn (0.13.2)
|
||||||
afm
|
|
||||||
pdf-reader (~> 1.2)
|
pdf-reader (~> 1.2)
|
||||||
ruby-rc4
|
ruby-rc4
|
||||||
ttfunk (~> 1.0.3)
|
ttfunk (~> 1.0.3)
|
||||||
|
@ -241,7 +244,7 @@ GEM
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
rdoc (~> 3.4)
|
rdoc (~> 3.4)
|
||||||
thor (>= 0.14.6, < 2.0)
|
thor (>= 0.14.6, < 2.0)
|
||||||
rake (10.1.0)
|
rake (10.1.1)
|
||||||
rdoc (3.12.2)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
redis (3.0.6)
|
redis (3.0.6)
|
||||||
|
@ -258,6 +261,7 @@ GEM
|
||||||
vegas (~> 0.1.2)
|
vegas (~> 0.1.2)
|
||||||
rest-client (1.6.7)
|
rest-client (1.6.7)
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
|
rinku (1.7.3)
|
||||||
rspec (2.14.1)
|
rspec (2.14.1)
|
||||||
rspec-core (~> 2.14.0)
|
rspec-core (~> 2.14.0)
|
||||||
rspec-expectations (~> 2.14.0)
|
rspec-expectations (~> 2.14.0)
|
||||||
|
@ -266,8 +270,9 @@ GEM
|
||||||
rspec-expectations (2.14.4)
|
rspec-expectations (2.14.4)
|
||||||
diff-lcs (>= 1.1.3, < 2.0)
|
diff-lcs (>= 1.1.3, < 2.0)
|
||||||
rspec-mocks (2.14.4)
|
rspec-mocks (2.14.4)
|
||||||
rspec-rails (2.14.0)
|
rspec-rails (2.14.1)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
|
activemodel (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
rspec-core (~> 2.14.0)
|
rspec-core (~> 2.14.0)
|
||||||
|
@ -275,10 +280,10 @@ GEM
|
||||||
rspec-mocks (~> 2.14.0)
|
rspec-mocks (~> 2.14.0)
|
||||||
rspec-rerun (0.1.3)
|
rspec-rerun (0.1.3)
|
||||||
rspec (>= 2.11.0)
|
rspec (>= 2.11.0)
|
||||||
ruby-prof (0.13.1)
|
ruby-prof (0.14.2)
|
||||||
ruby-rc4 (0.1.5)
|
ruby-rc4 (0.1.5)
|
||||||
rubyzip (1.1.0)
|
rubyzip (1.1.0)
|
||||||
sass (3.2.12)
|
sass (3.2.13)
|
||||||
sass-rails (3.2.6)
|
sass-rails (3.2.6)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
sass (>= 3.1.10)
|
sass (>= 3.1.10)
|
||||||
|
@ -338,7 +343,7 @@ GEM
|
||||||
rails (>= 3.1)
|
rails (>= 3.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
tzinfo (0.3.38)
|
tzinfo (0.3.38)
|
||||||
uglifier (2.3.3)
|
uglifier (2.4.0)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
json (>= 1.8.0)
|
json (>= 1.8.0)
|
||||||
uniform_notifier (1.4.0)
|
uniform_notifier (1.4.0)
|
||||||
|
@ -348,9 +353,10 @@ GEM
|
||||||
whenever (0.9.0)
|
whenever (0.9.0)
|
||||||
activesupport (>= 2.3.4)
|
activesupport (>= 2.3.4)
|
||||||
chronic (>= 0.6.3)
|
chronic (>= 0.6.3)
|
||||||
wikicloth (0.8.0)
|
wikicloth (0.8.1)
|
||||||
builder
|
builder
|
||||||
expression_parser
|
expression_parser
|
||||||
|
rinku
|
||||||
xpath (2.0.0)
|
xpath (2.0.0)
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
FoodSoft
|
FoodSoft
|
||||||
=========
|
=========
|
||||||
[![Build Status](https://travis-ci.org/foodcoops/foodsoft.png)](https://travis-ci.org/foodcoops/foodsoft)
|
[![Build Status](https://travis-ci.org/foodcoops/foodsoft.png?branch=master)](https://travis-ci.org/foodcoops/foodsoft)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/foodcoops/foodsoft/badge.png?branch=master)](https://coveralls.io/r/foodcoops/foodsoft?branch=master)
|
||||||
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.png)](https://codeclimate.com/github/foodcoops/foodsoft)
|
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.png)](https://codeclimate.com/github/foodcoops/foodsoft)
|
||||||
[![Dependency Status](https://gemnasium.com/foodcoops/foodsoft.png)](https://gemnasium.com/foodcoops/foodsoft)
|
[![Dependency Status](https://gemnasium.com/foodcoops/foodsoft.png)](https://gemnasium.com/foodcoops/foodsoft)
|
||||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/foodcoops/foodsoft/trend.png)](https://bitdeli.com/foodcoops "Bitdeli Badge")
|
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/foodcoops/foodsoft/trend.png)](https://bitdeli.com/foodcoops "Bitdeli Badge")
|
||||||
|
|
|
@ -26,7 +26,7 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
def deny_access
|
def deny_access
|
||||||
session[:return_to] = request.original_url
|
session[:return_to] = request.original_url
|
||||||
redirect_to login_url, :alert => I18n.t('application.controller.error_denied')
|
redirect_to root_url, alert: I18n.t('application.controller.error_denied', sign_in: ActionController::Base.helpers.link_to(t('application.controller.error_denied_sign_in'), login_path))
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -92,6 +92,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
|
||||||
|
|
|
@ -28,6 +28,7 @@ class SessionsController < ApplicationController
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
session[:user_id] = nil
|
session[:user_id] = nil
|
||||||
|
session[:return_to] = nil
|
||||||
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
redirect_to login_url, :notice => I18n.t('sessions.logged_out')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,35 +7,14 @@
|
||||||
.modal-body
|
.modal-body
|
||||||
= f.input :availability
|
= f.input :availability
|
||||||
= f.input :name
|
= f.input :name
|
||||||
.fold-line
|
|
||||||
= f.input :unit_quantity, label: Article.human_attribute_name(:unit),
|
= render partial: 'shared/article_fields_units', locals: {f: f}
|
||||||
input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit_quantity)}
|
|
||||||
= f.input :unit, label: '×'.html_safe,
|
|
||||||
input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit)}
|
|
||||||
|
|
||||||
= f.input :note
|
= f.input :note
|
||||||
= f.association :article_category
|
= f.association :article_category
|
||||||
/ TODO labels
|
/ TODO labels
|
||||||
|
|
||||||
.fold-line
|
= render partial: 'shared/article_fields_price', locals: {f: f}
|
||||||
= f.input :price do
|
|
||||||
.input-prepend
|
|
||||||
%span.add-on= t 'number.currency.format.unit'
|
|
||||||
= f.input_field :price, class: 'input-mini'
|
|
||||||
= f.input :tax do
|
|
||||||
.input-append
|
|
||||||
= f.input_field :tax, class: 'input-mini'
|
|
||||||
%span.add-on %
|
|
||||||
.fold-line
|
|
||||||
= f.input :deposit do
|
|
||||||
.input-prepend
|
|
||||||
%span.add-on= t 'number.currency.format.unit'
|
|
||||||
= f.input_field :deposit, class: 'input-mini'
|
|
||||||
.control-group
|
|
||||||
%label.control-label{for: 'article_fc_price'}
|
|
||||||
= Article.human_attribute_name(:fc_price)
|
|
||||||
.controls.control-text#article_fc_price
|
|
||||||
= number_to_currency(@article.fc_price) rescue nil
|
|
||||||
|
|
||||||
= f.input :origin
|
= f.input :origin
|
||||||
= f.input :manufacturer
|
= f.input :manufacturer
|
||||||
|
@ -44,14 +23,3 @@
|
||||||
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
||||||
= f.submit class: 'btn btn-primary'
|
= f.submit class: 'btn btn-primary'
|
||||||
|
|
||||||
:javascript
|
|
||||||
var form = $('form.edit_article, form.new_article');
|
|
||||||
$('#article_price, #article_tax, #article_deposit', form).on('change keyup', function() {
|
|
||||||
var price = parseFloat($('#article_price', form).val());
|
|
||||||
var tax = parseFloat($('#article_tax', form).val());
|
|
||||||
var deposit = parseFloat($('#article_deposit', form).val());
|
|
||||||
// Article#gross_price and Article#fc_price
|
|
||||||
var gross_price = (price + deposit) * (tax / 100 + 1);
|
|
||||||
var fc_price = gross_price * (#{FoodsoftConfig[:price_markup].to_f} / 100 + 1);
|
|
||||||
$('#article_fc_price').html($.isNumeric(fc_price) ? I18n.l("currency", fc_price) : '…');
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- title t('.title')
|
- title t('.title')
|
||||||
= raw t('.body', user: h(show_user(@user)))
|
= raw t('.body', user: h(show_user(@user)))
|
||||||
= simple_form_for @user, :url => update_password_path(@user.id, :token => @user.reset_password_token) do |form|
|
= simple_form_for @user, :url => {:action => 'update_password', :id => @user.id, :token => @user.reset_password_token} do |form|
|
||||||
= form.input :password
|
= form.input :password
|
||||||
= form.input :password_confirmation
|
= form.input :password_confirmation
|
||||||
.form-actions
|
.form-actions
|
||||||
|
|
|
@ -17,18 +17,16 @@
|
||||||
|
|
||||||
= simple_fields_for :article, @order_article.article do |f|
|
= simple_fields_for :article, @order_article.article do |f|
|
||||||
= f.input :name
|
= f.input :name
|
||||||
= f.input :order_number
|
|
||||||
= f.input :unit
|
|
||||||
|
|
||||||
- if @order_article.article.is_a?(StockArticle)
|
- if @order_article.article.is_a?(StockArticle)
|
||||||
%div.alert= t '.stock_alert'
|
%div.alert= t '.stock_alert'
|
||||||
- else
|
- else
|
||||||
= simple_fields_for :article_price, @order_article.article_price do |f|
|
= simple_fields_for :article_price, @order_article.article_price do |fprice|
|
||||||
= f.input :unit_quantity
|
= render partial: 'shared/article_fields_units', locals: {f_unit: f, f_uq: fprice}
|
||||||
= f.input :price
|
= render partial: 'shared/article_fields_price', locals: {f: fprice}
|
||||||
= f.input :tax
|
|
||||||
= f.input :deposit
|
|
||||||
= form.input :update_current_price, as: :boolean
|
= form.input :update_current_price, as: :boolean
|
||||||
|
= f.input :order_number
|
||||||
.modal-footer
|
.modal-footer
|
||||||
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
|
||||||
= form.submit class: 'btn btn-primary'
|
= form.submit class: 'btn btn-primary'
|
||||||
|
|
34
app/views/shared/_article_fields_price.html.haml
Normal file
34
app/views/shared/_article_fields_price.html.haml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
.fold-line
|
||||||
|
= f.input :price do
|
||||||
|
.input-prepend
|
||||||
|
%span.add-on= t 'number.currency.format.unit'
|
||||||
|
= f.input_field :price, class: 'input-mini'
|
||||||
|
= f.input :tax do
|
||||||
|
.input-append
|
||||||
|
= f.input_field :tax, class: 'input-mini'
|
||||||
|
%span.add-on %
|
||||||
|
.fold-line
|
||||||
|
= f.input :deposit do
|
||||||
|
.input-prepend
|
||||||
|
%span.add-on= t 'number.currency.format.unit'
|
||||||
|
= f.input_field :deposit, class: 'input-mini'
|
||||||
|
.control-group
|
||||||
|
%label.control-label{for: 'article_fc_price'}
|
||||||
|
= Article.human_attribute_name(:fc_price)
|
||||||
|
.controls.control-text#article_fc_price
|
||||||
|
= number_to_currency(f.object.fc_price) rescue nil
|
||||||
|
|
||||||
|
-# do this inline, since it's being used in ajax forms only
|
||||||
|
- field = f.object.class.model_name.underscore
|
||||||
|
:javascript
|
||||||
|
var form = $('#article_fc_price').closest('form');
|
||||||
|
$('##{field}_price, ##{field}_tax, ##{field}_deposit', form).on('change keyup', function() {
|
||||||
|
var price = parseFloat($('##{field}_price', form).val());
|
||||||
|
var tax = parseFloat($('##{field}_tax', form).val());
|
||||||
|
var deposit = parseFloat($('##{field}_deposit', form).val());
|
||||||
|
// Article#gross_price and Article#fc_price
|
||||||
|
var gross_price = (price + deposit) * (tax / 100 + 1);
|
||||||
|
var fc_price = gross_price * (#{FoodsoftConfig[:price_markup].to_f} / 100 + 1);
|
||||||
|
$('#article_fc_price').html($.isNumeric(fc_price) ? I18n.l("currency", fc_price) : '…');
|
||||||
|
});
|
||||||
|
|
6
app/views/shared/_article_fields_units.html.haml
Normal file
6
app/views/shared/_article_fields_units.html.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
-# use the local 'f', or supply 'f_uq' and 'f_unit' for more control (like in balancing)
|
||||||
|
.fold-line
|
||||||
|
= (f_uq rescue f).input :unit_quantity, label: Article.human_attribute_name(:unit),
|
||||||
|
input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit_quantity)}
|
||||||
|
= (f_unit rescue f).input :unit, label: '×'.html_safe,
|
||||||
|
input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit)}
|
|
@ -281,7 +281,8 @@ de:
|
||||||
application:
|
application:
|
||||||
controller:
|
controller:
|
||||||
error_authn: Anmeldung erforderlich!
|
error_authn: Anmeldung erforderlich!
|
||||||
error_denied: Kein Zugriff!
|
error_denied: Du darfst die gewünschte Seite nicht sehen. Wenn Du denkst, dass Du dürfen solltest, frage eine Administratorin, dass sie Dir die entsprechenden Rechte einräumt. Falls Du Zugang zu mehreren Benutzerkonten hast, möchtest Du Dich vielleicht %{sign_in}.
|
||||||
|
error_denied_sign_in: als ein anderer Benutzer anmelden
|
||||||
error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!
|
error_members_only: Diese Aktion ist nur für Mitglieder der Gruppe erlaubt!
|
||||||
article_categories:
|
article_categories:
|
||||||
create:
|
create:
|
||||||
|
|
|
@ -287,8 +287,10 @@ en:
|
||||||
application:
|
application:
|
||||||
controller:
|
controller:
|
||||||
error_authn: Authentication required!
|
error_authn: Authentication required!
|
||||||
error_denied: Access denied!
|
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_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
|
||||||
|
|
|
@ -21,7 +21,7 @@ Foodsoft::Application.routes.draw do
|
||||||
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
|
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
|
||||||
post '/login/reset_password' => 'login#reset_password', as: :reset_password
|
post '/login/reset_password' => 'login#reset_password', as: :reset_password
|
||||||
get '/login/new_password' => 'login#new_password', as: :new_password
|
get '/login/new_password' => 'login#new_password', as: :new_password
|
||||||
get '/login/update_password' => 'login#update_password', as: :update_password
|
put '/login/update_password' => 'login#update_password', as: :update_password
|
||||||
match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation
|
match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation
|
||||||
resources :sessions, :only => [:new, :create, :destroy]
|
resources :sessions, :only => [:new, :create, :destroy]
|
||||||
|
|
||||||
|
|
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
|
# 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
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 "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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
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