1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

Merge branch 'historysettings' into dev

This commit is contained in:
Sylvain 2018-12-18 10:56:00 +01:00
commit a23e514935
17 changed files with 669 additions and 340 deletions

View File

@ -1,8 +1,10 @@
# Changelog Fab Manager
- Refactored subscriptions to keep track of the previous ones
- Refactored settings to keep track of the previous values (notably VAT rate)
- Improved automated tests suite
- Added Rubocop gem to the Gemfile
- Added Rubocop gem to the Gemfile (ruby syntax checking)
- Added badges to README
- Fix a security update: dependency ActiveJob < 4.2.11 has a vulnerability as described in [CVE-2018-16476](https://nvd.nist.gov/vuln/detail/CVE-2018-16476)
- [TODO DEPLOY] `rake db:migrate`
- [TODO DEPLOY] `bundle install`

View File

@ -2,6 +2,9 @@
FabManager is the FabLab management solution. It is web-based, open-source and totally free.
[![Coverage Status](https://coveralls.io/repos/github/LaCasemate/fab-manager/badge.svg)](https://coveralls.io/github/LaCasemate/fab-manager)
[![Docker pulls](https://img.shields.io/docker/pulls/sleede/fab-manager.svg)](https://hub.docker.com/r/sleede/fab-manager/)
[![Docker Build Status](https://img.shields.io/docker/build/sleede/fab-manager.svg)](https://hub.docker.com/r/sleede/fab-manager/builds)
##### Table of Contents
1. [Software stack](#software-stack)
@ -328,6 +331,7 @@ This can be achieved doing the following:
- `app/controllers/api/members_controllers.rb@search` is using `f_unaccent()` (see above) and `regexp_replace()`
- `db/migrate/20150604131525_add_meta_data_to_notifications.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20160915105234_add_transformation_to_o_auth2_mapping.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html), a PostgreSQL 9.4+ datatype.
- `db/migrate/20181217103441_migrate_settings_value_to_history_values.rb` is using `SELECT DISTINCT ON`.
- If you intend to contribute to the project code, you will need to run the test suite with `rake test`.
This also requires your user to have the _SUPERUSER_ role.
Please see the [known issues](#known-issues) section for more information about this.
@ -520,14 +524,14 @@ Developers may find information on how to implement their own authentication pro
Error:
...
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "..." violates foreign key constraint "fk_rails_..."
DETAIL: Key (group_id)=(1) is not present in table "groups".
DETAIL: Key (group_id)=(1) is not present in table "...".
: ...
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:11:in `block in transaction'
test_after_commit (1.0.0) lib/test_after_commit/database_statements.rb:5:in `transaction'
This is due to an ActiveRecord behavior witch disable referential integrity in PostgreSQL to load the fixtures.
PostgreSQL will prevent any users to disable referential integrity on the fly if they doesn't have the `SUPERUSER` role.
To fix that, logon as the `postgres` user and run the PostgreSQL shell (see [Setup the FabManager database in PostgreSQL](#setup-fabmanager-in-postgresql) for an example).
To fix that, logon as the `postgres` user and run the PostgreSQL shell (see [the dedicated section](#run-postgresql-cli) for instructions).
Then, run the following command (replace `sleede` with your test database user, as specified in your database.yml):
ALTER ROLE sleede WITH SUPERUSER;

View File

@ -2,8 +2,8 @@ class API::AvailabilitiesController < API::ApiController
include FablabConfiguration
before_action :authenticate_user!, except: [:public]
before_action :set_availability, only: [:show, :update, :destroy, :reservations, :lock]
before_action :define_max_visibility, only: [:machine, :trainings, :spaces]
before_action :set_availability, only: %i[show update destroy reservations lock]
before_action :define_max_visibility, only: %i[machine trainings spaces]
respond_to :json
def index
@ -27,29 +27,31 @@ class API::AvailabilitiesController < API::ApiController
# request for 1 single day
if in_same_day(start_date, end_date)
# trainings, events
@training_and_event_availabilities = Availability.includes(:tags, :trainings, :event, :slots).where(available_type: %w(training event))
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
.where(lock: false)
@training_and_event_availabilities = Availability.includes(:tags, :trainings, :event, :slots)
.where(available_type: %w[training event])
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
.where(lock: false)
# machines
@machine_availabilities = Availability.includes(:tags, :machines).where(available_type: 'machines')
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
.where(lock: false)
@machine_availabilities = Availability.includes(:tags, :machines)
.where(available_type: 'machines')
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
.where(lock: false)
@machine_slots = []
@machine_availabilities.each do |a|
a.machines.each do |machine|
if params[:m] and params[:m].include?(machine.id.to_s)
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
machine: machine,
title: machine.name
)
slot = verify_machine_is_reserved(slot, @reservations, current_user, '')
@machine_slots << slot
end
next unless params[:m]&.include?(machine.id.to_s)
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
machine: machine,
title: machine.name
)
slot = verify_machine_is_reserved(slot, @reservations, current_user, '')
@machine_slots << slot
end
end
end
@ -59,26 +61,24 @@ class API::AvailabilitiesController < API::ApiController
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
.where(lock: false)
if params[:s]
@space_availabilities.where(available_id: params[:s])
end
@space_availabilities.where(available_id: params[:s]) if params[:s]
@space_slots = []
@space_availabilities.each do |a|
space = a.spaces.first
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
space: space,
title: space.name
)
slot = verify_space_is_reserved(slot, @reservations, current_user, '')
@space_slots << slot
end
next unless (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
space: space,
title: space.name
)
slot = verify_space_is_reserved(slot, @reservations, current_user, '')
@space_slots << slot
end
end
@availabilities = [].concat(@training_and_event_availabilities).concat(@machine_slots).concat(@space_slots)
@ -134,15 +134,18 @@ class API::AvailabilitiesController < API::ApiController
end
def machine
if params[:member_id]
@user = User.find(params[:member_id])
else
@user = current_user
end
@user = if params[:member_id]
User.find(params[:member_id])
else
current_user
end
@current_user_role = current_user.is_admin? ? 'admin' : 'user'
@machine = Machine.friendly.find(params[:machine_id])
@slots = []
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at > ?', Time.now)
@reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id)
.includes(:slots, user: [:profile])
.references(:slots, :user)
.where('slots.start_at > ?', Time.now)
if @user.is_admin?
@availabilities = @machine.availabilities.includes(:tags)
.where("end_at > ? AND available_type = 'machines'", Time.now)
@ -150,34 +153,36 @@ class API::AvailabilitiesController < API::ApiController
else
end_at = @visi_max_other
end_at = @visi_max_year if is_subscription_year(@user)
@availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at)
.where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
.where(lock: false)
@availabilities = @machine.availabilities
.includes(:tags)
.where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at)
.where('availability_tags.tag_id' => @user.tag_ids.concat([nil]))
.where(lock: false)
end
@availabilities.each do |a|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
machine: @machine,
title: ''
)
slot = verify_machine_is_reserved(slot, @reservations, current_user, @current_user_role)
@slots << slot
end
next unless (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
machine: @machine,
title: ''
)
slot = verify_machine_is_reserved(slot, @reservations, current_user, @current_user_role)
@slots << slot
end
end
end
def trainings
if params[:member_id]
@user = User.find(params[:member_id])
else
@user = current_user
end
@user = if params[:member_id]
User.find(params[:member_id])
else
current_user
end
@slots = []
# first, we get the already-made reservations
@ -186,13 +191,13 @@ class API::AvailabilitiesController < API::ApiController
@reservations = @reservations.joins(:slots).where('slots.start_at > ?', Time.now)
# what is requested?
# 1) a single training
if params[:training_id].is_number? or (params[:training_id].length > 0 and params[:training_id] != 'all')
@availabilities = Training.friendly.find(params[:training_id]).availabilities
# 2) all trainings
else
@availabilities = Availability.trainings
end
# 1) a single training
@availabilities = if params[:training_id].is_number? or (params[:training_id].length > 0 and params[:training_id] != 'all')
Training.friendly.find(params[:training_id]).availabilities
# 2) all trainings
else
Availability.trainings
end
# who made the request?
# 1) an admin (he can see all future availabilities)
@ -217,11 +222,11 @@ class API::AvailabilitiesController < API::ApiController
end
def spaces
if params[:member_id]
@user = User.find(params[:member_id])
else
@user = current_user
end
@user = if params[:member_id]
User.find(params[:member_id])
else
current_user
end
@current_user_role = current_user.is_admin? ? 'admin' : 'user'
@space = Space.friendly.find(params[:space_id])
@slots = []
@ -242,18 +247,18 @@ class API::AvailabilitiesController < API::ApiController
end
@availabilities.each do |a|
((a.end_at - a.start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
if (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
space: @space,
title: ''
)
slot = verify_space_is_reserved(slot, @reservations, @user, @current_user_role)
@slots << slot
end
next unless (a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes) > Time.now
slot = Slot.new(
start_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes,
end_at: a.start_at + (i*ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes,
availability_id: a.id,
availability: a,
space: @space,
title: ''
)
slot = verify_space_is_reserved(slot, @reservations, @user, @current_user_role)
@slots << slot
end
end
@slots.each do |s|
@ -271,16 +276,19 @@ class API::AvailabilitiesController < API::ApiController
def export_availabilities
authorize :export
export = Export.where({category:'availabilities', export_type: 'index'}).where('created_at > ?', Availability.maximum('updated_at')).last
export = Export.where(category: 'availabilities', export_type: 'index')
.where('created_at > ?', Availability.maximum('updated_at')).last
if export.nil? || !FileTest.exist?(export.file)
@export = Export.new({category:'availabilities', export_type: 'index', user: current_user})
@export = Export.new(category: 'availabilities', export_type: 'index', user: current_user)
if @export.save
render json: {export_id: @export.id}, status: :ok
else
render json: @export.errors, status: :unprocessable_entity
end
else
send_file File.join(Rails.root, export.file), :type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', :disposition => 'attachment'
send_file File.join(Rails.root, export.file),
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
disposition: 'attachment'
end
end
@ -294,151 +302,156 @@ class API::AvailabilitiesController < API::ApiController
end
private
def set_availability
@availability = Availability.find(params[:id])
end
def availability_params
params.require(:availability).permit(:start_at, :end_at, :available_type, :machine_ids, :training_ids, :nb_total_places, machine_ids: [], training_ids: [], space_ids: [], tag_ids: [],
:machines_attributes => [:id, :_destroy])
end
def set_availability
@availability = Availability.find(params[:id])
end
def lock_params
params.require(:lock)
end
def availability_params
params.require(:availability).permit(:start_at, :end_at, :available_type, :machine_ids, :training_ids, :nb_total_places,
machine_ids: [], training_ids: [], space_ids: [], tag_ids: [],
machines_attributes: %i[id _destroy])
end
def is_reserved_availability(availability, user)
if user
reserved_slots = []
availability.slots.each do |s|
if s.canceled_at.nil?
reserved_slots << s
end
def lock_params
params.require(:lock)
end
def is_reserved_availability(availability, user)
if user
reserved_slots = []
availability.slots.each do |s|
if s.canceled_at.nil?
reserved_slots << s
end
reserved_slots.map(&:reservations).flatten.map(&:user_id).include? user.id
end
reserved_slots.map(&:reservations).flatten.map(&:user_id).include? user.id
else
false
end
end
def is_reserved(start_at, reservations)
is_reserved = false
reservations.each do |r|
r.slots.each do |s|
is_reserved = true if s.start_at == start_at
end
end
is_reserved
end
def verify_machine_is_reserved(slot, reservations, user, user_role)
show_name = (user_role == 'admin' or Setting.find_by(name: 'display_name_enable').value == 'true')
reservations.each do |r|
r.slots.each do |s|
next unless slot.machine.id == r.reservable_id
if s.start_at == slot.start_at and s.canceled_at == nil
slot.id = s.id
slot.is_reserved = true
slot.title = "#{slot.machine.name} - #{show_name ? r.user.profile.full_name : t('availabilities.not_available')}"
slot.can_modify = true if user_role === 'admin'
slot.reservations.push r
end
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
slot.title = "#{slot.machine.name} - #{t('availabilities.i_ve_reserved')}"
slot.can_modify = true
slot.is_reserved_by_current_user = true
end
end
end
slot
end
def verify_space_is_reserved(slot, reservations, user, user_role)
reservations.each do |r|
r.slots.each do |s|
next unless slot.space.id == r.reservable_id
if s.start_at == slot.start_at and s.canceled_at == nil
slot.can_modify = true if user_role === 'admin'
slot.reservations.push r
end
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
slot.id = s.id
slot.title = t('availabilities.i_ve_reserved')
slot.can_modify = true
slot.is_reserved = true
end
end
end
slot
end
def verify_training_event_is_reserved(availability, reservations, user)
reservations.each do |r|
r.slots.each do |s|
next unless (
(availability.available_type == 'training' && availability.trainings.first.id == r.reservable_id) ||
(availability.available_type == 'event' && availability.event.id == r.reservable_id)
) && s.start_at == availability.start_at && s.canceled_at == nil
availability.slot_id = s.id
if r.user == user
availability.is_reserved = true
availability.can_modify = true
end
end
end
availability
end
def can_show_slot_plus_three_months(user)
# member must have validated at least 1 training and must have a valid yearly subscription.
user.trainings.size > 0 and is_subscription_year(user)
end
def is_subscription_year(user)
user.subscription and user.subscription.plan.interval == 'year' and user.subscription.expired_at >= Time.now
end
def in_same_day(start_date, end_date)
(end_date.to_date - start_date.to_date).to_i == 1
end
def filter_availabilites(availabilities)
availabilities_filtered = []
availabilities.to_ary.each do |a|
# machine slot
if !a.try(:available_type)
availabilities_filtered << a
else
false
end
end
def is_reserved(start_at, reservations)
is_reserved = false
reservations.each do |r|
r.slots.each do |s|
is_reserved = true if s.start_at == start_at
end
end
is_reserved
end
def verify_machine_is_reserved(slot, reservations, user, user_role)
show_name = (user_role == 'admin' or Setting.find_by(name: 'display_name_enable').value == 'true')
reservations.each do |r|
r.slots.each do |s|
if slot.machine.id == r.reservable_id
if s.start_at == slot.start_at and s.canceled_at == nil
slot.id = s.id
slot.is_reserved = true
slot.title = "#{slot.machine.name} - #{show_name ? r.user.profile.full_name : t('availabilities.not_available')}"
slot.can_modify = true if user_role === 'admin'
slot.reservations.push r
end
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
slot.title = "#{slot.machine.name} - #{t('availabilities.i_ve_reserved')}"
slot.can_modify = true
slot.is_reserved_by_current_user = true
end
end
end
end
slot
end
def verify_space_is_reserved(slot, reservations, user, user_role)
reservations.each do |r|
r.slots.each do |s|
if slot.space.id == r.reservable_id
if s.start_at == slot.start_at and s.canceled_at == nil
slot.can_modify = true if user_role === 'admin'
slot.reservations.push r
end
if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil
slot.id = s.id
slot.title = t('availabilities.i_ve_reserved')
slot.can_modify = true
slot.is_reserved = true
end
end
end
end
slot
end
def verify_training_event_is_reserved(availability, reservations, user)
reservations.each do |r|
r.slots.each do |s|
if ((availability.available_type == 'training' and availability.trainings.first.id == r.reservable_id) or (availability.available_type == 'event' and availability.event.id == r.reservable_id)) and s.start_at == availability.start_at and s.canceled_at == nil
availability.slot_id = s.id
if r.user == user
availability.is_reserved = true
availability.can_modify = true
end
end
end
end
availability
end
def can_show_slot_plus_three_months(user)
# member must have validated at least 1 training and must have a valid yearly subscription.
user.trainings.size > 0 and is_subscription_year(user)
end
def is_subscription_year(user)
user.subscription and user.subscription.plan.interval == 'year' and user.subscription.expired_at >= Time.now
end
def in_same_day(start_date, end_date)
(end_date.to_date - start_date.to_date).to_i == 1
end
def filter_availabilites(availabilities)
availabilities_filtered = []
availabilities.to_ary.each do |a|
# machine slot
if !a.try(:available_type)
availabilities_filtered << a
else
# training
if params[:t] and a.available_type == 'training'
if params[:t].include?(a.trainings.first.id.to_s)
availabilities_filtered << a
end
end
# space
if params[:s] and a.available_type == 'space'
if params[:s].include?(a.spaces.first.id.to_s)
availabilities_filtered << a
end
end
# machines
if params[:m] and a.available_type == 'machines'
if (params[:m].map(&:to_i) & a.machine_ids).any?
availabilities_filtered << a
end
end
# event
if params[:evt] and params[:evt] == 'true' and a.available_type == 'event'
# training
if params[:t] and a.available_type == 'training'
if params[:t].include?(a.trainings.first.id.to_s)
availabilities_filtered << a
end
end
end
availabilities_filtered.delete_if do |a|
if params[:dispo] == 'false'
a.is_reserved or (a.try(:completed?) and a.completed?)
# space
if params[:s] and a.available_type == 'space'
if params[:s].include?(a.spaces.first.id.to_s)
availabilities_filtered << a
end
end
# machines
if params[:m] and a.available_type == 'machines'
if (params[:m].map(&:to_i) & a.machine_ids).any?
availabilities_filtered << a
end
end
# event
if params[:evt] and params[:evt] == 'true' and a.available_type == 'event'
availabilities_filtered << a
end
end
end
availabilities_filtered.delete_if do |a|
if params[:dispo] == 'false'
a.is_reserved or (a.try(:completed?) and a.completed?)
end
end
end
def define_max_visibility
@visi_max_year = Setting.find_by(name: 'visibility_yearly').value.to_i.months.since

View File

@ -8,7 +8,7 @@ class API::SettingsController < API::ApiController
def update
authorize Setting
@setting = Setting.find_or_initialize_by(name: params[:name])
if @setting.update(setting_params)
if @setting.save && @setting.history_values.create(value: setting_params[:value], user: current_user)
render status: :ok
else
render json: @setting.errors.full_messages, status: :unprocessable_entity
@ -20,11 +20,12 @@ class API::SettingsController < API::ApiController
end
private
def setting_params
params.require(:setting).permit(:value)
end
def names_as_string_to_array
params[:names][1..-2].split(',').map(&:strip).map { |param| param[1..-2] }.map(&:strip)
end
def setting_params
params.require(:setting).permit(:value)
end
def names_as_string_to_array
params[:names][1..-2].split(',').map(&:strip).map { |param| param[1..-2] }.map(&:strip)
end
end

View File

@ -0,0 +1,4 @@
class HistoryValue < ActiveRecord::Base
belongs_to :setting
belongs_to :user
end

View File

@ -29,7 +29,7 @@ class Invoice < ActiveRecord::Base
def generate_reference
pattern = Setting.find_by({name: 'invoice_reference'}).value
pattern = Setting.find_by(name: 'invoice_reference').value
# invoice number per day (dd..dd)
reference = pattern.gsub(/d+(?![^\[]*\])/) do |match|

View File

@ -1,6 +1,7 @@
class Setting < ActiveRecord::Base
has_many :history_values
validates :name, inclusion:
{ in: %w(about_title
{ in: %w[about_title
about_body
about_contacts
twitter_name
@ -36,16 +37,21 @@ class Setting < ActiveRecord::Base
visibility_yearly
visibility_others
display_name_enable
machines_sort_by )
}
machines_sort_by] }
after_update :update_stylesheet if :value_changed?
def update_stylesheet
if %w(main_color secondary_color).include? self.name
Stylesheet.first.rebuild!
end
Stylesheet.first&.rebuild! if %w[main_color secondary_color].include? name
end
def value
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).first
last_value&.value
end
def value=(val)
admin = User.admins.first
save && history_values.create(user: admin, value: val)
end
end

View File

@ -2,42 +2,47 @@ class Stylesheet < ActiveRecord::Base
validates_presence_of :contents
def rebuild!
self.update(contents: Stylesheet.css)
return unless Stylesheet.primary && Stylesheet.secondary
update(contents: Stylesheet.css)
end
def self.build_sheet!
unless Stylesheet.first
return unless Stylesheet.primary && Stylesheet.secondary
if Stylesheet.first
Stylesheet.first.rebuild!
else
Stylesheet.create!(contents: Stylesheet.css)
end
end
private
def self.primary
Setting.find_by(name: 'main_color').value
Setting.find_by(name: 'main_color')&.value
end
def self.secondary
Setting.find_by(name: 'secondary_color').value
Setting.find_by(name: 'secondary_color')&.value
end
def self.primary_light
self.primary.paint.lighten(10)
Stylesheet.primary.paint.lighten(10)
end
def self.primary_dark
self.primary.paint.darken(20)
Stylesheet.primary.paint.darken(20)
end
def self.secondary_light
self.secondary.paint.lighten(10)
Stylesheet.secondary.paint.lighten(10)
end
def self.secondary_dark
self.secondary.paint.darken(20)
Stylesheet.secondary.paint.darken(20)
end
def self.primary_with_alpha(alpha)
self.primary.paint.to_rgb.insert(3, 'a').insert(-2, ", #{alpha}")
Stylesheet.primary.paint.to_rgb.insert(3, 'a').insert(-2, ", #{alpha}")
end
def self.css
@ -77,6 +82,5 @@ class Stylesheet < ActiveRecord::Base
.social-icons > div:hover { background-color: #{Stylesheet.secondary}; }
.profile-top { background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), linear-gradient(#{Stylesheet.primary_with_alpha(0.78)}, #{Stylesheet.primary_with_alpha(0.82)} ), url('#{CustomAsset.get_url('profile-image-file') || '/about-fablab.jpg'}') no-repeat; }
.profile-top .social-links a:hover { background-color: #{Stylesheet.secondary} !important; border-color: #{Stylesheet.secondary} !important; }"
end
end

View File

@ -1,25 +1,25 @@
class MigrateEventReducedAmountToPriceCategory < ActiveRecord::Migration
def up
pc = PriceCategory.new(
name: I18n.t('price_category.reduced_fare'),
conditions: I18n.t('price_category.reduced_fare_if_you_are_under_25_student_or_unemployed')
name: I18n.t('price_category.reduced_fare'),
conditions: I18n.t('price_category.reduced_fare_if_you_are_under_25_student_or_unemployed')
)
pc.save!
Event.where.not(reduced_amount: nil).each do |event|
unless event.reduced_amount == 0 and event.amount == 0
unless event.reduced_amount.zero? && event.amount.zero?
epc = EventPriceCategory.new(
event: event,
price_category: pc,
amount: event.reduced_amount
event: event,
price_category: pc,
amount: event.reduced_amount
)
epc.save!
Reservation.where(reservable_type: 'Event', reservable_id: event.id).where('nb_reserve_reduced_places > 0').each do |r|
t = Ticket.new(
reservation: r,
event_price_category: epc,
booked: r.nb_reserve_reduced_places
reservation: r,
event_price_category: epc,
booked: r.nb_reserve_reduced_places
)
t.save!
end

View File

@ -0,0 +1,11 @@
class CreateHistoryValues < ActiveRecord::Migration
def change
create_table :history_values do |t|
t.references :setting, index: true, foreign_key: true
t.references :user, index: true, foreign_key: true
t.string :value
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,26 @@
class MigrateSettingsValueToHistoryValues < ActiveRecord::Migration
def up
user = User.admins.first
Setting.all.each do |setting|
hv = HistoryValue.new(
setting: setting,
user: user,
value: setting['value']
)
hv.save!
end
end
def down
# PostgreSQL only (distinct on)
values = execute("SELECT DISTINCT ON (setting_id) setting_id, value, created_at
FROM #{HistoryValue.arel_table.name}
ORDER BY setting_id, created_at DESC, value")
values.each do |val|
value = val['value'] ? val['value'].tr("'", '"') : ''
execute("UPDATE settings
SET value = '#{value}'
WHERE id = #{val['setting_id']}")
end
end
end

View File

@ -0,0 +1,5 @@
class RemoveValueFromSettings < ActiveRecord::Migration
def change
remove_column :settings, :value, :string
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181210105917) do
ActiveRecord::Schema.define(version: 20181217110454) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -221,6 +221,17 @@ ActiveRecord::Schema.define(version: 20181210105917) do
add_index "groups", ["slug"], name: "index_groups_on_slug", unique: true, using: :btree
create_table "history_values", force: :cascade do |t|
t.integer "setting_id"
t.integer "user_id"
t.string "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "history_values", ["setting_id"], name: "index_history_values_on_setting_id", using: :btree
add_index "history_values", ["user_id"], name: "index_history_values_on_user_id", using: :btree
create_table "invoice_items", force: :cascade do |t|
t.integer "invoice_id"
t.string "stp_invoice_item_id", limit: 255
@ -533,7 +544,6 @@ ActiveRecord::Schema.define(version: 20181210105917) do
create_table "settings", force: :cascade do |t|
t.string "name", null: false
t.text "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@ -854,6 +864,8 @@ ActiveRecord::Schema.define(version: 20181210105917) do
add_foreign_key "events_event_themes", "event_themes"
add_foreign_key "events_event_themes", "events"
add_foreign_key "exports", "users"
add_foreign_key "history_values", "settings"
add_foreign_key "history_values", "users"
add_foreign_key "invoices", "coupons"
add_foreign_key "invoices", "wallet_transactions"
add_foreign_key "o_auth2_mappings", "o_auth2_providers"

287
test/fixtures/history_values.yml vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,33 +7,43 @@ class SettingsTest < ActionDispatch::IntegrationTest
login_as(@admin, scope: :user)
end
# Called after every test method runs. Can be used to tear
# down fixture information.
def teardown
# Do nothing
end
test 'update setting value' do
put '/api/settings/fablab_name',
setting: {
value: 'Test Fablab'
}
setting: {
value: 'Test Fablab'
}
assert_equal 200, response.status
assert_equal Mime::JSON, response.content_type
resp = json_response(response.body)
assert_equal 'fablab_name', resp[:setting][:name]
assert_equal 'Test Fablab', resp[:setting][:value]
# Check record
setting = Setting.find_by_name(resp[:setting][:name])
assert_not_nil setting, 'setting was not found in database'
assert_equal 2, setting.history_values.count, 'all historical values were not found'
assert_includes setting.history_values.map(&:value), 'Fab Lab de La Casemate', 'previous parameter was not saved'
assert_includes setting.history_values.map(&:value), 'Test Fablab', 'current parameter was not saved'
end
test 'update setting with wrong name' do
put '/api/settings/does_not_exists',
setting: {
value: 'ERROR EXPECTED'
}
setting: {
value: 'ERROR EXPECTED'
}
assert_equal 422, response.status
assert_match /Name is not included in the list/, response.body
end
test 'show setting' do
get '/api/settings/fablab_name'
assert_equal 200, response.status
assert_equal Mime::JSON, response.content_type
resp = json_response(response.body)
assert_equal 'fablab_name', resp[:setting][:name], 'wrong parameter name'
assert_equal 'Fab Lab de La Casemate', resp[:setting][:value], 'wrong parameter value'
end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class ValueHistoryTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end