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 2.4.2

This commit is contained in:
Sylvain 2016-11-08 16:24:59 +01:00
commit 8ab2df1bc7
29 changed files with 174 additions and 58 deletions

View File

@ -1 +1 @@
2.4.1
2.4.2

View File

@ -1,5 +1,15 @@
# Changelog Fab Manager
## v2.4.2 2016 November 8
- Image max size is configurable, default size is 2 megabytes
- Allow add more pictures for project step
- Ability to use HTML in event's descriptions using a WYSIWYG editor
- Fix a bug: statistics graphs were not showing
- Fix a bug: On invoices, only starting date is shown for multi-days events
- Fix a bug: In the sign-up modal, the translation for 'i_accept_to_receive_information_from_the_fablab' was not loaded
- [TODO DEPLOY] add `MAX_IMAGE_SIZE` environment variable in `application.yml` and docker env
## v2.4.1 2016 October 11
- Fix a bug: unable to share a project/event without image on social networks

View File

@ -13,9 +13,8 @@ patches and features.
## Using the issue tracker
The [issue tracker](https://github.com/LaCasemate/fab-manager/issues) is the preferred channel for [bug reports](#bugs),
[features requests](#features) and [submitting pull requests](#pull-requests), but please respect the following
restrictions:
The [issue tracker](https://github.com/LaCasemate/fab-manager/issues) is the preferred channel for [bug reports](#bugs)
and [submitting pull requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use [the forum](https://forum.fab-manager.com)).
@ -70,6 +69,9 @@ Feature requests are welcome. But take a moment to find out whether your idea fi
project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature.
Please provide as much detail and context as possible.
Please note also that [the forum](https://forum.fab-manager.com) is probably a better place for discussing about feature
requests.
<a name="pull-requests"></a>
## Pull requests

View File

@ -14,36 +14,39 @@ Copyright (C) 2015 La Casemate
along with this program. If not, see <http://www.gnu.org/licenses/>.
FabManager uses some external components, which are licenced under the
terms of [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0):
Fab-Manager uses some external components, which are licenced under the
terms of the following licences:
- [jasny-bootstrap](https://github.com/jasny/bootstrap/)
- [elasticsearch](https://github.com/elasticsearch/bower-elasticsearch-js)
- [nvd3](https://github.com/novus/nvd3)
- [angular-bootstrap-switch](https://github.com/frapontillo/angular-bootstrap-switch)
- [elasticsearch-rails](https://github.com/elastic/elasticsearch-rails)
- [elasticsearch-model](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model)
- [elasticsearch-persistence](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence)
- font [Open Sans](http://www.fontsquirrel.com/fonts/open-sans)
- [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0):
- [jasny-bootstrap](https://github.com/jasny/bootstrap/)
- [elasticsearch](https://github.com/elasticsearch/bower-elasticsearch-js)
- [nvd3](https://github.com/novus/nvd3)
- [angular-bootstrap-switch](https://github.com/frapontillo/angular-bootstrap-switch)
- [elasticsearch-rails](https://github.com/elastic/elasticsearch-rails)
- [elasticsearch-model](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model)
- [elasticsearch-persistence](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence)
- font [Open Sans](http://www.fontsquirrel.com/fonts/open-sans)
- [General Public License version 2](http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.en.html):
- [railroady](https://github.com/preston/railroady)
- [unicorn](https://github.com/defunkt/unicorn)
- [prawn](https://github.com/prawnpdf/prawn)
- [prawn-table](https://github.com/prawnpdf/prawn-table)
Some other used libraries/components are licenced under the terms of the
[General Public License version 2](http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.en.html):
- [BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause)
- [ruby](https://www.ruby-lang.org)
- [rubyzip](https://github.com/rubyzip/rubyzip)
- [byebug](https://github.com/deivid-rodriguez/byebug)
- [ruby](https://www.ruby-lang.org)
- [railroady](https://github.com/preston/railroady)
- [unicorn](https://github.com/defunkt/unicorn)
- [prawn](https://github.com/prawnpdf/prawn)
- [prawn-table](https://github.com/prawnpdf/prawn-table)
- [MIT Licence](https://opensource.org/licenses/MIT)
- Errors and omissions excepted, all the other external libraries used
in this project.
Errors and omissions excepted, the other external libraries used in this
project are licenced under the terms of the [MIT Licence](https://opensource.org/licenses/MIT).
Please refer to the libraries documentation for more information about
their licences.
Complete lists of used libraries are available in `bower.json` for the
EcmaScript libraries and in `Gemfile` for Ruby libraries.
JS/EcmaScript libraries and in `Gemfile` for Ruby libraries.

View File

@ -258,6 +258,12 @@ Each item in the list must be separated from the others by a space char.
You will probably want to check that this list match the `ALLOWED_EXTENSIONS` values above.
Please consider that allowing file archives (eg. application/zip) or binary executable (eg. application/exe) may result in a **dangerous** security issue and must be avoided in any cases.
MAX_IMAGE_SIZE
Maximum size (in bytes) allowed for image uploaded on the platform.
This parameter concerns events, plans, user's avatars, projects and steps of projects.
If this parameter is not specified the maximum size allowed will be 2MB.
Settings related to Open Projects
See the [Open Projects](#open-projects) section for a detailed description of these parameters.

View File

@ -362,6 +362,7 @@ Application.Controllers.controller "GraphsController", ["$scope", "$state", "$ro
"index": "stats"
"type": esType
"searchType": "count"
"stat-type": statType
"body": buildElasticAggregationsQuery(statType, $scope.display.interval, moment($scope.datePickerStart.selected), moment($scope.datePickerEnd.selected))
, (error, response) ->
if (error)

View File

@ -124,7 +124,7 @@ class ProjectsController
##
$scope.addStep = ->
$scope.totalSteps += 1
$scope.project.project_steps_attributes.push { step_nb: $scope.totalSteps }
$scope.project.project_steps_attributes.push { step_nb: $scope.totalSteps, project_step_images_attributes: [] }
@ -185,6 +185,26 @@ class ProjectsController
console.error(error)
##
# This will create a single new empty entry into the project's step image list.
##
$scope.addProjectStepImage = (step)->
step.project_step_images_attributes.push {}
##
# This will remove the given image from the project's step image list.
# @param step {Object} the project step has images
# @param image {Object} the image to delete
##
$scope.deleteProjectStepImage = (step, image) ->
index = step.project_step_images_attributes.indexOf(image)
if image.id?
image._destroy = true
else
step.project_step_images_attributes.splice(index, 1)
##
# Controller used on projects listing page

View File

@ -15,6 +15,20 @@
.note-editor .note-editable {
background-color: white;
}
.note-editor {
.form-group {
margin-left: 0px;
margin-right: 0px;
}
.modal-header {
padding: 15px;
}
.note-group-select-from-files {
display: none;
}
}
// Growl

View File

@ -40,7 +40,16 @@
<div class="form-group" ng-class="{'has-error': eventForm['event[description]'].$dirty && eventForm['event[description]'].$invalid}">
<label for="description" class="col-sm-3 control-label">{{ 'description' | translate }} *</label>
<div class="col-sm-9">
<textarea ng-model="event.description" rows="16" class="form-control" id="event_description" placeholder="" name="event[description]" required></textarea>
<input type="hidden"
name="event[description]"
ng-value="event.description" />
<summernote ng-model="event.description"
id="event_description"
placeholder=""
config="summernoteOpts"
name="event[description]"
required>
</summernote>
<span class="help-block" ng-show="eventForm['event[description]'].$dirty && eventForm['event[description]'].$error.required" translate>{{ 'description_is_required' }}</span>
</div>
</div>

View File

@ -36,7 +36,7 @@
</div>
<h3 translate>{{ 'event_description' }}</h3>
<p ng-bind-html="event.description | breakFilter"></p>
<p ng-bind-html="event.description | toTrusted"></p>
</div>

View File

@ -100,15 +100,32 @@
<input type="hidden" name="project[project_steps_attributes][][description]" ng-value="step.description" />
<summernote ng-model="step.description" placeholder="" config="summernoteOpts" name=project[project_steps_attributes][][description]></summernote>
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(step.project_step_image)">
<span class="btn btn-default btn-file"><span class="fileinput-new">{{ 'add_a_picture' | translate }} <i class="fa fa-file-image-o m-l-sm" aria-hidden="true"></i></span><span class="fileinput-exists" translate>{{ 'change_the_picture' }}</span>
<input type="file"
name="project[project_steps_attributes][][project_step_image_attributes][attachment]"></span>
<span class="fileinput-filename">{{step.project_step_image}}</span>
<a class="close fileinput-exists" data-dismiss="fileinput" style="float: none"><i class="fa fa-trash-o"></i></a>
<div class="row">
<div ng-repeat-start="image in step.project_step_images_attributes" class="clearfix" ng-if="$index % 3 == 0"></div>
<div class="col-md-4" ng-repeat-end ng-show="!image._destroy">
<input type="hidden" name="project[project_steps_attributes][][project_step_images_attributes][][id]" ng-value="image.id" />
<input type="hidden" name="project[project_steps_attributes][][project_step_images_attributes][][_destroy]" ng-value="image._destroy" />
<div class="fileinput" data-provides="fileinput" ng-class="fileinputClass(image.attachment)" style="width: 100%;">
<div class="fileinput-new thumbnail" style="width: 100%; height: 200px;">
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:&#xf03e;/font:FontAwesome/icon" bs-holder ng-if="!image.attachment">
</div>
<div class="fileinput-preview fileinput-exists thumbnail" data-trigger="fileinput" style="max-width: 334px;">
<img ng-src="{{ image.attachment_url }}" alt="{{image.attachment}}" />
</div>
<div>
<span class="btn btn-default btn-file"><span class="fileinput-new">{{ 'browse' | translate }} <i class="fa fa-upload fa-fw"></i></span><span class="fileinput-exists" translate>{{ 'change' }}</span>
<input type="file" name="project[project_steps_attributes][][project_step_images_attributes][][attachment]"></span>
<a class="btn btn-danger fileinput-exists" data-dismiss="fileinput" ng-click="deleteProjectStepImage(step, image)" translate>{{ 'delete' }}</a>
</div>
</div>
</div>
</div>
<div>
<a class="btn btn-default" ng-click="addProjectStepImage(step)" role="button">{{ 'add_a_picture' | translate }} <i class="fa fa-file-o fa-fw"></i></a>
</div>
<div>
<div class="m-t">
<a class="btn btn-sm btn-danger" ng-click="deleteStep(step)" role="button"><i class="fa fa-trash-o m-r-xs"></i> {{ 'delete_the_step' | translate }}</a>
</div>
</div>

View File

@ -42,10 +42,11 @@
<div class="col-md-12 m-b-xs">
<h3 class="well well-simple step-title">{{ 'step_N' | translate:{INDEX:step.step_nb} }} : {{step.title}}</h3>
</div>
<div class="col-md-4" ng-if="step.project_step_image">
<a href="{{step.project_step_full_image_url}}" target="_blank"><img class="img-responsive m-b" ng-src="{{step.project_step_image_url}}" alt="{{step.title}}" ></a>
<div ng-repeat-start="image in step.project_step_images_attributes" class="clearfix" ng-if="$index % 3 == 0"></div>
<div class="col-md-4" ng-repeat-end>
<a href="{{image.attachment_full_url}}" target="_blank"><img class="img-responsive m-b" ng-src="{{image.attachment_url}}" alt="{{image.attachment}}" ></a>
</div>
<div class="col-md-8" ng-class="{'col-md-12' : step.project_step_image == undefined}">
<div class="col-md-8" ng-class="{'col-md-12' : step.project_step_images_attributes.length > 1 || step.project_step_images_attributes.length == 0}">
<p ng-bind-html="step.description | toTrusted"></p>
</div>

View File

@ -217,7 +217,7 @@
id="is_allow_newsletter"
ng-model="user.is_allow_newsletter"
value="true"/>
<label for="is_allow_newsletter" translate>{{ 'i_accept_to_receive_informations_from_the_fablab' }}</label>
<label for="is_allow_newsletter" translate>{{ 'i_accept_to_receive_information_from_the_fablab' }}</label>
</div>
</div>

View File

@ -71,6 +71,6 @@ class API::ProjectsController < API::ApiController
user_ids: [], machine_ids: [], component_ids: [], theme_ids: [], project_image_attributes: [:attachment],
project_caos_attributes: [:id, :attachment, :_destroy],
project_steps_attributes: [:id, :description, :title, :_destroy, :step_nb,
:project_step_image_attributes => :attachment])
:project_step_images_attributes => [:id, :attachment, :_destroy]])
end
end

View File

@ -0,0 +1,7 @@
module ImageValidatorConcern
extend ActiveSupport::Concern
included do
validates :attachment, file_size: { maximum: Rails.application.secrets.max_image_size ? Rails.application.secrets.max_image_size.to_i : 2.megabytes.to_i }
end
end

View File

@ -1,5 +1,4 @@
class EventImage < Asset
include ImageValidatorConcern
mount_uploader :attachment, EventImageUploader
validates :attachment, file_size: { maximum: 2.megabytes.to_i }
end

View File

@ -1,4 +1,4 @@
class PlanImage < Asset
include ImageValidatorConcern
mount_uploader :attachment, PlanImageUploader
validates :attachment, file_size: { maximum: 2.megabytes.to_i }
end

View File

@ -1,5 +1,4 @@
class ProjectImage < Asset
include ImageValidatorConcern
mount_uploader :attachment, ProjectImageUploader
validates :attachment, file_size: { maximum: 2.megabytes.to_i }
end

View File

@ -1,5 +1,5 @@
class ProjectStep < ActiveRecord::Base
belongs_to :project
has_one :project_step_image, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :project_step_image, allow_destroy: true
has_many :project_step_images, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :project_step_images, allow_destroy: true, reject_if: :all_blank
end

View File

@ -1,5 +1,4 @@
class ProjectStepImage < Asset
include ImageValidatorConcern
mount_uploader :attachment, ProjectImageUploader
validates :attachment, file_size: { maximum: 2.megabytes.to_i }
end

View File

@ -107,7 +107,11 @@ class Reservation < ActiveRecord::Base
amount += ticket.booked * ticket.event_price_category.amount
end
slots.each do |slot|
description = reservable.name + " #{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}"
description = "#{reservable.name} "
(slot.start_at.to_date..slot.end_at.to_date).each do |d|
description += "\n" if slot.start_at.to_date != slot.end_at.to_date
description += "#{I18n.l d, format: :long} #{I18n.l slot.start_at, format: :hour_minute} - #{I18n.l slot.end_at, format: :hour_minute}"
end
ii_amount = amount
ii_amount = 0 if (slot.offered and on_site)
unless on_site

View File

@ -1,4 +1,4 @@
class UserAvatar < Asset
include ImageValidatorConcern
mount_uploader :attachment, ProfilImageUploader
end

View File

@ -51,9 +51,12 @@ json.project_steps_attributes @project.project_steps.order('project_steps.step_n
json.id s.id
json.description s.description
json.title s.title
json.project_step_image s.project_step_image.attachment_identifier if s.project_step_image
json.project_step_image_url s.project_step_image.attachment.medium.url if s.project_step_image
json.project_step_full_image_url s.project_step_image.attachment.url if s.project_step_image
json.project_step_images_attributes s.project_step_images.order('created_at ASC') do |si|
json.id si.id
json.attachment si.attachment_identifier
json.attachment_url si.attachment.medium.url
json.attachment_full_url si.attachment.url
end
json.step_nb s.step_nb
end
json.state @project.state

View File

@ -8,6 +8,12 @@
<p><%= t('.body.reserved_slots') %></p>
<ul>
<% @attached_object.slots.each do |slot| %>
<li><%= "#{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% if @attached_object.reservable_type == 'Event' %>
<% (slot.start_at.to_date..slot.end_at.to_date).each do |d| %>
<li><%= "#{I18n.l d, format: :long} #{I18n.l slot.start_at, format: :hour_minute} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% end %>
<% else %>
<li><%= "#{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% end %>
<% end %>
</ul>

View File

@ -5,6 +5,12 @@
<p><%= t('.body.your_reserved_slots') %> </p>
<ul>
<% @attached_object.slots.each do |slot| %>
<li><%= "#{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% if @attached_object.reservable_type == 'Event' %>
<% (slot.start_at.to_date..slot.end_at.to_date).each do |d| %>
<li><%= "#{I18n.l d, format: :long} #{I18n.l slot.start_at, format: :hour_minute} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% end %>
<% else %>
<li><%= "#{I18n.l slot.start_at, format: :long} - #{I18n.l slot.end_at, format: :hour_minute}" %></li>
<% end %>
<% end %>
</ul>

View File

@ -59,3 +59,6 @@ LOG_LEVEL: 'debug'
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
ALLOWED_MIME_TYPES: application/pdf application/postscript application/illustrator image/x-eps image/svg+xml application/sla application/dxf application/acad application/dwg application/octet-stream application/step application/iges model/iges x-world/x-3dmf application/ application/vnd.openxmlformats-officedocument.wordprocessingml.document image/png text/x-arduino text/plain application/scad application/vnd.sketchup.skp application/x-koan application/vnd-koan koan/x-skm application/vnd.koan application/x-tex application/x-latex
# 10485760 = 10 megabytes
MAX_IMAGE_SIZE: 10485760

View File

@ -33,7 +33,7 @@ fr:
formats:
default: "%d/%m/%Y"
short: "%e %b"
long: "%e %B %Y"
long: "%A%e %B %Y"
month_names:
-
- janvier

View File

@ -39,6 +39,7 @@ development:
log_level: <%= ENV["LOG_LEVEL"] %>
facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %>
elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %>
max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %>
test:
secret_key_base: 83daf5e7b80d990f037407bab78dff9904aaf3c195a50f84fa8695a22287e707dfbd9524b403b1dcf116ae1d8c06844c3d7ed942564e5b46be6ae3ead93a9d30
@ -69,6 +70,7 @@ test:
log_level: <%= ENV["LOG_LEVEL"] %>
facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %>
elaticsearch_host: localhost
max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %>
staging:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
@ -104,6 +106,7 @@ staging:
log_level: <%= ENV["LOG_LEVEL"] %>
facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %>
elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %>
max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %>
# Do not keep production secrets in the repository,
# instead read values from the environment.
@ -142,3 +145,4 @@ production:
log_level: <%= ENV["LOG_LEVEL"] %>
facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %>
elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %>
max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %>

View File

@ -60,3 +60,6 @@ LOG_LEVEL=debug
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
ALLOWED_MIME_TYPES=application/pdf application/postscript application/illustrator image/x-eps image/svg+xml application/sla application/dxf application/acad application/dwg application/octet-stream application/step application/iges model/iges x-world/x-3dmf application/ application/vnd.openxmlformats-officedocument.wordprocessingml.document image/png text/x-arduino text/plain application/scad application/vnd.sketchup.skp application/x-koan application/vnd-koan koan/x-skm application/vnd.koan application/x-tex application/x-latex
# 10485760 = 10 megabytes
MAX_IMAGE_SIZE=10485760