diff options
| author | Vlatka Pavisic | 2016-12-29 17:20:06 +0100 |
|---|---|---|
| committer | Vlatka Pavisic | 2016-12-29 17:20:06 +0100 |
| commit | adfb4aebfab39b5a7a4b9a70ac62f639567aead6 (patch) | |
| tree | 3ef864b0f48e946d04d1c79947a2e8efe1f96853 /app | |
| parent | 08b68565fb7f82bc7754bd62ad3f72efca6ea62d (diff) | |
| download | chouette-core-adfb4aebfab39b5a7a4b9a70ac62f639567aead6.tar.bz2 | |
Refs #2262 Refs #2263 Refs #2264 Refs #2265 : Calendars
Diffstat (limited to 'app')
| -rw-r--r-- | app/controllers/calendars_controller.rb | 72 | ||||
| -rw-r--r-- | app/helpers/breadcrumb_helper.rb | 7 | ||||
| -rw-r--r-- | app/helpers/newfront_helper.rb | 16 | ||||
| -rw-r--r-- | app/models/calendar.rb | 152 | ||||
| -rw-r--r-- | app/models/organisation.rb | 1 | ||||
| -rw-r--r-- | app/policies/calendar_policy.rb | 25 | ||||
| -rw-r--r-- | app/views/calendars/_calendars.html.slim | 12 | ||||
| -rw-r--r-- | app/views/calendars/_date_fields.html.slim | 4 | ||||
| -rw-r--r-- | app/views/calendars/_form.html.slim | 20 | ||||
| -rw-r--r-- | app/views/calendars/_range_fields.html.slim | 6 | ||||
| -rw-r--r-- | app/views/calendars/edit.html.slim | 3 | ||||
| -rw-r--r-- | app/views/calendars/index.html.slim | 16 | ||||
| -rw-r--r-- | app/views/calendars/new.html.slim | 5 | ||||
| -rw-r--r-- | app/views/calendars/show.html.slim | 28 |
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' |
