1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

Merge branch 'dev' for release 4.4.2

This commit is contained in:
Sylvain 2020-05-19 13:52:05 +02:00
commit bba897137e
34 changed files with 173 additions and 78 deletions

View File

@ -1 +1 @@
ruby-2.3.8
ruby-2.6.5

View File

@ -1,5 +1,22 @@
# Changelog Fab-manager
## v4.4.2 2020 May 19
- Upgraded to ruby 2.6.5
- Prevent admins from leaving their dedicated group
- Faraday was downgraded from 1.0 to 0.17 for better compatibility with elasticsearch-ruby 5 (#205 #196)
- Added [an option](doc/environment.md#ALLOW_INSECURE_HTTP) to allow usage in production without HTTPS
- Now using node.js instead of therubyracer for building javascript assets
- Removed dependency to has_secure_token to fix warnings about already initialized constant
- Fix a bug: when an admin logs on the subscription page, his view is broken
- Fix a bug: admin's members list shows the same members multiple times
- Fix a bug: when a new account is created through the sign-up modal, the role is not reported in the StatisticProfile (#196)
- Fix a bug: openAPI clients interface has a bugged behavior when creating/editing a client
- Fix a security issue: updated actionpack-page_caching from 1.1.0 to 1.2.2 to fix [CVE-2020-8159](https://nvd.nist.gov/vuln/detail/CVE-2020-8159)
- [TODO DEPLOY] `rails fablab:fix:role_in_statistic_profile`
- [TODO DEPLOY] `rails fablab:es:generate_stats[2019-06-13]` (run after the command above!)
- [TODO DEPLOY] -> (only dev) `rvm use && bundle install`
## v4.4.1 2020 May 12
- Prevent VersionCheckWorker from polluting the sidekiq stack in development

View File

@ -1,4 +1,4 @@
FROM ruby:2.3.8-alpine
FROM ruby:2.6.5-alpine
MAINTAINER peng@sleede.com
# Install upgrade system packages
@ -28,6 +28,8 @@ RUN apk update && apk upgrade && \
git \
patch
RUN gem install bundler
# Throw error if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1

View File

@ -18,8 +18,6 @@ gem 'sass-rails', '~> 5.0', '>= 5.0.6'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 4.1.20'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
gem 'therubyracer', '= 0.12.0', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails'
@ -115,6 +113,7 @@ gem 'prawn-table'
gem 'elasticsearch-model', '~> 5'
gem 'elasticsearch-persistence', '~> 5'
gem 'elasticsearch-rails', '~> 5'
gem 'faraday', '~> 0.17'
gem 'notify_with'
@ -122,7 +121,7 @@ gem 'pundit'
gem 'oj'
gem 'actionpack-page_caching', '1.1.0'
gem 'actionpack-page_caching', '1.2.2'
gem 'rails-observers'
gem 'chroma'
@ -133,7 +132,6 @@ gem 'openlab_ruby'
gem 'api-pagination'
gem 'apipie-rails'
gem 'has_secure_token'
# XLS files generation
gem 'caxlsx'

View File

@ -39,8 +39,8 @@ GEM
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionpack-page_caching (1.1.0)
actionpack (>= 4.0.0, < 6)
actionpack-page_caching (1.2.2)
actionpack (>= 5.0.0)
actionview (5.2.4.2)
activesupport (= 5.2.4.2)
builder (~> 3.1)
@ -165,7 +165,7 @@ GEM
execjs (2.7.0)
faker (2.10.2)
i18n (>= 1.6, < 2)
faraday (1.0.0)
faraday (0.17.3)
multipart-post (>= 1.2, < 3)
ffi (1.12.2)
font-awesome-rails (4.7.0.5)
@ -179,8 +179,6 @@ GEM
raabro (~> 1.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
has_secure_token (1.0.0)
activerecord (>= 3.0)
hashdiff (1.0.1)
hashery (2.1.2)
hashie (4.1.0)
@ -217,11 +215,10 @@ GEM
activerecord
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
libv8 (3.16.14.19)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
loofah (2.4.0)
loofah (2.5.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@ -348,7 +345,6 @@ GEM
redis (4.1.3)
redis-namespace (1.6.0)
redis (>= 3.0.4)
ref (2.0.0)
repost (0.3.2)
responders (2.4.1)
actionpack (>= 4.2.0, < 6.0)
@ -419,9 +415,6 @@ GEM
ffi
term-ansicolor (1.7.1)
tins (~> 1.0)
therubyracer (0.12.0)
libv8 (~> 3.16.14.0)
ref
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.10)
@ -432,7 +425,7 @@ GEM
camertron-eprun
cldr-plurals-runtime-rb (~> 1.0)
tzinfo
tzinfo (1.2.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
@ -463,7 +456,7 @@ PLATFORMS
DEPENDENCIES
aasm
actionpack-page_caching (= 1.1.0)
actionpack-page_caching (= 1.2.2)
active_record_query_trace
api-pagination
apipie-rails
@ -484,11 +477,11 @@ DEPENDENCIES
elasticsearch-persistence (~> 5)
elasticsearch-rails (~> 5)
faker
faraday (~> 0.17)
font-awesome-rails
foreman
forgery
friendly_id (~> 5.1.0)
has_secure_token
icalendar
jbuilder (~> 2.5)
jbuilder_cache_multi
@ -533,11 +526,10 @@ DEPENDENCIES
spring-watcher-listen (~> 2.0.0)
stripe (= 5.1.1)
sys-filesystem
therubyracer (= 0.12.0)
uglifier (>= 4.1.20)
vcr (= 3.0.1)
web-console (>= 3.3.0)
webmock
BUNDLED WITH
1.17.3
2.1.4

View File

@ -111,7 +111,7 @@ Before reporting an issue, please check if your issue is not listed in the [know
<a name="related-documentation"></a>
## Related Documentation
- [Ruby 2.3.0](http://ruby-doc.org/core-2.3.0/)
- [Ruby 2.6.5](http://ruby-doc.org/core-2.6.5/)
- [Ruby on Rails](http://api.rubyonrails.org)
- [AngularJS](https://docs.angularjs.org/api)
- [Angular-Bootstrap](http://angular-ui.github.io/bootstrap/)

View File

@ -20,11 +20,18 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
$scope.clientFormVisible = false;
$scope.client = {};
$scope.toggleForm = () => $scope.clientFormVisible = !$scope.clientFormVisible;
/**
* Show the name edition form for a new client
*/
$scope.createClient = function () {
$scope.clientFormVisible = true;
$scope.client = {};
};
// Change the order criterion to the one provided
// @param orderBy {string} ordering criterion
//
/**
* Change the order criterion to the one provided
* @param orderBy {string} ordering criterion
*/
$scope.setOrder = function (orderBy) {
if ($scope.order === orderBy) {
return $scope.order = `-${orderBy}`;
@ -33,6 +40,14 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
}
};
/**
* Reset the name ot its original value and close the edition form
*/
$scope.cancelEdit = function () {
$scope.client.name = $scope.clientOriginalName;
$scope.clientFormVisible = false;
};
$scope.saveClient = function (client) {
if (client.id != null) {
OpenAPIClient.update({ id: client.id }, { open_api_client: client }, function (clientResp) {
@ -47,13 +62,13 @@ Application.Controllers.controller('OpenAPIClientsController', ['$scope', 'clien
}
$scope.clientFormVisible = false;
$scope.clientForm.$setPristine();
return $scope.client = {};
$scope.client = {};
};
$scope.editClient = function (client) {
$scope.clientFormVisible = true;
return $scope.client = client;
$scope.client = client;
$scope.clientOriginalName = client.name;
};
$scope.deleteClient = index =>

View File

@ -450,9 +450,9 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
return $state.go(toState, toParams);
}
}, function (reason) {
// authentication did not ended successfully
// authentication did not end successfully
if (reason === 'signup') {
// open signup modal
// open sign-up modal
$scope.signup();
} else if (reason === 'resetPassword') {
// open the 'reset password' modal

View File

@ -197,10 +197,10 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
$scope.$on('devise:new-session', function (event, user) { $scope.ctrl.member = user; });
$scope.$on('devise:new-session', function (event, user) { if (user.role !== 'admin') { $scope.ctrl.member = user; } });
// watch when a coupon is applied to re-compute the total price
return $scope.$watch('coupon.applied', function (newValue, oldValue) {
$scope.$watch('coupon.applied', function (newValue, oldValue) {
if ((newValue !== null) || (oldValue !== null)) {
return updateCartPrice();
}

View File

@ -34,14 +34,14 @@
<div class="col-md-12">
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ng-click="toggleForm()" ng-show="!clientFormVisible" translate>{{ 'app.admin.open_api_clients.add_new_client' | translate }}</button>
<button type="button" class="btn btn-warning m-t m-b" ng-click="createClient()" ng-show="!clientFormVisible" translate>{{ 'app.admin.open_api_clients.add_new_client' | translate }}</button>
<form role="form" id="clientForm" ng-show="clientFormVisible" name="clientForm" class="form-inline m-b m-t" novalidate>
<div class="form-group" ng-class="{'has-error': clientForm['client[name]'].$dirty && clientForm['client[name]'].$invalid}">
<input class="form-control" type="text" name="client[name]" ng-model="client.name" value="" placeholder="{{ 'app.admin.open_api_clients.client_name' | translate }}" required>
</div>
<button class="btn btn-default" ng-click="toggleForm()" name="button">{{ 'app.shared.buttons.cancel' | translate }}</button>
<button class="btn btn-default" ng-click="cancelEdit()" name="button">{{ 'app.shared.buttons.cancel' | translate }}</button>
<input type="submit" class="btn btn-warning" ng-disabled="!client.name || client.name.length == 0" ng-click="saveClient(client)" value="{{ 'app.shared.buttons.save' | translate }}">
</form>

View File

@ -20,7 +20,8 @@ class HealthController < ActionController::Base
version: Version.up_to_date?
},
stats: HealthService.stats,
tagline: 'The platform to manage your fablab or your coworking space.'
tagline: 'The platform to manage your fablab or your coworking space.',
url: 'https://www.fab-manager.com'
}
end
end

View File

@ -1,9 +1,29 @@
# frozen_string_literal: true
# OpenAPI::Client keeps track of the authorized accesses to the 3-rd party API (aka. OpenAPI)
class OpenAPI::Client < ApplicationRecord
has_many :calls_count_tracings, foreign_key: :open_api_client_id, dependent: :destroy
has_secure_token
validates :name, presence: true
validates_uniqueness_of :token
before_create :set_initial_token
def increment_calls_count
update_column(:calls_count, calls_count+1)
end
def regenerate_token
update_attributes(token: generate_unique_secure_token)
end
private
def set_initial_token
self.token = generate_unique_secure_token
end
def generate_unique_secure_token
SecureRandom.base58(24)
end
end

View File

@ -319,6 +319,16 @@ class User < ApplicationRecord
.delete_if { |col| blacklist.include?(col[0]) }
end
# will update the statistic_profile after a group switch or a role update
def update_statistic_profile
raise NoProfileError if statistic_profile.nil? || statistic_profile.id.nil?
statistic_profile.update_attributes(
group_id: group_id,
role_id: roles.first.id
)
end
protected
# remove projects drafts that are not linked to another user
@ -430,13 +440,4 @@ class User < ApplicationRecord
email: email
)
end
# will update the statistic_profile after a group switch. Updating the role is not supported
def update_statistic_profile
raise NoProfileError if statistic_profile.nil?
statistic_profile.update_attributes(
group_id: group_id
)
end
end

View File

@ -8,7 +8,7 @@ class TrainingPolicy < ApplicationPolicy
end
end
def create
def create?
user.admin?
end

View File

@ -7,8 +7,7 @@ class Members::ImportService
require 'csv'
log = []
begin
CSV.foreach(import.attachment.url, headers: true, col_sep: ';') do |row|
begin
CSV.foreach(import.attachment.url, headers: true, col_sep: ';') do |row|
password = hide_password(row)
log << { row: row.to_hash }
@ -31,7 +30,6 @@ class Members::ImportService
puts e
puts e.backtrace
end
end
rescue ArgumentError => e
log << e.to_s
puts e

View File

@ -16,7 +16,7 @@ class Members::ListService
SELECT MAX("created_at") AS "s2_created_at", "statistic_profile_id" AS "s2_statistic_profile_id"
FROM "subscriptions"
GROUP BY "statistic_profile_id"
) As s2
) AS s2
ON "s1"."statistic_profile_id" = "s2"."s2_statistic_profile_id"
WHERE "s1"."expiration_date" > now()::date
) AS "subscriptions" ON "subscriptions"."statistic_profile_id" = "statistic_profiles"."id" ' \
@ -78,7 +78,7 @@ class Members::ListService
direction = (params[:order_by][0] == '-' ? 'DESC' : 'ASC')
order_key = (params[:order_by][0] == '-' ? params[:order_by][1, params[:order_by].size] : params[:order_by])
limit = params[:size]
offset = (params[:page]&.to_i || 1) - 1
offset = ((params[:page]&.to_i || 1) - 1) * (params[:size]&.to_i || 1)
order_key = case order_key
when 'last_name'
@ -97,7 +97,7 @@ class Members::ListService
'users.id'
end
"#{order_key} #{direction} LIMIT #{limit} OFFSET #{offset}"
"#{order_key} #{direction}, users.id ASC LIMIT #{limit} OFFSET #{offset}"
end
end
end

View File

@ -15,6 +15,12 @@ class Members::MembersService
return false
end
if params[:group_id] && params[:group_id].to_i != Group.find_by(slug: 'admins').id && @member.admin?
# an admin cannot change his group
@member.errors.add(:group_id, I18n.t('members.admins_cant_change_group'))
return false
end
not_complete = member.need_completion?
up_result = member.update(params)
@ -35,13 +41,16 @@ class Members::MembersService
@member.statistic_profile.group_id = params[:group_id]
@member.statistic_profile.role_id = Role.find_or_create_by!(name: 'member').id
if @member.save
@member.generate_subscription_invoice(current_user.id)
@member.send_confirmation_instructions
UsersMailer.delay.notify_user_account_created(@member, @member.password)
true
else
false
ActiveRecord::Base.transaction do
if @member.save
@member.update_statistic_profile
@member.generate_subscription_invoice(current_user.id)
@member.send_confirmation_instructions
UsersMailer.delay.notify_user_account_created(@member, @member.password)
true
else
false
end
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
@members.each do |member|
json.set! member.id, member.profile.full_name
json.set! member.id, member&.profile&.full_name
end

View File

@ -1,24 +1,26 @@
# frozen_string_literal: true
ActiveRecord::Base.class_eval do
def dump_fixture
fixture_file = "#{Rails.root}/test/fixtures/#{self.class.table_name}.yml"
File.open(fixture_file, "a") do |f|
File.open(fixture_file, 'a') do |f|
f.puts({ "#{self.class.table_name.singularize}_#{id}" => attributes }.
to_yaml.sub!(/---\s?/, "\n"))
end
end
def self.dump_fixtures
fixture_file = "#{Rails.root}/test/fixtures/#{self.table_name}.yml"
mode = (File.exists?(fixture_file) ? 'a' : 'w')
fixture_file = "#{Rails.root}/test/fixtures/#{table_name}.yml"
mode = (File.exist?(fixture_file) ? 'a' : 'w')
File.open(fixture_file, mode) do |f|
if self.attribute_names.include?("id")
self.all.each do |instance|
f.puts({ "#{self.table_name.singularize}_#{instance.id}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
if attribute_names.include?('id')
all.each do |instance|
f.puts({ "#{table_name.singularize}_#{instance.id}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
end
else
self.all.each_with_index do |instance, i|
f.puts({ "#{self.table_name.singularize}_#{i}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
all.each_with_index do |instance, i|
f.puts({ "#{table_name.singularize}_#{i}" => instance.attributes }.to_yaml.sub!(/---\s?/, "\n"))
end
end
end

View File

@ -2,4 +2,6 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_Fab-manager_session', secure: (Rails.env.production? || Rails.env.staging?)
Rails.application.config.session_store :cookie_store,
key: '_Fab-manager_session',
secure: (Rails.env.production? || Rails.env.staging?) && !Rails.application.secrets.allow_insecure_http

View File

@ -53,6 +53,7 @@ en:
#members management
members:
unable_to_change_the_group_while_a_subscription_is_running: "Unable to change the group while a subscription is running"
admins_cant_change_group: "Unable to remove an administrator from his dedicated group"
please_input_the_authentication_code_sent_to_the_address: "Please input the authentication code sent to the e-mail address %{EMAIL}"
your_authentication_code_is_not_valid: "Your authentication code is not valid."
current_authentication_method_no_code: "The current authentication method does not require any migration code"

View File

@ -53,6 +53,7 @@ es:
#members management
members:
unable_to_change_the_group_while_a_subscription_is_running: "No se puede cambiar de grupo mientras haya una suscripción en curso"
admins_cant_change_group: "Unable to remove an administrator from his dedicated group"
please_input_the_authentication_code_sent_to_the_address: "Por favor Ingrese el código de autenticación enviado a la dirección de correo electrónico %{EMAIL}"
your_authentication_code_is_not_valid: "Su código de autenticación no es válido."
current_authentication_method_no_code: "El método de autenticación actual no requiere ningún código de migración"

View File

@ -53,6 +53,7 @@ fr:
#members management
members:
unable_to_change_the_group_while_a_subscription_is_running: "Impossible de changer le groupe tant qu'un abonnement est en cours"
admins_cant_change_group: "Impossible de supprimer un administrateur de son groupe dédié"
please_input_the_authentication_code_sent_to_the_address: "Merci d'enter le code d'authentification qui a été envoyé à l'adresse de courriel %{EMAIL}"
your_authentication_code_is_not_valid: "Votre code d'authentification n'est pas valide."
current_authentication_method_no_code: "La méthode d'authentification actuelle ne requiert pas de code de migration"

View File

@ -53,6 +53,7 @@ pt:
#members management
members:
unable_to_change_the_group_while_a_subscription_is_running: "Não é possível alterar o grupo enquanto uma assinatura está sendo executada"
admins_cant_change_group: "Unable to remove an administrator from his dedicated group"
please_input_the_authentication_code_sent_to_the_address: "Por favor insira o código de autenticação enviado para seu endereço de email %{EMAIL}"
your_authentication_code_is_not_valid: "Seu código de autentiicação não é válido."
current_authentication_method_no_code: "O método de autenticação atual não requer nenhum código de migração"

View File

@ -53,6 +53,7 @@ zu:
#members management
members:
unable_to_change_the_group_while_a_subscription_is_running: "crwdns3275:0crwdne3275:0"
admins_cant_change_group: "crwdns20496:0crwdne20496:0"
please_input_the_authentication_code_sent_to_the_address: "crwdns3277:0%{EMAIL}crwdne3277:0"
your_authentication_code_is_not_valid: "crwdns3279:0crwdne3279:0"
current_authentication_method_no_code: "crwdns3281:0crwdne3281:0"

View File

@ -56,6 +56,7 @@ development:
superadmin_email: <%= ENV["SUPERADMIN_EMAIL"] %>
recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>
allow_insecure_http: <%= ENV.fetch("ALLOW_INSECURE_HTTP", false) %>
test:
secret_key_base: 83daf5e7b80d990f037407bab78dff9904aaf3c195a50f84fa8695a22287e707dfbd9524b403b1dcf116ae1d8c06844c3d7ed942564e5b46be6ae3ead93a9d30
@ -103,6 +104,7 @@ test:
superadmin_email: <%= ENV["SUPERADMIN_EMAIL"] %>
recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>
allow_insecure_http: <%= ENV.fetch("ALLOW_INSECURE_HTTP", false) %>
staging:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
@ -160,6 +162,7 @@ staging:
recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>
enable_in_context_translation: <%= ENV["ENABLE_IN_CONTEXT_TRANSLATION"] %>
allow_insecure_http: <%= ENV.fetch("ALLOW_INSECURE_HTTP", false) %>
# Do not keep production secrets in the repository,
# instead read values from the environment.
@ -218,3 +221,4 @@ production:
superadmin_email: <%= ENV["SUPERADMIN_EMAIL"] %>
recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>
allow_insecure_http: <%= ENV.fetch("ALLOW_INSECURE_HTTP", false) %>

View File

@ -93,7 +93,7 @@ This procedure is not easy to follow so if you don't need to write some code for
10. Install bundler in the current RVM gemset
```bash
gem install bundler --version=1.17.3
gem install bundler
```
11. Install the required ruby gems and javascript plugins

View File

@ -266,6 +266,14 @@ You can change this behavior by setting this variable to one of the following va
- "session" to display the tours each time you reopen the application.
- "manual" to prevent displaying the tours automatically; you'll still be able to trigger them by pressing the F1 key.
<a name="ALLOW_INSECURE_HTTP"></a>
ALLOW_INSECURE_HTTP
In production and staging environments, the session cookie won't be sent to the server unless through the HTTPS protocol.
If you're using Fab-manager on a non-public network or for testing purposes, you can disable this behavior by setting this variable to `true`.
Please, ensure you know what you're doing, as this can lead to serious security issues.
<a name="internationalization-settings"></a>
## Internationalization setting.
<a name="APP_LOCALE"></a>

View File

@ -71,6 +71,7 @@ SUMMERNOTE_LOCALE=fr-FR
ANGULAR_LOCALE=fr-fr
FULLCALENDAR_LOCALE=fr
FORCE_VERSION_CHECK=false
ALLOW_INSECURE_HTTP=false
ELASTICSEARCH_LANGUAGE_ANALYZER=french

View File

@ -166,9 +166,10 @@ namespace :fablab do
desc '(re)generate statistics in ElasticSearch for the past period. Use 0 to generate for today'
task :generate_stats, [:period] => :environment do |_task, args|
raise 'FATAL ERROR: You must pass a number of days (=> past period) to generate statistics on' unless args.period
raise 'FATAL ERROR: You must pass a number of days (=> past period) OR a date to generate statistics' unless args.period
days = args.period.to_i
days = date_to_days(args.period)
puts "\n==> generating statistics for the last #{days} days <==\n"
if days.zero?
StatisticService.new.generate_statistic(start_date: DateTime.current.beginning_of_day, end_date: DateTime.current.end_of_day)
else
@ -178,5 +179,11 @@ namespace :fablab do
end
end
def date_to_days(value)
date = Date.parse(value.to_s)
(DateTime.current.to_date - date).to_i
rescue ArgumentError
value.to_i
end
end
end

View File

@ -164,5 +164,15 @@ namespace :fablab do
end
end
end
desc '[release 4.4.2] add missing role to StatisticProfile'
task role_in_statistic_profile: :environment do
puts "Fixing #{StatisticProfile.where(role_id: nil).count} bugged profiles...\n"
StatisticProfile.where(role_id: nil).each do |sp|
role_id = sp&.user&.roles&.first&.id
sp.role_id = role_id
sp.save!
end
end
end
end

View File

@ -1,6 +1,6 @@
{
"name": "fab-manager",
"version": "4.4.1",
"version": "4.4.2",
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
"keywords": [
"fablab",

View File

@ -157,11 +157,11 @@ install_rvm() {
install_ruby() {
echo 'Installing Ruby'
sudo apt-get install -y libxml2-dev libxslt1-dev libpq-dev libidn11-dev
rvm install ruby-2.3.8
rvm use ruby-2.3.8@global
rvm install ruby-2.6.5
rvm use ruby-2.6.5@global
gem update --system --no-doc
gem update --no-doc
rvm use ruby-2.3.8 --default
rvm use ruby-2.6.5 --default
rvm cleanup all
}

View File

@ -72,6 +72,7 @@ NAVINUM_API_PASSWORD=
LOG_LEVEL=debug
DISK_SPACE_MB_ALERT='100'
SUPERADMIN_EMAIL=
ALLOW_INSECURE_HTTP=false
ALLOWED_EXTENSIONS=pdf ai eps cad math svg stl dxf dwg obj step iges igs 3dm 3dmf doc docx png ino scad fcad skp sldprt sldasm slddrw slddrt tex latex ps fcstd fcstd1