aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorVlatka Pavisic2016-12-29 17:20:06 +0100
committerVlatka Pavisic2016-12-29 17:20:06 +0100
commitadfb4aebfab39b5a7a4b9a70ac62f639567aead6 (patch)
tree3ef864b0f48e946d04d1c79947a2e8efe1f96853 /app
parent08b68565fb7f82bc7754bd62ad3f72efca6ea62d (diff)
downloadchouette-core-adfb4aebfab39b5a7a4b9a70ac62f639567aead6.tar.bz2
Refs #2262 Refs #2263 Refs #2264 Refs #2265 : Calendars
Diffstat (limited to 'app')
-rw-r--r--app/controllers/calendars_controller.rb72
-rw-r--r--app/helpers/breadcrumb_helper.rb7
-rw-r--r--app/helpers/newfront_helper.rb16
-rw-r--r--app/models/calendar.rb152
-rw-r--r--app/models/organisation.rb1
-rw-r--r--app/policies/calendar_policy.rb25
-rw-r--r--app/views/calendars/_calendars.html.slim12
-rw-r--r--app/views/calendars/_date_fields.html.slim4
-rw-r--r--app/views/calendars/_form.html.slim20
-rw-r--r--app/views/calendars/_range_fields.html.slim6
-rw-r--r--app/views/calendars/edit.html.slim3
-rw-r--r--app/views/calendars/index.html.slim16
-rw-r--r--app/views/calendars/new.html.slim5
-rw-r--r--app/views/calendars/show.html.slim28
14 files changed, 360 insertions, 7 deletions
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
new file mode 100644
index 000000000..84b432bf3
--- /dev/null
+++ b/app/controllers/calendars_controller.rb
@@ -0,0 +1,72 @@
+class CalendarsController < BreadcrumbController
+ defaults resource_class: Calendar
+ before_action :check_policy, only: [:edit, :update, :destroy]
+
+ def new
+ new! do
+ @calendar.date_ranges = []
+ @calendar.dates = []
+ end
+ end
+
+ def create
+ @calendar = current_organisation.calendars.build(calendar_params)
+
+ if @calendar.valid?
+ respond_with @calendar
+ else
+ render action: 'new'
+ end
+ end
+
+ def update
+ update! do |success, failure|
+ success.html { redirect_to calendar_path(@calendar) }
+ end
+ end
+
+ def destroy
+ destroy! do |success, failure|
+ success.html { redirect_to calendars_path }
+ end
+ end
+
+ private
+ def calendar_params
+ params.require(:calendar).permit(:id, :name, :short_name, :shared, ranges_attributes: [:id, :begin, :end, :_destroy], dates: [])
+ end
+
+ def sort_column
+ current_organisation.calendars.column_names.include?(params[:sort]) ? params[:sort] : 'short_name'
+ end
+
+ def sort_direction
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
+ end
+
+ protected
+ def collection
+ # if params[:q]
+ # if params[:q][:shared_eq]
+ # if params[:q][:shared_eq] == 'all'
+ # params[:q].delete(:shared_eq)
+ # else
+ # params[:q][:shared_eq] = params[:q][:shared_eq] == 'true'
+ # end
+ # end
+ # end
+
+ @q = current_organisation.calendars.search(params[:q])
+
+ if sort_column && sort_direction
+ @calendars ||= @q.result(distinct: true).order(sort_column + ' ' + sort_direction).paginate(page: params[:page])
+ else
+ @calendars ||= @q.result(distinct: true).paginate(page: params[:page])
+ end
+ end
+
+ def check_policy
+ authorize resource
+ end
+end
+
diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb
index dc68bd897..d764ad60f 100644
--- a/app/helpers/breadcrumb_helper.rb
+++ b/app/helpers/breadcrumb_helper.rb
@@ -38,6 +38,8 @@ module BreadcrumbHelper
timeband_breadcrumb action
when 'Chouette::RoutingConstraintZone'
routing_constraint_zone_breadcrumb action
+ when 'Calendar'
+ calendar_breadcrumb action
when "StopAreaCopy"
stop_area_copy_breadcrumb action
when "Import"
@@ -70,6 +72,11 @@ module BreadcrumbHelper
end
end
+ def calendar_breadcrumb(action)
+ add_breadcrumb Calendar.model_name.human.pluralize
+ add_breadcrumb @calendar.name if %i(show edit).include? action
+ end
+
def workbench_breadcrumb(action)
add_breadcrumb I18n.t("breadcrumbs.referentials"), referentials_path
add_breadcrumb breadcrumb_label(@workbench), workbench_path(@workbench), :title => breadcrumb_tooltip(@workbench)
diff --git a/app/helpers/newfront_helper.rb b/app/helpers/newfront_helper.rb
index 2abfd589d..7bedbeea9 100644
--- a/app/helpers/newfront_helper.rb
+++ b/app/helpers/newfront_helper.rb
@@ -55,13 +55,15 @@ module NewfrontHelper
polymorph_url << action
end
- if current_referential
- polymorph_url << current_referential
- polymorph_url << item.line if item.respond_to? :line
- elsif item.respond_to? :referential
- polymorph_url << item.referential
- elsif item.respond_to? :line_referential
- polymorph_url << item.line_referential
+ unless item.class.to_s == 'Calendar'
+ if current_referential
+ polymorph_url << current_referential
+ polymorph_url << item.line if item.respond_to? :line
+ elsif item.respond_to? :referential
+ polymorph_url << item.referential
+ elsif item.respond_to? :line_referential
+ polymorph_url << item.line_referential
+ end
end
polymorph_url << item
diff --git a/app/models/calendar.rb b/app/models/calendar.rb
new file mode 100644
index 000000000..a04aa6d0a
--- /dev/null
+++ b/app/models/calendar.rb
@@ -0,0 +1,152 @@
+class Calendar < ActiveRecord::Base
+ belongs_to :organisation
+
+ validates_presence_of :name, :short_name, :organisation
+ validates_uniqueness_of :short_name
+ validate :date_not_in_date_ranges
+
+ private
+ def date_not_in_date_ranges
+ errors.add(:dates, I18n.t('activerecord.errors.models.calendar.attributes.dates.date_in_date_ranges')) if dates && date_ranges && dates_and_date_ranges_overlap?
+ end
+
+ def dates_and_date_ranges_overlap?
+ overlap = false
+ dates.each do |date|
+ date_ranges.each do |date_range|
+ overlap = true if date_range.cover? date
+ end
+ end
+ overlap
+ end
+
+ class DateRange
+ include ActiveAttr::Model
+
+ attribute :id, type: Integer
+ attribute :begin, type: Date
+ attribute :end, type: Date
+
+ validates_presence_of :begin, :end
+ validate :check_end_greather_than_begin
+
+ def check_end_greather_than_begin
+ if self.begin and self.end and self.begin > self.end
+ errors.add(:end, :invalid)
+ end
+ end
+
+ def self.from_range(index, range)
+ DateRange.new id: index, begin: range.begin, end: range.end
+ end
+
+ def range
+ if self.begin and self.end and self.begin <= self.end
+ Range.new self.begin, self.end
+ end
+ end
+
+ def intersect?(*other)
+ return false if range.nil?
+
+ other = other.flatten
+ other = other.delete_if { |o| o.id == id } if id
+
+ other.any? do |date_range|
+ if other_range = date_range.range
+ (range & other_range).present?
+ end
+ end
+ end
+
+ def cover? date
+ range.cover? date
+ end
+
+ # Stuff required for coocon
+ def new_record?
+ !persisted?
+ end
+ def persisted?
+ id.present?
+ end
+
+ def mark_for_destruction
+ self._destroy = true
+ end
+
+ attribute :_destroy, type: Boolean
+ alias_method :marked_for_destruction?, :_destroy
+ end
+
+ # Required by coocon
+ def build_date_range
+ DateRange.new
+ end
+
+ def ranges
+ @ranges ||= init_ranges
+ end
+
+ def init_ranges
+ if date_ranges
+ date_ranges.each_with_index.map { |r, index| DateRange.from_range(index, r) }
+ else
+ []
+ end
+ end
+ private :init_ranges
+
+ validate :validate_ranges
+
+ def validate_ranges
+ ranges_are_valid = true
+
+ unless ranges.all?(&:valid?)
+ ranges_are_valid = false
+ end
+
+ ranges.each do |range|
+ if range.intersect?(ranges)
+ range.errors.add(:base, I18n.t("referentials.errors.overlapped_period"))
+ ranges_are_valid = false
+ end
+ end
+
+ errors.add(:ranges, :invalid) unless ranges_are_valid
+ end
+
+ def ranges_attributes=(attributes = {})
+ @ranges = []
+ attributes.each do |index, range_attribute|
+ range = DateRange.new(range_attribute.merge(id: index))
+ @ranges << range unless range.marked_for_destruction?
+ end
+
+ date_ranges_will_change!
+ end
+
+ before_validation :fill_date_ranges
+
+ def fill_date_ranges
+ if @ranges
+ self.date_ranges = @ranges.map(&:range).compact.sort_by(&:begin)
+ end
+ end
+
+ after_save :clear_ranges
+
+ def clear_ranges
+ @ranges = nil
+ end
+ private :clear_ranges
+end
+
+class Range
+ def intersection(other)
+ return nil if (self.max < other.begin or other.max < self.begin)
+ [self.begin, other.begin].max..[self.max, other.max].min
+ end
+ alias_method :&, :intersection
+end
+
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index af1042081..8547ce5e1 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -13,6 +13,7 @@ class Organisation < ActiveRecord::Base
has_many :line_referentials, through: :line_referential_memberships
has_many :workbenches
+ has_many :calendars
validates_presence_of :name
validates_uniqueness_of :code
diff --git a/app/policies/calendar_policy.rb b/app/policies/calendar_policy.rb
new file mode 100644
index 000000000..4fbed2ccb
--- /dev/null
+++ b/app/policies/calendar_policy.rb
@@ -0,0 +1,25 @@
+class CalendarPolicy < ApplicationPolicy
+ class Scope < Scope
+ def resolve
+ scope
+ end
+ end
+
+ def show?
+ organisation_match? || share?
+ end
+
+ def create? ; true end
+ def update? ; true end
+ def new? ; true end
+ def edit? ; true end
+ def destroy? ; true end
+
+ def share?
+ record.shared
+ end
+
+ def organisation_match?
+ current_organisation == record.organisation
+ end
+end
diff --git a/app/views/calendars/_calendars.html.slim b/app/views/calendars/_calendars.html.slim
new file mode 100644
index 000000000..7813ff89e
--- /dev/null
+++ b/app/views/calendars/_calendars.html.slim
@@ -0,0 +1,12 @@
+- if @calendars.any?
+ = table_builder @calendars,
+ { @calendars.human_attribute_name(:short_name) => 'short_name', @calendars.human_attribute_name(:shared) => 'shared' },
+ [:show, :edit, :delete],
+ 'table table-bordered'
+
+ .text-center
+ = will_paginate @calendars, container: false, renderer: RemoteBootstrapPaginationLinkRenderer
+
+- else
+ = replacement_msg t('calendars.search_no_results')
+
diff --git a/app/views/calendars/_date_fields.html.slim b/app/views/calendars/_date_fields.html.slim
new file mode 100644
index 000000000..8fd1b5f14
--- /dev/null
+++ b/app/views/calendars/_date_fields.html.slim
@@ -0,0 +1,4 @@
+.nested-fields
+ = f.input :date, as: :date, html5: true
+ /= link_to_remove_association f, data: { confirm: t('are_you_sure') } do
+ span.fa.fa-trash
diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim
new file mode 100644
index 000000000..d12473b3e
--- /dev/null
+++ b/app/views/calendars/_form.html.slim
@@ -0,0 +1,20 @@
+= simple_form_for @calendar, html: { class: 'form-horizontal' } do |f|
+ = f.input :name
+ = f.input :short_name
+ / = f.label :dates
+ / = f.simple_fields_for :dates do |date|
+ / = render 'date_fields', f: date
+ / .links
+ / = link_to_add_association '+', f, :dates
+ = f.label :date_ranges
+ = f.simple_fields_for :ranges do |range|
+ = render 'range_fields', f: range
+ .links
+ = link_to_add_association '+', f, :ranges
+
+ .form-actions
+ = f.button :submit, as: :button
+ = link_to t('cancel'), calendars_path
+
+
+/ TODO : cocoon
diff --git a/app/views/calendars/_range_fields.html.slim b/app/views/calendars/_range_fields.html.slim
new file mode 100644
index 000000000..92cadc4e9
--- /dev/null
+++ b/app/views/calendars/_range_fields.html.slim
@@ -0,0 +1,6 @@
+.nested-fields
+ = f.input :begin, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.begin')
+ = f.input :end, as: :date, html5: true, label: t('simple_form.labels.calendar.ranges.end')
+ /= link_to_remove_association f, data: { confirm: t('are_you_sure') } do
+ span.fa.fa-trash
+
diff --git a/app/views/calendars/edit.html.slim b/app/views/calendars/edit.html.slim
new file mode 100644
index 000000000..f827e2eb6
--- /dev/null
+++ b/app/views/calendars/edit.html.slim
@@ -0,0 +1,3 @@
+= title_tag t('.title')
+
+= render 'form'
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
new file mode 100644
index 000000000..3feca1b1c
--- /dev/null
+++ b/app/views/calendars/index.html.slim
@@ -0,0 +1,16 @@
+= title_tag t('.title')
+
+= search_form_for @q, url: calendars_path, remote: true, html: { method: :get, class: 'form', id: 'search', role: 'form' } do |f|
+
+ .panel.panel-default
+ .panel-heading
+ .input-group.col-md-12
+ = f.search_field :short_name_cont, placeholder: t('.short_name_cont'), class: 'form-control'
+
+ .input-group-btn
+ button.btn.btn-primary#search-btn type='submit'
+ span.fa.fa-search
+
+#calendars
+ = render 'calendars'
+
diff --git a/app/views/calendars/new.html.slim b/app/views/calendars/new.html.slim
new file mode 100644
index 000000000..ccae7ce89
--- /dev/null
+++ b/app/views/calendars/new.html.slim
@@ -0,0 +1,5 @@
+= title_tag t('.title')
+
+= @calendar.inspect
+
+= render 'form'
diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim
new file mode 100644
index 000000000..419cec78e
--- /dev/null
+++ b/app/views/calendars/show.html.slim
@@ -0,0 +1,28 @@
+= title_tag t('.title', calendar: @calendar.name)
+
+p
+ label => "#{Calendar.human_attribute_name('name')} : "
+ = @calendar.name
+p
+ label => "#{Calendar.human_attribute_name('short_name')} : "
+ = @calendar.short_name
+p
+ label => "#{Calendar.human_attribute_name('date_ranges')} : "
+ = @calendar.date_ranges
+p
+ label => "#{Calendar.human_attribute_name('dates')} : "
+ = @calendar.dates
+p
+ label => "#{Calendar.human_attribute_name('shared')} : "
+ = @calendar.shared
+p
+ label => "#{Organisation.model_name.human} : "
+ = @calendar.organisation.name
+
+
+- content_for(:sidebar) do
+ ul.actions
+ li
+ = link_to t('calendars.actions.edit'), edit_calendar_path(@calendar), class: 'edit'
+ li
+ = link_to t('calendars.actions.destroy'), calendar_path(@calendar), method: :delete, data: { confirm: t('calendars.actions.destroy_confirm') }, class: 'remove'