diff options
98 files changed, 2197 insertions, 971 deletions
| diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass index a649a07ef..93f9907a5 100644 --- a/app/assets/stylesheets/components/_buttons.sass +++ b/app/assets/stylesheets/components/_buttons.sass @@ -142,16 +142,25 @@ table, .table          margin: 0          border-radius: 0          box-shadow: 0 0 3px rgba($darkgrey, 0.25) - -        > li > a, > li > button -          padding: 5px 15px - -        > li.delete-action -          > a, > button +        > ul +          padding: 0 +          margin: 0 +          > li > a, > li > button +            padding: 5px 15px +            white-space: nowrap +            padding: 5px 15px +            font-weight: normal +            line-height: $line-height              display: block +            font-size: 14px +            &:hover, &:focus +              text-decoration: none +              background-color: whitesmoke +              outline: none + +          &:not(:first-child)              position: relative              margin-top: 11px -              &:before                content: ''                display: block @@ -162,14 +171,18 @@ table, .table                height: 1px                background-color: $grey -            .fa:first-child -              margin-right: 0.5em - -          & + li.delete-action +          > li.delete-action              > a, > button -              margin-top: 0 -              &:before -                display: none +              display: block +              position: relative +              .fa:first-child +                margin-right: 0.5em + +            & + li.delete-action +              > a, > button +                margin-top: 0 +                &:before +                  display: none    &.table-2entries .t2e-item diff --git a/app/assets/stylesheets/components/_dropdown.sass b/app/assets/stylesheets/components/_dropdown.sass index 8a8d69063..a0d217b14 100644 --- a/app/assets/stylesheets/components/_dropdown.sass +++ b/app/assets/stylesheets/components/_dropdown.sass @@ -18,9 +18,19 @@        background-color: whitesmoke        outline: none -  > .disabled > a, > .disabled > button -    cursor: not-allowed -    &, &:hover, &:focus -      color: rgba($darkgrey, 0.5) -      background-color: transparent -      outline: none +  &, & > ul +    > .disabled > a, > .disabled > button +      cursor: not-allowed +      &, &:hover, &:focus +        color: rgba($darkgrey, 0.5) +        background-color: transparent +        outline: none + +  > ul > li > a +    display: block +    padding: 3px 20px +    clear: both +    font-weight: normal +    line-height: 1.42857 +    color: #333333 +    white-space: nowrap diff --git a/app/controllers/api_keys_controller.rb b/app/controllers/api_keys_controller.rb index 9706c5961..a03a67481 100644 --- a/app/controllers/api_keys_controller.rb +++ b/app/controllers/api_keys_controller.rb @@ -31,11 +31,4 @@ class ApiKeysController < ChouetteController    def api_key_params      params.require(:api_key).permit(:name, :referential_id)    end - -  def decorate_api_keys(api_keys) -    ModelDecorator.decorate( -      api_keys, -      with: ApiKeyDecorator, -    ) -  end  end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 80d194096..8bd3da2f9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -52,6 +52,19 @@ class ApplicationController < ActionController::Base    end    helper_method :current_functional_scope +  def collection_name +    self.class.name.split("::").last.gsub('Controller', '').underscore +  end + +  def decorated_collection +    if instance_variable_defined?("@#{collection_name}") +      instance_variable_get("@#{collection_name}") +    else +      nil +    end +  end +  helper_method :decorated_collection +    def begin_of_association_chain      current_organisation    end diff --git a/app/controllers/companies_controller.rb b/app/controllers/companies_controller.rb index f84252920..4afd12be1 100644 --- a/app/controllers/companies_controller.rb +++ b/app/controllers/companies_controller.rb @@ -47,6 +47,9 @@ class CompaniesController < ChouetteController      end    end +  def resource +    super.decorate(context: { referential: line_referential }) +  end    def resource_url(company = nil)      line_referential_company_path(line_referential, company || resource) @@ -79,9 +82,8 @@ class CompaniesController < ChouetteController    end    def decorate_companies(companies) -    ModelDecorator.decorate( +    CompanyDecorator.decorate(        companies, -      with: CompanyDecorator,        context: {          referential: line_referential        } diff --git a/app/controllers/compliance_check_sets_controller.rb b/app/controllers/compliance_check_sets_controller.rb index 4ec86f0d6..271598428 100644 --- a/app/controllers/compliance_check_sets_controller.rb +++ b/app/controllers/compliance_check_sets_controller.rb @@ -11,9 +11,8 @@ class ComplianceCheckSetsController < ChouetteController        scope = self.ransack_period_range(scope: @compliance_check_sets, error_message: t('compliance_check_sets.filters.error_period_filter'), query: :where_created_at_between)        @q_for_form = scope.ransack(params[:q])        format.html { -        @compliance_check_sets = ModelDecorator.decorate( -          @q_for_form.result.order(created_at: :desc), -          with: ComplianceCheckSetDecorator +        @compliance_check_sets = ComplianceCheckSetDecorator.decorate( +          @q_for_form.result.order(created_at: :desc)          )        }      end @@ -41,18 +40,9 @@ class ComplianceCheckSetsController < ChouetteController    def executed_for_html      @q_checks_form        = @compliance_check_set.compliance_checks.ransack(params[:q])      @compliance_check_set = @compliance_check_set.decorate -    compliance_checks    = -      decorate_compliance_checks( @q_checks_form.result) -        .group_by(&:compliance_check_block) +    compliance_checks = @q_checks_form.result +      .group_by(&:compliance_check_block)      @direct_compliance_checks        = compliance_checks.delete nil      @blocks_to_compliance_checks_map = compliance_checks    end - -  # Decoration -  # ---------- -  def decorate_compliance_checks(compliance_checks) -    ModelDecorator.decorate( -      compliance_checks, -      with: ComplianceCheckDecorator) -  end  end diff --git a/app/controllers/compliance_control_sets_controller.rb b/app/controllers/compliance_control_sets_controller.rb index 2d3d03ad0..ae1d01feb 100644 --- a/app/controllers/compliance_control_sets_controller.rb +++ b/app/controllers/compliance_control_sets_controller.rb @@ -38,17 +38,11 @@ class ComplianceControlSetsController < ChouetteController    private    def decorate_compliance_control_sets(compliance_control_sets) -    ModelDecorator.decorate( -      compliance_control_sets, -      with: ComplianceControlSetDecorator -    ) +    ComplianceControlSetDecorator.decorate(compliance_control_sets)    end    def decorate_compliance_controls(compliance_controls) -    ModelDecorator.decorate( -      compliance_controls, -      with: ComplianceControlDecorator, -    ) +    ComplianceControlDecorator.decorate(compliance_controls)    end    def compliance_control_set_params diff --git a/app/controllers/compliance_controls_controller.rb b/app/controllers/compliance_controls_controller.rb index dfbecaa71..73dc18f59 100644 --- a/app/controllers/compliance_controls_controller.rb +++ b/app/controllers/compliance_controls_controller.rb @@ -8,6 +8,12 @@ class ComplianceControlsController < ChouetteController      @sti_subclasses = ComplianceControl.subclasses    end +  def show +    show! do +      @compliance_control = @compliance_control.decorate +    end +  end +    def new      if params[:sti_class].blank?        flash[:notice] = I18n.t("compliance_controls.errors.mandatory_control_type") diff --git a/app/controllers/import_resources_controller.rb b/app/controllers/import_resources_controller.rb index c83721310..ea78394a1 100644 --- a/app/controllers/import_resources_controller.rb +++ b/app/controllers/import_resources_controller.rb @@ -27,12 +27,6 @@ class ImportResourcesController < ChouetteController    private    def decorate_import_resources(import_resources) -    ImportResourcesDecorator.decorate( -      import_resources, -      with: ImportResourceDecorator, -      context: { -        import: @import -      } -    ) +    ImportResourcesDecorator.decorate(import_resources)    end  end diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 46d34efda..7a999d657 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -84,9 +84,8 @@ class ImportsController < ChouetteController    end    def decorate_imports(imports) -    ModelDecorator.decorate( +    ImportDecorator.decorate(        imports, -      with: ImportDecorator,        context: {          workbench: @workbench        } diff --git a/app/controllers/lines_controller.rb b/app/controllers/lines_controller.rb index 7041a3a26..27a9bf9be 100644 --- a/app/controllers/lines_controller.rb +++ b/app/controllers/lines_controller.rb @@ -15,9 +15,8 @@ class LinesController < ChouetteController    def index      @hide_group_of_line = line_referential.group_of_lines.empty?      index! do |format| -      @lines = ModelDecorator.decorate( +      @lines = LineDecorator.decorate(          @lines, -        with: LineDecorator,          context: {            line_referential: @line_referential,            current_organisation: current_organisation @@ -69,7 +68,6 @@ class LinesController < ChouetteController      respond_to do |format|        format.json { render :json => filtered_lines_maps}      end -    end    protected diff --git a/app/controllers/networks_controller.rb b/app/controllers/networks_controller.rb index 79a5eb97b..1c69b1240 100644 --- a/app/controllers/networks_controller.rb +++ b/app/controllers/networks_controller.rb @@ -89,9 +89,8 @@ class NetworksController < ChouetteController    end    def decorate_networks(networks) -    ModelDecorator.decorate( +    NetworkDecorator.decorate(        networks, -      with: NetworkDecorator,        context: {          line_referential: line_referential        } diff --git a/app/controllers/purchase_windows_controller.rb b/app/controllers/purchase_windows_controller.rb index 04b5736bb..293a7d8e4 100644 --- a/app/controllers/purchase_windows_controller.rb +++ b/app/controllers/purchase_windows_controller.rb @@ -36,13 +36,12 @@ class PurchaseWindowsController < ChouetteController    end    def decorate_purchase_windows(purchase_windows) -    ModelDecorator.decorate( +    PurchaseWindowDecorator.decorate(        purchase_windows, -      with: PurchaseWindowDecorator,        context: {          referential: @referential -        } -      ) +      } +    )    end     def sort_column diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb index 7e65a72cf..806a70c8f 100644 --- a/app/controllers/referential_companies_controller.rb +++ b/app/controllers/referential_companies_controller.rb @@ -69,10 +69,13 @@ class ReferentialCompaniesController < ChouetteController      %w[asc desc].include?(params[:direction]) ?  params[:direction] : 'asc'    end +  def collection_name +    "companies" +  end +    def decorate_companies(companies) -    ModelDecorator.decorate( +    CompanyDecorator.decorate(        companies, -      with: CompanyDecorator,        context: {          referential: referential        } diff --git a/app/controllers/referential_lines_controller.rb b/app/controllers/referential_lines_controller.rb index 9e8f5c512..37051faeb 100644 --- a/app/controllers/referential_lines_controller.rb +++ b/app/controllers/referential_lines_controller.rb @@ -28,9 +28,8 @@ class ReferentialLinesController < ChouetteController      @routes = @routes.paginate(page: params[:page], per_page: 10) -    @routes = ModelDecorator.decorate( +    @routes = RouteDecorator.decorate(        @routes, -      with: RouteDecorator,        context: {          referential: referential,          line: @line diff --git a/app/controllers/referential_networks_controller.rb b/app/controllers/referential_networks_controller.rb index b2d83f953..fe00a99df 100644 --- a/app/controllers/referential_networks_controller.rb +++ b/app/controllers/referential_networks_controller.rb @@ -56,6 +56,10 @@ class ReferentialNetworksController < ChouetteController      end    end +  def collection_name +    'networks' +  end +    def resource_url(network = nil)      referential_network_path(referential, network || resource)    end @@ -78,9 +82,8 @@ class ReferentialNetworksController < ChouetteController    end    def decorate_networks(networks) -    ModelDecorator.decorate( +    ReferentialNetworkDecorator.decorate(        networks, -      with: ReferentialNetworkDecorator,        context: {          referential: referential        } diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 436d5ccb5..0ed3f75dd 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -32,9 +32,8 @@ class ReferentialsController < ChouetteController      show! do |format|        @referential = @referential.decorate(context: { current_workbench_id: params[:current_workbench_id] } )        @reflines = lines_collection.paginate(page: params[:page], per_page: 10) -      @reflines = ModelDecorator.decorate( +      @reflines = ReferentialLineDecorator.decorate(          @reflines, -        with: ReferentialLineDecorator,          context: {            referential: referential,            current_organisation: current_organisation @@ -80,6 +79,7 @@ class ReferentialsController < ChouetteController      referential.archive!      redirect_to workbench_path(referential.workbench_id), notice: t('notice.referential.archived')    end +    def unarchive      if referential.unarchive!        flash[:notice] = t('notice.referential.unarchived') @@ -97,7 +97,7 @@ class ReferentialsController < ChouetteController    helper_method :current_referential    def resource -    @referential ||= current_organisation.find_referential(params[:id]) +    @referential ||= current_organisation.find_referential(params[:id]).decorate    end    def collection diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb index 79f49143a..af5a9a91b 100644 --- a/app/controllers/routes_controller.rb +++ b/app/controllers/routes_controller.rb @@ -47,10 +47,7 @@ class RoutesController < ChouetteController          line: @line        }) -      @route_sp = ModelDecorator.decorate( -        @route_sp, -        with: StopPointDecorator -      ) +      @route_sp = StopPointDecorator.decorate(@route_sp)      end    end diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb index a72b288b8..47df211d0 100644 --- a/app/controllers/routing_constraint_zones_controller.rb +++ b/app/controllers/routing_constraint_zones_controller.rb @@ -13,9 +13,8 @@ class RoutingConstraintZonesController < ChouetteController    def index      index! do |format| -      @routing_constraint_zones = ModelDecorator.decorate( +      @routing_constraint_zones = RoutingConstraintZoneDecorator.decorate(          @routing_constraint_zones, -        with: RoutingConstraintZoneDecorator,          context: {            referential: referential,            line: parent diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index 5243ce56c..79ffea72e 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -57,10 +57,7 @@ class StopAreasController < ChouetteController            redirect_to params.merge(:page => 1)          end -        @stop_areas = ModelDecorator.decorate( -          @stop_areas, -          with: StopAreaDecorator -        ) +        @stop_areas = StopAreaDecorator.decorate(@stop_areas)        }      end    end diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index a0fa168f0..0707b9648 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -167,9 +167,8 @@ class TimeTablesController < ChouetteController    end    def decorate_time_tables(time_tables) -    ModelDecorator.decorate( +    TimeTableDecorator.decorate(        time_tables, -      with: TimeTableDecorator,        context: {          referential: @referential        } diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb index b2dac9e67..2a71fe811 100644 --- a/app/controllers/workbenches_controller.rb +++ b/app/controllers/workbenches_controller.rb @@ -18,9 +18,8 @@ class WorkbenchesController < ChouetteController      @q_for_form   = scope.ransack(params[:q])      @q_for_result = scope.ransack(ransack_params)      @wbench_refs  = sort_result(@q_for_result.result).paginate(page: params[:page], per_page: 30) -    @wbench_refs  = ModelDecorator.decorate( +    @wbench_refs  = ReferentialDecorator.decorate(        @wbench_refs, -      with: ReferentialDecorator,        context: {          current_workbench_id: params[:id]        } diff --git a/app/decorators/api_key_decorator.rb b/app/decorators/api_key_decorator.rb deleted file mode 100644 index def3a6a01..000000000 --- a/app/decorators/api_key_decorator.rb +++ /dev/null @@ -1,30 +0,0 @@ -class ApiKeyDecorator < Draper::Decorator -  decorates Api::V1::ApiKey -  delegate_all - - -  def action_links -    links = [] - -    links << Link.new( -      content: h.t('api_keys.actions.show'), -      href: h.organisation_api_key_path(object), -    ) - -    links << Link.new( -      content: h.t('api_keys.actions.edit'), -      href: h.edit_organisation_api_key_path(object), -    ) - -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.organisation_api_key_path(object), -        method: :delete, -        data: { confirm: h.t('api_keys.actions.destroy_confirm') } -      ) -    end - -    links -  end -end diff --git a/app/decorators/calendar_decorator.rb b/app/decorators/calendar_decorator.rb index ce2c1a2dd..be1f9e3bf 100644 --- a/app/decorators/calendar_decorator.rb +++ b/app/decorators/calendar_decorator.rb @@ -1,18 +1,13 @@ -class CalendarDecorator < Draper::Decorator -  delegate_all +class CalendarDecorator < AF83::Decorator +  decorates Calendar -  def action_links -    links = [] +  create_action_link -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.workgroup_calendar_path(context[:workgroup], object), -        method: :delete, -        data: { confirm: h.t('calendars.actions.destroy_confirm') } -      ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link +    instance_decorator.edit_action_link +    instance_decorator.destroy_action_link do |l| +      l.data {{ confirm: h.t('calendars.actions.destroy_confirm') }}      end - -    links    end  end diff --git a/app/decorators/company_decorator.rb b/app/decorators/company_decorator.rb index 50b82d276..aadce68bb 100644 --- a/app/decorators/company_decorator.rb +++ b/app/decorators/company_decorator.rb @@ -1,45 +1,35 @@ -class CompanyDecorator < Draper::Decorator +class CompanyDecorator < AF83::Decorator    decorates Chouette::Company -  delegate_all - -  def self.collection_decorator_class -    PaginatingDecorator -  end - -  def linecount -    object.lines.count +  create_action_link do |l| +    l.content { h.t('companies.actions.new') } +    l.href    { [:new, context[:referential], :company] }    end -  # Requires: -  #   context: { -  #     referential: -  #   } -  def action_links -    links = [] +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href { [context[:referential], object] } +    end -    if h.policy(object).update? -      links << Link.new( -        content: h.t('companies.actions.edit'), -        href: h.edit_line_referential_company_path( +    instance_decorator.edit_action_link do |l| +      l.content {|l| l.action == "show" ? h.t('actions.edit') : h.t('companies.actions.edit') } +      l.href { +        h.edit_line_referential_company_path(            context[:referential],            object          ) -      ) +      }      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content('companies.actions.destroy'), -        href: h.line_referential_company_path( +    instance_decorator.destroy_action_link do |l| +      l.content { h.destroy_link_content('companies.actions.destroy') } +      l.href { +        h.edit_line_referential_company_path(            context[:referential],            object -        ), -        method: :delete, -        data: { confirm: h.t('companies.actions.destroy_confirm') } -      ) +        ) +      } +      l.data {{ confirm: h.t('companies.actions.destroy_confirm') }}      end - -    links    end  end diff --git a/app/decorators/compliance_check_decorator.rb b/app/decorators/compliance_check_decorator.rb deleted file mode 100644 index 5431f5796..000000000 --- a/app/decorators/compliance_check_decorator.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ComplianceCheckDecorator < Draper::Decorator -  delegate_all - -  def action_links -    [] -  end - -end diff --git a/app/decorators/compliance_check_set_decorator.rb b/app/decorators/compliance_check_set_decorator.rb index c58e14d88..334f39e88 100644 --- a/app/decorators/compliance_check_set_decorator.rb +++ b/app/decorators/compliance_check_set_decorator.rb @@ -1,15 +1,15 @@ -class ComplianceCheckSetDecorator < Draper::Decorator -  delegate_all +class ComplianceCheckSetDecorator < AF83::Decorator +  decorates ComplianceCheckSet -  def action_links -    links = [] +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link    end -  def lines_status +  define_instance_method :lines_status do      object.compliance_check_resources.where(status: :OK, resource_type: :line).count    end -  def lines_in_compliance_check_set +  define_instance_method :lines_in_compliance_check_set do      object.compliance_check_resources.where(resource_type: :line).count    end diff --git a/app/decorators/compliance_control_decorator.rb b/app/decorators/compliance_control_decorator.rb index f56e80417..c57a7ccc7 100644 --- a/app/decorators/compliance_control_decorator.rb +++ b/app/decorators/compliance_control_decorator.rb @@ -1,30 +1,47 @@ -class ComplianceControlDecorator < Draper::Decorator -  delegate_all +class ComplianceControlDecorator < AF83::Decorator +  decorates ComplianceControl -  def action_links -    policy = h.policy(object) -    links = [] - -    links << Link.new( -      content: h.t('compliance_control_sets.actions.show'), -      href:  h.compliance_control_set_compliance_control_path(object.compliance_control_set.id, object.id) -    ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.content h.t('compliance_control_sets.actions.show') +      l.href do +        h.compliance_control_set_compliance_control_path( +          object.compliance_control_set.id, +          object.id +        ) +      end +    end -    if policy.edit? -      links << Link.new( -        content: h.t('compliance_controls.actions.edit'), -        href:  h.edit_compliance_control_set_compliance_control_path(object.compliance_control_set.id, object.id) -      ) +    instance_decorator.edit_action_link do |l| +      l.href do +        h.edit_compliance_control_set_compliance_control_path( +          object.compliance_control_set_id, +          object.id +        ) +      end      end -    if policy.destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.compliance_control_set_compliance_control_path(object.compliance_control_set.id, object.id), -        method: :delete, -        data: { confirm: h.t('compliance_controls.actions.destroy_confirm') } -      ) +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content +      l.href do +        h.compliance_control_set_compliance_control_path( +          object.compliance_control_set.id, +          object.id +        ) +      end +      l.data confirm: h.t('compliance_controls.actions.destroy_confirm')      end -    links +  end + +  define_instance_class_method :predicate do +    object_class.predicate +  end + +  define_instance_class_method :prerequisite do +    object_class.prerequisite +  end + +  define_instance_class_method :dynamic_attributes do +    object_class.dynamic_attributes    end  end diff --git a/app/decorators/compliance_control_set_decorator.rb b/app/decorators/compliance_control_set_decorator.rb index 73d65d54a..387822c67 100644 --- a/app/decorators/compliance_control_set_decorator.rb +++ b/app/decorators/compliance_control_set_decorator.rb @@ -1,35 +1,24 @@ -class ComplianceControlSetDecorator < Draper::Decorator -  delegate_all +class ComplianceControlSetDecorator < AF83::Decorator +  decorates ComplianceControlSet -  def action_links -    policy = h.policy(object) -    links = [] +  create_action_link do |l| +    l.content t('compliance_control_sets.actions.new') +  end -    if policy.edit? -      links << Link.new( -        content: h.t('compliance_control_sets.actions.edit'), -        href:  h.edit_compliance_control_set_path(object.id) -      ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.edit_action_link do |l| +      l.content t('compliance_control_sets.actions.edit')      end -    if policy.clone? -      links << Link.new( -        content: h.t('actions.clone'), -        href: h.clone_compliance_control_set_path(object.id) -      ) +    instance_decorator.action_link policy: :clone, secondary: :show do |l| +      l.content t('actions.clone') +      l.href { h.clone_compliance_control_set_path(object.id) }      end -    if policy.destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.compliance_control_set_path(object.id), -        method: :delete, -        data: { confirm: h.t('compliance_control_sets.actions.destroy_confirm') } -      ) +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content +      l.href { h.compliance_control_set_path(object.id) } +      l.data confirm: h.t('compliance_control_sets.actions.destroy_confirm')      end - -    links    end -  end - diff --git a/app/decorators/import_decorator.rb b/app/decorators/import_decorator.rb index 8b00234d2..c6b1f2349 100644 --- a/app/decorators/import_decorator.rb +++ b/app/decorators/import_decorator.rb @@ -1,9 +1,7 @@ -class ImportDecorator < Draper::Decorator +class ImportDecorator < AF83::Decorator    decorates Import -  delegate_all - -  def import_status_css_class +  define_instance_method :import_status_css_class do      cls =''      cls = 'overheaded-success' if object.status == 'successful'      cls = 'overheaded-warning' if object.status == 'warning' @@ -11,28 +9,19 @@ class ImportDecorator < Draper::Decorator      cls    end -  def action_links -    policy = h.policy(object) -    links = [] - -    links << Link.new( -      content: h.t('imports.actions.download'), -      href: object.file.url -    ) +  create_action_link do |l| +    l.content t('imports.actions.new') +    l.href { h.new_workbench_import_path(workbench_id: context[:workbench]) } +  end -    if policy.destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.workbench_import_path( -          context[:workbench], -          object -        ), -        method: :delete, -        data: { confirm: h.t('imports.actions.destroy_confirm') } -      ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href { h.workbench_import_path(context[:workbench], object) }      end -    links +    instance_decorator.action_link secondary: :show do |l| +      l.content t('imports.actions.download') +      l.href { object.file.url } +    end    end -  end diff --git a/app/decorators/import_resource_decorator.rb b/app/decorators/import_resource_decorator.rb deleted file mode 100644 index 9bfd1f757..000000000 --- a/app/decorators/import_resource_decorator.rb +++ /dev/null @@ -1,10 +0,0 @@ -class ImportResourceDecorator < Draper::Decorator -  decorates ImportResource - -  delegate_all - -  def action_links -    links = [] -  end - -end diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb index 9c0cf7292..9171a6310 100644 --- a/app/decorators/line_decorator.rb +++ b/app/decorators/line_decorator.rb @@ -1,76 +1,71 @@ -class LineDecorator < Draper::Decorator +class LineDecorator < AF83::Decorator    decorates Chouette::Line -  delegate_all - -  # Requires: -  #   context: { -  #     line_referential: , -  #     current_organisation: -  #   } -  def action_links -    links = [] +  create_action_link do |l| +    l.content t('lines.actions.new') +    l.href    { h.new_line_referential_line_path(context[:line_referential]) } +  end -    links << Link.new( -      content: h.t('lines.actions.show_network'), -      href: [context[:line_referential], object.network] -    ) +  with_instance_decorator do |instance_decorator| +    ### primary (and secondary) can be +    ### - a single action +    ### - an array of actions +    ### - a boolean -    links << Link.new( -      content: h.t('lines.actions.show_company'), -      href: [context[:line_referential], object.company], -      disabled: object.company.nil? -    ) +    instance_decorator.show_action_link do |l| +      l.content t('lines.actions.show') +      l.href   { [context[:line_referential], object] } +    end -    if h.policy(Chouette::Line).create? && -      context[:line_referential].organisations.include?( -        context[:current_organisation] -      ) -      links << Link.new( -        content: h.t('lines.actions.edit'), -        href: h.edit_line_referential_line_path(context[:line_referential], object.id) -      ) +    instance_decorator.action_link secondary: :show do |l| +      l.content t('lines.actions.show_network') +      l.href   { [context[:line_referential], object.network] }      end -    if h.policy(Chouette::Line).create? && -      context[:line_referential].organisations.include?( -        context[:current_organisation] -      ) -      links << Link.new( -        content: h.t('lines.actions.new'), -        href: h.new_line_referential_line_path(context[:line_referential]) -      ) +    instance_decorator.action_link secondary: :show do |l| +      l.content  t('lines.actions.show_company') +      l.href     { [context[:line_referential], object.company] } +      l.disabled { object.company.nil? }      end -    if h.policy(object).deactivate? -      links << Link.new( -        content: h.deactivate_link_content('lines.actions.deactivate'), -        href: h.deactivate_line_referential_line_path(context[:line_referential], object), -        method: :put, -        data: {confirm: h.t('lines.actions.deactivate_confirm')}, -        extra_class: "delete-action" -      ) +    can_edit_line = ->(){ h.policy(Chouette::Line).create? && context[:line_referential].organisations.include?(context[:current_organisation]) } + +    instance_decorator.with_condition can_edit_line do +      edit_action_link do |l| +        l.content {|l| l.primary? ? h.t('actions.edit') : h.t('lines.actions.edit') } +        l.href    { h.edit_line_referential_line_path(context[:line_referential], object.id) } +      end + +      action_link on: :index, secondary: :index do |l| +        l.content t('lines.actions.new') +        l.href    { h.new_line_referential_line_path(context[:line_referential]) } +      end      end -    if h.policy(object).activate? -      links << Link.new( -        content: h.activate_link_content('lines.actions.activate'), -        href: h.activate_line_referential_line_path(context[:line_referential], object), -        method: :put, -        data: {confirm: h.t('lines.actions.activate_confirm')}, -        extra_class: "delete-action" -      ) +    ### the option :policy will automatically check for the corresponding method +    ### on the object's policy + +    instance_decorator.action_link policy: :deactivate, secondary: :show, footer: :index do |l| +      l.content  { h.deactivate_link_content('lines.actions.deactivate') } +      l.href     { h.deactivate_line_referential_line_path(context[:line_referential], object) } +      l.method   :put +      l.data     confirm: h.t('lines.actions.deactivate_confirm') +      l.add_class "delete-action"      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content('lines.actions.destroy'), -        href: h.line_referential_line_path(context[:line_referential], object), -        method: :delete, -        data: {confirm: h.t('lines.actions.destroy_confirm')} -      ) +    instance_decorator.action_link policy: :activate, secondary: :show, footer: :index do |l| +      l.content  { h.activate_link_content('lines.actions.activate') } +      l.href     { h.activate_line_referential_line_path(context[:line_referential], object) } +      l.method   :put +      l.data     confirm: h.t('lines.actions.activate_confirm') +      l.add_class "delete-action"      end -    links +    instance_decorator.destroy_action_link do |l| +      l.content  { h.destroy_link_content('lines.actions.destroy') } +      l.href     { h.line_referential_line_path(context[:line_referential], object) } +      l.data     confirm: h.t('lines.actions.destroy_confirm') +      l.add_class "delete-action" +    end    end  end diff --git a/app/decorators/network_decorator.rb b/app/decorators/network_decorator.rb index b0a19cf60..90f0d0e82 100644 --- a/app/decorators/network_decorator.rb +++ b/app/decorators/network_decorator.rb @@ -1,44 +1,42 @@ -class NetworkDecorator < Draper::Decorator +class NetworkDecorator < AF83::Decorator    decorates Chouette::Network -  delegate_all - -  # Requires: +  # Action links require:    #   context: {    #     line_referential: ,    #   } -  def action_links -    links = [] -    if h.policy(Chouette::Network).create? -      links << Link.new( -        content: h.t('networks.actions.new'), -        href: h.new_line_referential_network_path(context[:line_referential]) -      ) +  create_action_link do |l| +    l.content t('networks.actions.new') +    l.href { h.new_line_referential_network_path(context[:line_referential]) } +  end + +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href do +        h.line_referential_network_path(context[:line_referential], object) +      end      end -    if h.policy(object).update? -      links << Link.new( -        content: h.t('networks.actions.edit'), -        href: h.edit_line_referential_network_path( +    instance_decorator.action_link secondary: true, policy: :edit do |l| +      l.content t('networks.actions.edit') +      l.href do +        h.edit_line_referential_network_path(            context[:line_referential],            object          ) -      ) +      end      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content('networks.actions.destroy'), -        href: h.line_referential_network_path( +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content('networks.actions.destroy') +      l.href do +        h.line_referential_network_path(            context[:line_referential],            object -        ), -        method: :delete, -        data: { confirm: h.t('networks.actions.destroy_confirm') } -      ) +        ) +      end +      l.data confirm: h.t('networks.actions.destroy_confirm')      end - -    links    end  end diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb index 646fdea0d..54b241173 100644 --- a/app/decorators/purchase_window_decorator.rb +++ b/app/decorators/purchase_window_decorator.rb @@ -1,31 +1,37 @@ -class PurchaseWindowDecorator < Draper::Decorator +class PurchaseWindowDecorator < AF83::Decorator    decorates Chouette::PurchaseWindow -  delegate_all -  def action_links -    policy = h.policy(object) -    links = [] +  create_action_link do |l| +    l.content t('purchase_windows.actions.new') +    l.href { h.new_referential_purchase_window_path(context[:referential]) } +  end -    if policy.update? -      links << Link.new( -        content: I18n.t('actions.edit'), -        href: h.edit_referential_purchase_window_path(context[:referential].id, object) -      ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.content t('purchase_windows.actions.show') +      l.href do +        h.referential_purchase_window_path( +          context[:referential], +          object +        ) +      end      end -    if policy.destroy? -      links << Link.new( -        content: I18n.t('actions.destroy'), -        href: h.referential_purchase_window_path(context[:referential].id, object), -        method: :delete, -        data: { confirm: h.t('purchase_windows.actions.destroy_confirm') } -      ) +    instance_decorator.edit_action_link do |l| +      l.href do +        h.edit_referential_purchase_window_path(context[:referential].id, object) +      end      end -    links +    instance_decorator.destroy_action_link do |l| +      l.href do +        h.referential_purchase_window_path(context[:referential].id, object) +      end +      l.data confirm: h.t('purchase_windows.actions.destroy_confirm') +    end    end -  def bounding_dates +  define_instance_method :bounding_dates do      unless object.date_ranges.empty?        object.date_ranges.map(&:min).min..object.date_ranges.map(&:max).max      end diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb index d75ad1050..3132cbf92 100644 --- a/app/decorators/referential_decorator.rb +++ b/app/decorators/referential_decorator.rb @@ -1,88 +1,60 @@ -class ReferentialDecorator < Draper::Decorator -  delegate_all +class ReferentialDecorator < AF83::Decorator +  decorates Referential -  def action_links -    policy = h.policy(object) -    links = [] +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link +    instance_decorator.edit_action_link -    if has_feature?(:referential_vehicle_journeys) -      links << Link.new( -        content: h.t('referential_vehicle_journeys.index.title'), -        href: h.referential_vehicle_journeys_path(object) -      ) +    instance_decorator.action_link feature: :referential_vehicle_journeys, secondary: :show, on: :show do |l| +      l.content t('referential_vehicle_journeys.index.title') +      l.href { h.referential_vehicle_journeys_path(object) }      end -    if has_feature?(:purchase_windows) -      links << Link.new( -        content: h.t('purchase_windows.index.title'), -        href: h.referential_purchase_windows_path(object) -      ) +    instance_decorator.action_link feature: :purchase_windows, secondary: :show, on: :show do |l| +      l.content t('purchase_windows.index.title') +      l.href { h.referential_purchase_windows_path(object) }      end -    links << Link.new( -      content: h.t('time_tables.index.title'), -      href: h.referential_time_tables_path(object) -    ) - -    if policy.clone? -      links << Link.new( -        content: h.t('actions.clone'), -        href: h.new_referential_path(from: object.id, current_workbench_id: context[:current_workbench_id]) -      ) +    instance_decorator.action_link secondary: :show do |l| +      l.content t('time_tables.index.title') +      l.href { h.referential_time_tables_path(object) }      end -    if policy.validate? -      links << Link.new( -        content: h.t('actions.validate'), -        href: h.referential_select_compliance_control_set_path(object.id) -      ) +    instance_decorator.action_link policy: :clone, secondary: :show do |l| +      l.content t('actions.clone') +      l.href { h.new_referential_path(from: object.id, current_workbench_id: context[:current_workbench_id]) }      end -    if policy.archive? -      links << Link.new( -        content: h.t('actions.archive'), -        href: h.archive_referential_path(object.id), -        method: :put -      ) +    instance_decorator.action_link policy: :validate, secondary: :show do |l| +      l.content t('actions.validate') +      l.href { h.referential_select_compliance_control_set_path(object.id) }      end -    if policy.unarchive? -      links << Link.new( -        content: h.t('actions.unarchive'), -        href: h.unarchive_referential_path(object.id), -        method: :put -      ) +    instance_decorator.action_link policy: :archive, secondary: :show do |l| +      l.content t('actions.archive') +      l.href { h.archive_referential_path(object.id) } +      l.method :put      end -    if policy.edit? -      links << HTMLElement.new( -        :button, -        'Purger', -        type: 'button', -        data: { -          toggle: 'modal', -          target: '#purgeModal' -        } -      ) +    instance_decorator.action_link policy: :unarchive, secondary: :show, on: :show do |l| +      l.content t('actions.unarchive') +      l.href { h.unarchive_referential_path(object.id) } +      l.method :put      end -    if policy.destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.referential_path(object), -        method: :delete, -        data: { confirm: h.t('referentials.actions.destroy_confirm') } -      ) +    instance_decorator.action_link policy: :edit, secondary: :show, on: :show do |l| +      l.content 'Purger' +      l.href '#' +      l.type 'button' +      l.data {{ +        toggle: 'modal', +        target: '#purgeModal' +      }}      end -    links -  end - -  private - -  # TODO move to a base Decorator (ApplicationDecorator) -  def has_feature?(*features) -    h.has_feature?(*features) rescue false +    instance_decorator.destroy_action_link  do |l| +      l.href { h.referential_path(object) } +      l.data {{ confirm: h.t('referentials.actions.destroy_confirm') }} +    end    end -  end diff --git a/app/decorators/referential_line_decorator.rb b/app/decorators/referential_line_decorator.rb index dceb3e2a9..8f884a8e0 100644 --- a/app/decorators/referential_line_decorator.rb +++ b/app/decorators/referential_line_decorator.rb @@ -1,40 +1,43 @@ -class ReferentialLineDecorator < Draper::Decorator +class ReferentialLineDecorator < AF83::Decorator    decorates Chouette::Line -  delegate_all - -  # Requires: +  # Action links require:    #   context: {    #     referential: ,    #     current_organisation:    #   } -  def action_links -    links = [] -    links << Link.new( -      content: Chouette::Line.human_attribute_name(:footnotes), -      href: h.referential_line_footnotes_path(context[:referential], object) -    ) +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href { h.referential_line_path(context[:referential], object) } +    end -    links << Link.new( -      content: h.t('routing_constraint_zones.index.title'), -      href: h.referential_line_routing_constraint_zones_path( -        context[:referential], -        object -      ) -    ) +    instance_decorator.action_link secondary: true do |l| +      l.content Chouette::Line.human_attribute_name(:footnotes) +      l.href { h.referential_line_footnotes_path(context[:referential], object) } +    end -    if !object.hub_restricted? || -        (object.hub_restricted? && object.routes.size < 2) -      if h.policy(Chouette::Route).create? && -          context[:referential].organisation == context[:current_organisation] -        links << Link.new( -          content: h.t('routes.actions.new'), -          href: h.new_referential_line_route_path(context[:referential], object) +    instance_decorator.action_link secondary: true do |l| +      l.content h.t('routing_constraint_zones.index.title') +      l.href do +        h.referential_line_routing_constraint_zones_path( +          context[:referential], +          object          )        end      end -    links +    instance_decorator.action_link( +      if: ->() { +        (!object.hub_restricted? || +          (object.hub_restricted? && object.routes.size < 2)) && +        (h.policy(Chouette::Route).create? && +          context[:referential].organisation == context[:current_organisation]) +      }, +      secondary: true +    ) do |l| +      l.content h.t('routes.actions.new') +      l.href { h.new_referential_line_route_path(context[:referential], object) } +    end    end  end diff --git a/app/decorators/referential_network_decorator.rb b/app/decorators/referential_network_decorator.rb index 1260a38cb..ff3467188 100644 --- a/app/decorators/referential_network_decorator.rb +++ b/app/decorators/referential_network_decorator.rb @@ -1,38 +1,40 @@ -class ReferentialNetworkDecorator < Draper::Decorator +class ReferentialNetworkDecorator < AF83::Decorator    decorates Chouette::Network -  delegate_all +  # Action links require: +  #   context: { +  #     referential: , +  #   } -# Requires: -#   context: { -#     referential: , -#   } -def action_links -  links = [] - -  if h.policy(Chouette::Network).create? -    links << Link.new( -      content: h.t('networks.actions.new'), -      href: h.new_referential_network_path(context[:referential]) -    ) +  create_action_link do |l| +    l.content t('networks.actions.new') +    l.href { h.new_referential_network_path(context[:referential]) }    end -  if h.policy(object).update? -    links << Link.new( -      content: h.t('networks.actions.edit'), -      href: h.edit_referential_network_path(context[:referential], object) -    ) -  end +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href { h.referential_network_path(context[:referential], object) } +    end -  if h.policy(object).destroy? -    links << Link.new( -      content: h.destroy_link_content('networks.actions.destroy'), -      href: h.referential_network_path(context[:referential], object), -      method: :delete, -      data: { confirm: t('networks.actions.destroy_confirm') } -    ) -  end +    instance_decorator.edit_action_link do |l| +      l.content t('networks.actions.edit') +      l.href do +        h.edit_referential_network_path( +          context[:referential], +          object +        ) +      end +    end -    links +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content('networks.actions.destroy') +      l.href do +        h.referential_network_path( +          context[:referential], +          object +        ) +      end +      l.data confirm: h.t('networks.actions.destroy_confirm') +    end    end  end
\ No newline at end of file diff --git a/app/decorators/route_decorator.rb b/app/decorators/route_decorator.rb index ec7f0d6aa..7e3ea889f 100644 --- a/app/decorators/route_decorator.rb +++ b/app/decorators/route_decorator.rb @@ -1,75 +1,98 @@ -class RouteDecorator < Draper::Decorator +class RouteDecorator < AF83::Decorator    decorates Chouette::Route -  delegate_all - -  # Requires: +  # Action links require:    #   context: {    #     referential: ,    #     line:    #   } -  def action_links -    links = [] -    if object.stop_points.any? -      links << Link.new( -        content: h.t('journey_patterns.actions.index'), -        href: [ +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href do +        h.referential_line_route_path( +          context[:referential], +          context[:line], +          object +        ) +      end +    end + +    instance_decorator.edit_action_link do |l| +      l.href do +        h.edit_referential_line_route_path( +          context[:referential], +          context[:line], +          object +        ) +      end +    end + +    instance_decorator.action_link( +      if: ->() { object.stop_points.any? }, +      secondary: :show +    ) do |l| +      l.content h.t('journey_patterns.actions.index') +      l.href do +        [            context[:referential],            context[:line],            object,            :journey_patterns_collection          ] -      ) +      end      end -    if object.journey_patterns.present? -      links << Link.new( -        content: h.t('vehicle_journeys.actions.index'), -        href: [ +    instance_decorator.action_link( +      if: ->() { object.journey_patterns.present? }, +      secondary: :show +    ) do |l| +      l.content h.t('vehicle_journeys.actions.index') +      l.href do +        [            context[:referential],            context[:line],            object,            :vehicle_journeys          ] -      ) +      end      end -    links << Link.new( -      content: h.t('vehicle_journey_exports.new.title'), -      href: h.referential_line_route_vehicle_journey_exports_path( -        context[:referential], -        context[:line], -        object, -        format: :zip -      ) -    ) +    instance_decorator.action_link secondary: :show do |l| +      l.content h.t('vehicle_journey_exports.new.title') +      l.href do +        h.referential_line_route_vehicle_journey_exports_path( +          context[:referential], +          context[:line], +          object, +          format: :zip +        ) +      end +    end -    if h.policy(object).duplicate? -      links << Link.new( -        content: h.t('routes.duplicate.title'), -        href: h.duplicate_referential_line_route_path( +    instance_decorator.action_link( +      secondary: :show, +      policy: :duplicate +    ) do |l| +      l.content h.t('routes.duplicate.title') +      l.href do +        h.duplicate_referential_line_route_path(            context[:referential],            context[:line],            object -        ), -        method: :post -      ) +        ) +      end      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.referential_line_route_path( +    instance_decorator.destroy_action_link do |l| +      l.href do +        h.referential_line_route_path(            context[:referential],            context[:line],            object -        ), -        method: :delete, -        data: { confirm: h.t('routes.actions.destroy_confirm') } -      ) +        ) +      end +      l.data confirm: h.t('routes.actions.destroy_confirm')      end - -    links    end  end diff --git a/app/decorators/routing_constraint_zone_decorator.rb b/app/decorators/routing_constraint_zone_decorator.rb index 0b438a554..962625fa7 100644 --- a/app/decorators/routing_constraint_zone_decorator.rb +++ b/app/decorators/routing_constraint_zone_decorator.rb @@ -1,42 +1,56 @@ -class RoutingConstraintZoneDecorator < Draper::Decorator +class RoutingConstraintZoneDecorator < AF83::Decorator    decorates Chouette::RoutingConstraintZone -  delegate_all - -  # Requires: +  # Action links require:    #   context: {    #     referential: ,    #     line:    #   } -  def action_links -    links = [] -    if h.policy(object).update? -      links << Link.new( -        content: h.t('actions.edit'), -        href: h.edit_referential_line_routing_constraint_zone_path( +  create_action_link( +    if: ->() { +      h.policy(Chouette::RoutingConstraintZone).create? && +        context[:referential].organisation == h.current_organisation +    } +  ) do |l| +    l.href do +      h.new_referential_line_routing_constraint_zone_path( +       context[:referential], +       context[:line] +     ) +    end +  end + +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href do +        h.referential_line_routing_constraint_zone_path(            context[:referential],            context[:line],            object          ) -      ) +      end      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.referential_line_routing_constraint_zone_path( +    instance_decorator.edit_action_link do |l| +      l.href do +        h.edit_referential_line_routing_constraint_zone_path(            context[:referential],            context[:line],            object -        ), -        method: :delete, -        data: { -          confirm: h.t('routing_constraint_zones.actions.destroy_confirm') -        } -      ) +        ) +      end      end -    links +    instance_decorator.destroy_action_link do |l| +      l.href do +        h.referential_line_routing_constraint_zone_path( +          context[:referential], +          context[:line], +          object +        ) +      end +      l.data confirm: h.t('routing_constraint_zones.actions.destroy_confirm') +    end    end  end diff --git a/app/decorators/stop_area_decorator.rb b/app/decorators/stop_area_decorator.rb index 32f6e1d2b..2e57da0e4 100644 --- a/app/decorators/stop_area_decorator.rb +++ b/app/decorators/stop_area_decorator.rb @@ -1,66 +1,70 @@ -class StopAreaDecorator < Draper::Decorator +class StopAreaDecorator < AF83::Decorator    decorates Chouette::StopArea -  delegate_all - -  def common_action_links(stop_area = nil) -    top_links, bottom_links = [], [] -    stop_area ||= object +  create_action_link do |l| +    l.content t('stop_areas.actions.new') +    l.href { h.new_stop_area_referential_stop_area_path } +  end -    if h.policy(stop_area).update? -      top_links << Link.new( -        content: h.t('stop_areas.actions.edit'), -        href: h.edit_stop_area_referential_stop_area_path( -          stop_area.stop_area_referential, -          stop_area +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href do +        h.stop_area_referential_stop_area_path( +          object.stop_area_referential, +          object          ) -      ) +      end      end -    if h.policy(stop_area).destroy? -      bottom_links << Link.new( -        content: h.destroy_link_content('stop_areas.actions.destroy'), -        href: h.stop_area_referential_stop_area_path( -          stop_area.stop_area_referential, -          stop_area -        ), -        method: :delete, -        data: { confirm: t('stop_areas.actions.destroy_confirm') } -      ) +    instance_decorator.edit_action_link do |l| +      l.content h.t('stop_areas.actions.edit') +      l.href do +        h.edit_stop_area_referential_stop_area_path( +          object.stop_area_referential, +          object +        ) +      end      end -    [top_links, bottom_links] -  end - -  def action_links(stop_area = nil) -    stop_area ||= object -    top_links, bottom_links = common_action_links(stop_area) -    links = [] - -    if h.policy(object).deactivate? -      links << Link.new( -        content: h.deactivate_link_content('stop_areas.actions.deactivate'), -        href: h.deactivate_stop_area_referential_stop_area_path(stop_area.stop_area_referential, object), -        method: :put, -        data: {confirm: h.t('stop_areas.actions.deactivate_confirm')}, -        extra_class: "delete-action" -      ) +    instance_decorator.action_link policy: :deactivate, secondary: true do |l| +      l.content h.deactivate_link_content('stop_areas.actions.deactivate') +      l.href do +        h.deactivate_stop_area_referential_stop_area_path( +          object.stop_area_referential, +          object +        ) +      end +      l.method :put +      l.data confirm: h.t('stop_areas.actions.deactivate_confirm') +      l.add_class 'delete-action'      end -    if h.policy(object).activate? -      links << Link.new( -        content: h.activate_link_content('stop_areas.actions.activate'), -        href: h.activate_stop_area_referential_stop_area_path(stop_area.stop_area_referential, object), -        method: :put, -        data: {confirm: h.t('stop_areas.actions.activate_confirm')}, -        extra_class: "delete-action" -      ) +    instance_decorator.action_link policy: :activate, secondary: true do |l| +      l.content h.activate_link_content('stop_areas.actions.activate') +      l.href do +        h.activate_stop_area_referential_stop_area_path( +          object.stop_area_referential, +          object +        ) +      end +      l.method :put +      l.data confirm: h.t('stop_areas.actions.activate_confirm') +      l.add_class 'delete-action'      end -    top_links + links + bottom_links +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content('stop_areas.actions.destroy') +      l.href do +        h.stop_area_referential_stop_area_path( +          object.stop_area_referential, +          object +        ) +      end +      l.data confirm: h.t('stop_areas.actions.destroy_confirm') +    end    end -  def waiting_time_text +  define_instance_method :waiting_time_text do      return '-' if [nil, 0].include? waiting_time      h.t('stop_areas.waiting_time_format', value: waiting_time)    end diff --git a/app/decorators/stop_point_decorator.rb b/app/decorators/stop_point_decorator.rb index 27e1a7058..e777e2b56 100644 --- a/app/decorators/stop_point_decorator.rb +++ b/app/decorators/stop_point_decorator.rb @@ -1,9 +1,35 @@ -class StopPointDecorator < StopAreaDecorator +class StopPointDecorator < AF83::Decorator    decorates Chouette::StopPoint -  delegate_all +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href do +        h.referential_stop_area_path( +          object.referential, +          object.stop_area +        ) +      end +    end -  def action_links -    common_action_links(object.stop_area).flatten +    instance_decorator.edit_action_link do |l| +      l.content h.t('stop_points.actions.edit') +      l.href do +        h.edit_stop_area_referential_stop_area_path( +          object.stop_area.stop_area_referential, +          object.stop_area +        ) +      end +    end + +    instance_decorator.destroy_action_link do |l| +      l.content h.destroy_link_content('stop_points.actions.destroy') +      l.href do +        h.referential_stop_area_path( +          object.referential, +          object.stop_area +        ) +      end +      l.data confirm: h.t('stop_points.actions.destroy_confirm') +    end    end  end diff --git a/app/decorators/time_table_decorator.rb b/app/decorators/time_table_decorator.rb index c6eeac176..9a56fc2ee 100644 --- a/app/decorators/time_table_decorator.rb +++ b/app/decorators/time_table_decorator.rb @@ -1,55 +1,53 @@ -class TimeTableDecorator < Draper::Decorator +class TimeTableDecorator < AF83::Decorator    decorates Chouette::TimeTable -  delegate_all +  create_action_link if: ->{ h.policy(Chouette::TimeTable).create? && context[:referential].organisation == h.current_organisation } do |l| +    l.href { h.new_referential_time_table_path(context[:referential]) } +  end + +  with_instance_decorator do |instance_decorator| +    instance_decorator.show_action_link do |l| +      l.href { [context[:referential], object] } +    end -  # Requires: -  #   context: { -  #     referential: , -  #   } -  def action_links -    links = [] +    instance_decorator.edit_action_link do |l| +      l.href { [context[:referential], object] } +    end -    if object.calendar -      links << Link.new( -        content: h.t('actions.actualize'), -        href: h.actualize_referential_time_table_path( +    instance_decorator.action_link if: ->{ object.calendar }, secondary: true do |l| +      l.content t('actions.actualize') +      l.href do +         h.actualize_referential_time_table_path(            context[:referential],            object -        ), -        method: :post -      ) +        ) +      end +      l.method :post      end -    if h.policy(object).edit? -      links << Link.new( -        content: h.t('actions.combine'), -        href: h.new_referential_time_table_time_table_combination_path( +    instance_decorator.action_link policy: :edit, secondary: true do |l| +      l.content t('actions.combine') +      l.href do +        h.new_referential_time_table_time_table_combination_path(            context[:referential],            object          ) -      ) +      end      end -    if h.policy(object).duplicate? -      links << Link.new( -        content: h.t('actions.duplicate'), -        href: h.duplicate_referential_time_table_path( +    instance_decorator.action_link policy: :duplicate, secondary: true do |l| +      l.content t('actions.duplicate') +      l.href do +        h.duplicate_referential_time_table_path(            context[:referential],            object          ) -      ) +      end      end -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content, -        href: h.referential_time_table_path(context[:referential], object), -        method: :delete, -        data: { confirm: h.t('time_tables.actions.destroy_confirm') } -      ) +    instance_decorator.destroy_action_link  do |l| +      l.href { h.referential_time_table_path(context[:referential], object) } +      l.data {{ confirm: h.t('time_tables.actions.destroy_confirm') }}      end - -    links    end  end diff --git a/app/helpers/compliance_control_sets_helper.rb b/app/helpers/compliance_control_sets_helper.rb index 57e6d9608..448d5c008 100644 --- a/app/helpers/compliance_control_sets_helper.rb +++ b/app/helpers/compliance_control_sets_helper.rb @@ -100,7 +100,8 @@ module ComplianceControlSetsHelper        ],        sortable: true,        cls: 'table has-filter has-search', -      model: ComplianceControl +      model: ComplianceControl, +      action: :index      end      metas = content_tag :div, I18n.t('compliance_control_blocks.metas.control', count: compliance_controls.count), class: 'pull-right'      table + metas diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb index 9ead7180a..f48075ed9 100644 --- a/app/helpers/table_builder_helper.rb +++ b/app/helpers/table_builder_helper.rb @@ -86,30 +86,36 @@ module TableBuilderHelper      overhead: [],      # Possibility to override the result of collection.model -    model: nil +    model: nil, + +    #overrides the params[:action] value +    action: nil    )      content_tag :table, -      thead(collection, columns, sortable, selectable, links.any?, overhead, model || collection.model) + -        tbody(collection, columns, selectable, links, overhead), +      thead(collection, columns, sortable, selectable, links.any?, overhead, model || collection.model, action || params[:action]) + +        tbody(collection, columns, selectable, links, overhead, model, action || params[:action]),        class: cls    end -  def self.item_row_class_name collection -    if collection.respond_to?(:model) -      model_name = collection.model.name -    elsif collection.respond_to?(:first) -      model_name = collection.first.class.name -    else -      model_name = "item" -    end +  def self.item_row_class_name collection, model=nil +    model_name = model&.name + +    model_name ||= +      if collection.respond_to?(:model) +        collection.model.name +      elsif collection.respond_to?(:first) +        collection.first.class.name +      else +        "item" +      end      model_name.split("::").last.parameterize    end    private -  def thead(collection, columns, sortable, selectable, has_links, overhead, model ) +  def thead(collection, columns, sortable, selectable, has_links, overhead, model, action)      content_tag :thead do        # Inserts overhead content if any specified        over_head = '' @@ -188,7 +194,9 @@ module TableBuilderHelper          end          # Inserts a blank column for the gear menu -        if has_links || collection.last.try(:action_links).try(:any?) +        last_item = collection.last +        action_links = last_item && last_item.respond_to?(:action_links) && (last_item&.action_links&.is_a?(AF83::Decorator::ActionLinks) ? last_item.action_links(action) : last_item.action_links) +        if has_links || action_links.try(:any?)            hcont << content_tag(:th, '')          end @@ -199,7 +207,7 @@ module TableBuilderHelper      end    end -  def tr item, columns, selectable, links, overhead, model_name +  def tr item, columns, selectable, links, overhead, model_name, action      klass = "#{model_name}-#{item.id}"      content_tag :tr, class: klass do        bcont = [] @@ -267,10 +275,12 @@ module TableBuilderHelper          end        end -      if links.any? || item.try(:action_links).try(:any?) +      action_links = item && item.respond_to?(:action_links) && (item.action_links.is_a?(AF83::Decorator::ActionLinks) ? item.action_links(action) : item.action_links) + +      if links.any? || action_links.try(:any?)          bcont << content_tag(            :td, -          build_links(item, links), +          build_links(item, links, action),            class: 'actions'          )        end @@ -279,12 +289,12 @@ module TableBuilderHelper      end    end -  def tbody(collection, columns, selectable, links, overhead) -    model_name = TableBuilderHelper.item_row_class_name collection +  def tbody(collection, columns, selectable, links, overhead, model = nil, action) +    model_name = TableBuilderHelper.item_row_class_name collection, model      content_tag :tbody do        collection.map do |item| -        tr item, columns, selectable, links, overhead, model_name +        tr item, columns, selectable, links, overhead, model_name, action        end.join.html_safe      end    end @@ -297,7 +307,7 @@ module TableBuilderHelper      end    end -  def build_links(item, links) +  def build_links(item, links, action)      trigger = content_tag(        :div,        class: 'btn dropdown-toggle', @@ -306,13 +316,26 @@ module TableBuilderHelper        content_tag :span, '', class: 'fa fa-cog'      end -    menu = content_tag :ul, class: 'dropdown-menu' do -      ( -        CustomLinks.new(item, pundit_user, links, referential, workgroup).links + -        item.action_links.select { |link| link.is_a?(Link) } -      ).map do |link| -        gear_menu_link(link) -      end.join.html_safe +    action_links = item.action_links +    if action_links.is_a?(AF83::Decorator::ActionLinks) +      menu = content_tag :div, class: 'dropdown-menu' do +        item.action_links(action).grouped_by(:primary, :secondary, :footer).map do |group, _links| +          if _links.any? +            content_tag :ul, class: group do +              _links.map{|link| gear_menu_link(link)}.join.html_safe +            end +          end +        end.join.html_safe +      end +    else +      menu = content_tag :ul, class: 'dropdown-menu' do +        ( +          CustomLinks.new(item, pundit_user, links, referential, workgroup).links + +          action_links.select { |link| link.is_a?(Link) } +        ).map do |link| +          gear_menu_link(link) +        end.join.html_safe +      end      end      content_tag :div, trigger + menu, class: 'btn-group' diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb index e89a0ec7f..22bcc1de1 100644 --- a/app/models/chouette/purchase_window.rb +++ b/app/models/chouette/purchase_window.rb @@ -6,7 +6,9 @@ module Chouette      # include ChecksumSupport      include ObjectidSupport      include PeriodSupport +    include ChecksumSupport      extend Enumerize +      enumerize :color, in: %w(#9B9B9B #FFA070 #C67300 #7F551B #41CCE3 #09B09C #3655D7 #6321A0 #E796C6 #DD2DAA)      has_paper_trail @@ -16,7 +18,7 @@ module Chouette      validates_presence_of :name, :referential      scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) } -     +      def self.ransackable_scopes(auth_object = nil)        [:contains_date]      end @@ -29,6 +31,12 @@ module Chouette        "IBOO-#{self.referential.id}-#{self.id}"      end +    def checksum_attributes +      attrs = ['name', 'color', 'referential_id'] +      ranges_attrs = date_ranges.map{|r| [r.first, r.last]}.flatten.sort +      self.slice(*attrs).values + ranges_attrs +    end +      # def checksum_attributes      # end diff --git a/app/models/merge.rb b/app/models/merge.rb index 91be27f2c..62bf581d6 100644 --- a/app/models/merge.rb +++ b/app/models/merge.rb @@ -20,6 +20,10 @@ class Merge < ActiveRecord::Base      referentials.first(3).map { |r| r.name.truncate(10) }.join(',')    end +  def full_names +    referentials.map(&:name).to_sentence +  end +    attr_reader :new    def merge! diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim index 60bc9793d..309f0e6f9 100644 --- a/app/views/calendars/index.html.slim +++ b/app/views/calendars/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :calendars, current_workgroup -- content_for :page_header_actions do -  - if policy(Calendar).create? -    = link_to(t('actions.add'), new_workgroup_calendar_path(current_workgroup), class: 'btn btn-default')  .page_content    .container-fluid @@ -35,7 +32,6 @@                  attribute: Proc.new { |c| t("#{c.try(:shared)}") } \                ) \              ], -            links: [:show, :edit],              cls: 'table has-filter'            = new_pagination @calendars, 'pull-right' diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index 9f7512173..cd2be2bd1 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -1,25 +1,13 @@  - breadcrumb :calendar, @workgroup, @calendar  - page_header_content_for @calendar -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @calendar.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content -- if policy(@calendar).edit? -  - content_for :page_header_actions do -      = link_to(t('actions.edit'), edit_workgroup_calendar_path(@workgroup, @calendar), class: 'btn btn-default')  .page_content    .container-fluid      .row        .col-lg-6.col-md-6.col-sm-12.col-xs-12          = definition_list t('metadatas'), -          { 'Nom court' => @calendar.try(:short_name), -            Calendar.human_attribute_name(:shared) => t("#{@calendar.shared}"), -            'Organisation' => @calendar.organisation.name, -            Calendar.human_attribute_name(:dates) =>  @calendar.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe, -            Calendar.human_attribute_name(:date_ranges) => @calendar.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } +          { 'Nom court' => resource.try(:short_name), +            Calendar.human_attribute_name(:shared) => t("#{resource.shared}"), +            'Organisation' => resource.organisation.name, +            Calendar.human_attribute_name(:dates) =>  resource.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe, +            Calendar.human_attribute_name(:date_ranges) => resource.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } diff --git a/app/views/companies/index.html.slim b/app/views/companies/index.html.slim index e031f3776..9f1502e54 100644 --- a/app/views/companies/index.html.slim +++ b/app/views/companies/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :companies, @line_referential -- content_for :page_header_actions do -  - if policy(Chouette::Company).create? -    = link_to(t('companies.actions.new'), new_line_referential_company_path(@line_referential), class: 'btn btn-primary')  .page_content    .container-fluid @@ -34,7 +31,6 @@                  end \                ) \              ], -            links: [:show],              cls: 'table has-search'            = new_pagination @companies, 'pull-right' diff --git a/app/views/companies/show.html.slim b/app/views/companies/show.html.slim index 0d6b4aae3..ca0a410b3 100644 --- a/app/views/companies/show.html.slim +++ b/app/views/companies/show.html.slim @@ -1,25 +1,13 @@  - breadcrumb :company, @company -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - if policy(Chouette::Company).create? -        = link_to t('companies.actions.new'), new_line_referential_company_path(@line_referential), class: 'btn btn-primary' -      - if policy(@company).update? -        = link_to t('companies.actions.edit'), edit_line_referential_company_path(@line_referential, @company), class: 'btn btn-primary' -      - if policy(@company).destroy? -        = link_to line_referential_company_path(@line_referential, @company), method: :delete, data: {confirm:  t('companies.actions.destroy_confirm')}, class: 'btn btn-primary' do -          span.fa.fa-trash -          span = t('companies.actions.destroy')  - page_header_content_for @company -  .page_content    .container-fluid      .row        .col-lg-6.col-md-6.col-sm-12.col-xs-12          = definition_list t('metadatas'),            { 'ID Codif' => @company.try(:get_objectid).try(:short_id), -            Chouette::Company.human_attribute_name(:phone) => @company.phone, -            Chouette::Company.human_attribute_name(:email) => @company.email, -            Chouette::Company.human_attribute_name(:url) => @company.url } +            Chouette::Company.human_attribute_name(:phone) => resource.phone, +            Chouette::Company.human_attribute_name(:email) => resource.email, +            Chouette::Company.human_attribute_name(:url) => resource.url } diff --git a/app/views/compliance_check_sets/index.html.slim b/app/views/compliance_check_sets/index.html.slim index f15e85bdd..ead467174 100644 --- a/app/views/compliance_check_sets/index.html.slim +++ b/app/views/compliance_check_sets/index.html.slim @@ -41,7 +41,6 @@                  ), \                ],                sortable: true, -              links: [:show],                cls: 'table has-filter has-search'      - unless @compliance_check_sets.any?        .row.mt-xs diff --git a/app/views/compliance_check_sets/show.html.slim b/app/views/compliance_check_sets/show.html.slim index 4db2e805f..4df14ab06 100644 --- a/app/views/compliance_check_sets/show.html.slim +++ b/app/views/compliance_check_sets/show.html.slim @@ -1,15 +1,4 @@ -- breadcrumb :compliance_check_set, @workbench, @compliance_check_set -/ PageHeader -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @compliance_check_set.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content - +- breadcrumb :compliance_check_sets, @workbench, @compliance_check_set  - page_header_content_for @compliance_check_set  / PageContent diff --git a/app/views/compliance_control_sets/index.html.slim b/app/views/compliance_control_sets/index.html.slim index 28d2254bf..144a4e5b9 100644 --- a/app/views/compliance_control_sets/index.html.slim +++ b/app/views/compliance_control_sets/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :compliance_control_sets -- content_for :page_header_actions do -  - if policy(ComplianceControlSet).create? -    = link_to(t('compliance_control_sets.actions.new'), new_compliance_control_set_path, class: 'btn btn-default')  .page_content    .container-fluid @@ -38,7 +35,6 @@                  ) \                ],                sortable: true, -              links: [:show],                cls: 'table has-filter has-search'            = new_pagination @compliance_control_sets, 'pull-right' diff --git a/app/views/compliance_control_sets/show.html.slim b/app/views/compliance_control_sets/show.html.slim index d915bbdaf..59100681d 100644 --- a/app/views/compliance_control_sets/show.html.slim +++ b/app/views/compliance_control_sets/show.html.slim @@ -1,17 +1,5 @@  - breadcrumb :compliance_control_set, @compliance_control_set  - page_header_content_for @compliance_control_set -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @compliance_control_set.action_links.each do |link| -        - if link.is_a?(HTMLElement) -          = link.to_html(class: 'btn btn-primary') -        - else -          = link_to link.href, -              method: link.method, -              data: link.data, -              class: 'btn btn-primary' do -            = link.content  .page_content    .container-fluid diff --git a/app/views/compliance_controls/show.html.slim b/app/views/compliance_controls/show.html.slim index 54b07abf1..8a65bb864 100644 --- a/app/views/compliance_controls/show.html.slim +++ b/app/views/compliance_controls/show.html.slim @@ -1,6 +1,4 @@  - breadcrumb :compliance_control, @compliance_control -- content_for :page_header_actions do -  = link_to(t('actions.edit'), edit_compliance_control_set_compliance_control_path(params[:compliance_control_set_id], params[:id]), class: 'btn btn-default')  - page_header_content_for @compliance_control diff --git a/app/views/import_resources/index.html.slim b/app/views/import_resources/index.html.slim index 0c21a9e09..6b4e60026 100644 --- a/app/views/import_resources/index.html.slim +++ b/app/views/import_resources/index.html.slim @@ -43,7 +43,6 @@                    end \                  ), \                ], -              links: [],                cls: 'table has-search'          - else            .col-lg-12 diff --git a/app/views/imports/index.html.slim b/app/views/imports/index.html.slim index 856b715e0..4fc077bd6 100644 --- a/app/views/imports/index.html.slim +++ b/app/views/imports/index.html.slim @@ -1,6 +1,4 @@  - breadcrumb :imports, @workbench -- content_for :page_header_actions do -  = link_to(t('imports.actions.new'), new_workbench_import_path(workbench_id: @workbench), class: 'btn btn-primary')  .page_content    .container-fluid @@ -34,7 +32,6 @@                  attribute: 'creator' \                ) \              ], -            links: [:show],              cls: 'table has-search'            = new_pagination @imports, 'pull-right' diff --git a/app/views/imports/show.html.slim b/app/views/imports/show.html.slim index cf137867b..7a9d02077 100644 --- a/app/views/imports/show.html.slim +++ b/app/views/imports/show.html.slim @@ -1,13 +1,4 @@  - breadcrumb :import, @workbench, @import -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @import.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  - page_header_content_for @import @@ -54,7 +45,6 @@                  sortable: false, \                ) \              ], -            links: [],              cls: 'table',              overhead: [ \                {}, \ diff --git a/app/views/layouts/navigation/_page_header.html.slim b/app/views/layouts/navigation/_page_header.html.slim index 90fd7d855..e407e53da 100644 --- a/app/views/layouts/navigation/_page_header.html.slim +++ b/app/views/layouts/navigation/_page_header.html.slim @@ -1,23 +1,36 @@ -div.page_header -  div.container-fluid -    div.row -      div.col-lg-9.col-md-8.col-sm-7.col-xs-7 +- action_links = resource.action_links(params[:action]) rescue nil +- action_links ||= decorated_collection.action_links(params[:action]) rescue nil + +.page_header +  .container-fluid +    .row +      .col-lg-9.col-md-8.col-sm-7.col-xs-7          - if defined?(resource_class) -          div.page-icon +          .page-icon              span.sb class="sb-#{resource_class.model_name.name.underscore}" -        div.page-title +        .page-title            - if content_for? :page_header_title              h1 = yield :page_header_title            - else              - if defined?(resource_class)                h1 = t("#{resource_class.model_name.name.underscore.pluralize}.#{params[:action]}.title") -      div.col-lg-3.col-md-4.col-sm-5.col-xs-5.text-right -        div.page-action +      .col-lg-3.col-md-4.col-sm-5.col-xs-5.text-right +        .page-action            - if content_for? :page_header_meta              = yield :page_header_meta            - if content_for? :page_header_actions              = yield :page_header_actions +          - if action_links&.primary&.any? +            - action_links.primary.each do |link| +              = link.to_html do |l| +                - l.class "btn btn-default #{l.disabled ? "disabled" : ""}" +    - if action_links&.secondary&.any? +      .row.mb-sm +        .col-lg-12.text-right +          - action_links.secondary.each do |link| +            = link.to_html do |l| +              - l.class "btn btn-primary #{l.disabled ? "disabled" : ""}"      - if content_for? :page_header_content        = yield :page_header_content diff --git a/app/views/lines/index.html.slim b/app/views/lines/index.html.slim index b62263263..2d64e5f73 100644 --- a/app/views/lines/index.html.slim +++ b/app/views/lines/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :lines, @line_referential -- content_for :page_header_actions do -  - if policy(Chouette::Line).create? -    = link_to(t('lines.actions.new'), new_line_referential_line_path(@line_referential), class: 'btn btn-primary')  .page_content    .container-fluid @@ -52,7 +49,6 @@                  attribute: Proc.new { |n| n.transport_submode.present? ? t("enumerize.transport_submode.#{n.try(:transport_submode)}") : "-" } \                ) \              ], -            links: [:show],              cls: 'table has-filter has-search'            = new_pagination @lines, 'pull-right' diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim index 83244f739..34b907bdd 100644 --- a/app/views/lines/show.html.slim +++ b/app/views/lines/show.html.slim @@ -1,14 +1,4 @@  - breadcrumb :line, @line -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @line.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: "btn btn-primary #{link.disabled ? "disabled" : ""}" do -              = link.content -  - page_header_content_for @line  .page_content diff --git a/app/views/networks/index.html.slim b/app/views/networks/index.html.slim index b13c73e9e..6aeb140cc 100644 --- a/app/views/networks/index.html.slim +++ b/app/views/networks/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :networks, @line_referential -- content_for :page_header_actions do -  - if policy(Chouette::Network).create? -    = link_to(t('networks.actions.new'), new_line_referential_network_path(@line_referential), class: 'btn btn-primary')  .page_content    .container-fluid diff --git a/app/views/networks/show.html.slim b/app/views/networks/show.html.slim index 8e40a13b2..527fb8d5b 100644 --- a/app/views/networks/show.html.slim +++ b/app/views/networks/show.html.slim @@ -1,14 +1,6 @@  - breadcrumb :network, @network  - page_header_content_for @network -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @network.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content +  .page_content    .container-fluid      .row diff --git a/app/views/purchase_windows/index.html.slim b/app/views/purchase_windows/index.html.slim index 04f9fb0a8..0dba86935 100644 --- a/app/views/purchase_windows/index.html.slim +++ b/app/views/purchase_windows/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :purchase_windows, @referential -- content_for :page_header_actions do -  - if policy(Chouette::PurchaseWindow).create? -    = link_to(t('actions.add'), new_referential_purchase_window_path(@referential), class: 'btn btn-default')  .page_content    .container-fluid @@ -32,7 +29,6 @@                  sortable: false \                ) \              ], -            links: [:show],              cls: 'table has-filter'            = new_pagination @purchase_windows, 'pull-right' diff --git a/app/views/purchase_windows/show.html.slim b/app/views/purchase_windows/show.html.slim index 4ddde1706..4e836f424 100644 --- a/app/views/purchase_windows/show.html.slim +++ b/app/views/purchase_windows/show.html.slim @@ -1,14 +1,5 @@  - breadcrumb :purchase_window, @referential, @purchase_window  - page_header_content_for @purchase_window -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @purchase_window.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  .page_content    .container-fluid diff --git a/app/views/referential_companies/index.html.slim b/app/views/referential_companies/index.html.slim index 07de2bc9d..3bff448c7 100644 --- a/app/views/referential_companies/index.html.slim +++ b/app/views/referential_companies/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :referential_companies, @referential -- content_for :page_header_actions do -  - if policy(Chouette::Company).create? -    = link_to(t('companies.actions.new'), new_referential_company_path(@referential), class: 'btn btn-default')  .page_content    .container-fluid @@ -46,7 +43,6 @@                  attribute: 'url' \                ) \              ], -            links: [:show],              cls: 'table has-search'            = new_pagination @companies, 'pull-right' diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim index cfba8cab3..02d605d8c 100644 --- a/app/views/referential_lines/show.html.slim +++ b/app/views/referential_lines/show.html.slim @@ -1,13 +1,4 @@  - breadcrumb :referential_line, @referential, @line -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @line.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  - page_header_content_for @line  .page_content @@ -79,8 +70,8 @@                      attribute: Proc.new{ |r| r.try(:journey_patterns).count } \                    ) \                  ], -                links: [:show, :edit], -                cls: 'table has-search' +                cls: 'table has-search', +                action: :index                = new_pagination @routes, 'pull-right' diff --git a/app/views/referential_networks/index.html.slim b/app/views/referential_networks/index.html.slim index d556e7e5e..efa28dc05 100644 --- a/app/views/referential_networks/index.html.slim +++ b/app/views/referential_networks/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :referential_networks, @referential -- if policy(Chouette::Network).create? -  - content_for :page_header_actions do -      = link_to(t('networks.actions.new'), new_referential_network_path(@referential), class: 'btn btn-default')  .page_content    .container-fluid diff --git a/app/views/referential_networks/show.html.slim b/app/views/referential_networks/show.html.slim index 7de304671..3d13d9211 100644 --- a/app/views/referential_networks/show.html.slim +++ b/app/views/referential_networks/show.html.slim @@ -1,14 +1,5 @@  - breadcrumb :referential_network, @referential, @network  - page_header_content_for @network -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @network.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  / PageContent  .page_content diff --git a/app/views/referential_stop_areas/_form.html.slim b/app/views/referential_stop_areas/_form.html.slim index 50f5d4aaf..8181ec3f3 100644 --- a/app/views/referential_stop_areas/_form.html.slim +++ b/app/views/referential_stop_areas/_form.html.slim @@ -7,7 +7,7 @@        = form.inputs do          = form.input :id, as: :hidden          = form.input :name, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")} -        = form.input :stop_area_type, as: :select, :input_html => { :disabled => !@stop_area.new_record? }, :collection => Chouette::StopArea.area_type.options, :include_blank => false } +        = form.input :stop_area_type, as: :select, :input_html => { :disabled => !@stop_area.new_record? }, :collection => Chouette::StopArea.area_type.options, :include_blank => false          .location_info            h3 = t("stop_areas.stop_area.localisation") diff --git a/app/views/referential_stop_areas/show.html.slim b/app/views/referential_stop_areas/show.html.slim index 0470b4654..cb04ab7a6 100644 --- a/app/views/referential_stop_areas/show.html.slim +++ b/app/views/referential_stop_areas/show.html.slim @@ -1,14 +1,5 @@  - breadcrumb :referential_stop_area, @referential, @stop_area  - page_header_content_for @stop_area -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @stop_area.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  .page_content    .container-fluid diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index cbb622c44..6c88f5b81 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -1,21 +1,5 @@  - breadcrumb @referential  - page_header_content_for @referential -- content_for :page_header_actions do -  - unless (@referential.referential_read_only? || !policy(@referential).edit?) -    = link_to(t('actions.edit'), edit_referential_path(@referential), class: 'btn btn-default') - -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @referential.action_links.each do |link| -        - if link.is_a?(HTMLElement) -          = link.to_html(class: 'btn btn-primary') -        - else -          = link_to link.href, -              method: link.method, -              data: link.data, -              class: 'btn btn-primary' do -                = link.content  .page_content    .container-fluid @@ -72,8 +56,8 @@                  attribute: Proc.new { |n| n&.company&.name || "-" } \                ) \              ], -            links: [:show], -            cls: 'table has-filter has-search' +            cls: 'table has-filter has-search', +            action: :index            = new_pagination @reflines, 'pull-right' diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim index 644f79022..375d7c57b 100644 --- a/app/views/routes/show.html.slim +++ b/app/views/routes/show.html.slim @@ -1,18 +1,5 @@  - breadcrumb :route, @referential, @route  - page_header_content_for @route -- content_for :page_header_actions do -  - if policy(@route).edit? -    = link_to(t('actions.edit'), edit_referential_line_route_path(@referential, @line, @route), class: 'btn btn-default') - -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @route.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  .page_content    .container-fluid @@ -67,9 +54,9 @@                  attribute: Proc.new { |s| t("stop_points.stop_point.for_alighting.#{s.for_alighting}") } \                ) \              ], -            links: [:show],              sortable: false, -            cls: 'table has-stoppoints' +            cls: 'table has-stoppoints', +            action: :index          - else            = replacement_msg t('stop_areas.search_no_results') diff --git a/app/views/routing_constraint_zones/index.html.slim b/app/views/routing_constraint_zones/index.html.slim index 7c54fca68..2f67b467e 100644 --- a/app/views/routing_constraint_zones/index.html.slim +++ b/app/views/routing_constraint_zones/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :routing_constraint_zones, @referential, @line -- content_for :page_header_actions do -  - if (policy(Chouette::RoutingConstraintZone).create? && @referential.organisation == current_organisation) -    = link_to(t('actions.new'), new_referential_line_routing_constraint_zone_path(@referential, @line), class: 'btn btn-primary')  .page_content    .container-fluid diff --git a/app/views/routing_constraint_zones/show.html.slim b/app/views/routing_constraint_zones/show.html.slim index 3d1988545..8c8e9b17a 100644 --- a/app/views/routing_constraint_zones/show.html.slim +++ b/app/views/routing_constraint_zones/show.html.slim @@ -1,14 +1,5 @@  - breadcrumb :routing_constraint_zone, @referential, @line, @routing_constraint_zone  - page_header_content_for @routing_constraint_zone -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @routing_constraint_zone.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  .page_content    .container-fluid diff --git a/app/views/stop_areas/index.html.slim b/app/views/stop_areas/index.html.slim index 63e99fd75..71c7f995c 100644 --- a/app/views/stop_areas/index.html.slim +++ b/app/views/stop_areas/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :stop_areas, @stop_area_referential -- content_for :page_header_actions do -  - if policy(Chouette::StopArea).create? -    = link_to(t('stop_areas.actions.new'), new_stop_area_referential_stop_area_path(@stop_area_referential), class: 'btn btn-primary')  .page_content    .container-fluid @@ -51,7 +48,6 @@                  attribute: Proc.new { |s| Chouette::AreaType.find(s.area_type).try :label } \                ), \              ], -            links: [:show],              cls: 'table has-filter has-search'            = new_pagination @stop_areas, 'pull-right' diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim index f9de34a98..b5ec8ac00 100644 --- a/app/views/stop_areas/show.html.slim +++ b/app/views/stop_areas/show.html.slim @@ -1,14 +1,5 @@  - breadcrumb :stop_area, @stop_area_referential, @stop_area  - page_header_content_for @stop_area -- content_for :page_header_content do -  .row -    .col-lg-12.text-right.mb-sm -      - @stop_area.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content  / PageContent  .page_content diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim index b684b0bcb..f58fbb5ea 100644 --- a/app/views/time_tables/index.html.slim +++ b/app/views/time_tables/index.html.slim @@ -1,7 +1,4 @@  - breadcrumb :time_tables, @referential -- content_for :page_header_actions do -  - if (policy(Chouette::TimeTable).create? && @referential.organisation == current_organisation) -    = link_to(t('actions.add'), new_referential_time_table_path(@referential), class: 'btn btn-default')  .page_content    .container-fluid @@ -54,7 +51,6 @@                  attribute: Proc.new { |tt| l(tt.updated_at, format: :short) } \                ) \              ], -            links: [:show, :edit],              cls: 'table has-search'            = new_pagination @time_tables, 'pull-right' diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim index df9789055..6d15323f3 100644 --- a/app/views/time_tables/show.html.slim +++ b/app/views/time_tables/show.html.slim @@ -4,21 +4,6 @@  - content_for :page_header_title, t('time_tables.show.title', name: @time_table.comment), flush: true -- content_for :page_header_actions do -  - if policy(@time_table).edit? -    = link_to(t('actions.edit'), edit_referential_time_table_path(@referential, @time_table), class: 'btn btn-default') - -- content_for :page_header_content do -  .row.mb-sm -    .col-lg-12.text-right -      - @time_table.action_links.each do |link| -        = link_to link.href, -            method: link.method, -            data: link.data, -            class: 'btn btn-primary' do -              = link.content - -  .page_content    .container-fluid      .row diff --git a/app/views/workbench_outputs/show.html.slim b/app/views/workbench_outputs/show.html.slim index dc0a54204..4f71fb7ef 100644 --- a/app/views/workbench_outputs/show.html.slim +++ b/app/views/workbench_outputs/show.html.slim @@ -20,7 +20,7 @@                ), \                TableBuilderHelper::Column.new( \                  key: :name, \ -                attribute: 'name', \ +                attribute: 'full_names', \                  link_to: lambda do |merge| \                    workbench_merge_path merge.workbench, merge \                  end \ diff --git a/app/views/workbenches/show.html.slim b/app/views/workbenches/show.html.slim index 8907f3f08..a162ca334 100644 --- a/app/views/workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim @@ -59,8 +59,8 @@                  ) \                ],                selectable: ->(ref){ @workbench.referentials.include?(ref) }, -              links: [:show, :edit], -              cls: 'table has-filter has-search' +              cls: 'table has-filter has-search', +              action: :index              = multiple_selection_toolbox([:delete], collection_name: 'referentials') diff --git a/config/locales/lines.en.yml b/config/locales/lines.en.yml index 4b2bdfdb8..8e0189bd8 100644 --- a/config/locales/lines.en.yml +++ b/config/locales/lines.en.yml @@ -17,6 +17,7 @@ en:        export_kml_all: "Export KML lines"        export_hub: "Export HUB line"        export_hub_all: "Export HUB lines" +      show: 'Show'        show_network: 'Show network'        show_company: 'Show company'      new: diff --git a/config/locales/lines.fr.yml b/config/locales/lines.fr.yml index 6317e2930..2159f06ab 100644 --- a/config/locales/lines.fr.yml +++ b/config/locales/lines.fr.yml @@ -17,6 +17,7 @@ fr:        export_kml_all: "Export KML des lignes"        export_hub: "Export HUB de la ligne"        export_hub_all: "Export HUB des lignes" +      show: 'Consulter'        show_network: 'Voir le réseau'        show_company: 'Voir le transporteur principal'      new: diff --git a/config/locales/purchase_windows.en.yml b/config/locales/purchase_windows.en.yml index c0f402bf4..9ce05a1b9 100644 --- a/config/locales/purchase_windows.en.yml +++ b/config/locales/purchase_windows.en.yml @@ -24,6 +24,7 @@ en:        12: December      actions:        new: Add a new purchase window +      show: "Show"        edit: Edit this purchase window        destroy: Remove this purchase window        destroy_confirm: Are you sure you want destroy this purchase window? diff --git a/config/locales/purchase_windows.fr.yml b/config/locales/purchase_windows.fr.yml index 589546c32..3d5582ead 100644 --- a/config/locales/purchase_windows.fr.yml +++ b/config/locales/purchase_windows.fr.yml @@ -23,7 +23,8 @@ fr:        11: Novembre        12: Décembre      actions: -      new: Créer +      new: "Créer un calendrier commercial" +      show: "Consulter"        edit: Editer        destroy: Supprimer        destroy_confirm: Etes vous sûr de supprimer cet calendrier commercial ? diff --git a/lib/af83/decorator.rb b/lib/af83/decorator.rb new file mode 100644 index 000000000..f990555fe --- /dev/null +++ b/lib/af83/decorator.rb @@ -0,0 +1,122 @@ +class AF83::Decorator < ModelDecorator +  include AF83::Decorator::EnhancedDecorator +  extend AF83::Decorator::EnhancedDecorator::ClassMethods + +  def self.decorates klass +    instance_decorator.decorates klass +  end + +  def self.instance_decorator +    @instance_decorator ||= begin +      klass = Class.new(AF83::Decorator::InstanceDecorator) +      klass.delegate_all +      klass +    end +  end + +  def self.with_instance_decorator +    @_with_instance_decorator = true +    yield instance_decorator +    @_with_instance_decorator = false +  end + +  def self.decorate object, options = {} +    if object.is_a?(ActiveRecord::Base) +      return instance_decorator.decorate object, options +    else +      self.new object, options.update(with: instance_decorator) +    end +  end + +  def self.define_instance_method method_name, &block +    instance_decorator.send(:define_method, method_name, &block) +  end + +  # Defines a class method on the decorated object's class. These +  # can be called like `object.class.my_method`. +  def self.define_instance_class_method method_name, &block +    instance_decorator.send(:define_singleton_method, method_name, &block) +  end + +  class ActionLinks +    attr_reader :options + +    delegate :each, :map, :size, :first, :last, :any?, :select, to: :resolve + +    def initialize opts +      @options = opts.deep_dup +    end + +    def for_group group +      returning_a_copy do +        @options[:groups] = [group] if group.present? +      end +    end + +    def for_groups groups +      returning_a_copy do +        @options[:groups] = groups if groups.present? +      end +    end + +    def primary +      for_group :primary +    end + +    def secondary +      for_group :secondary +    end + +    def resolve +      out = @options[:links].map{|l| l.bind_to_context(@options[:context], @options[:action])}.select{|l| l.enabled?} +      if @options[:groups].present? +        out = out.select do |l| +          @options[:groups].any? do |g| +            l.in_group_for_action?(g) +          end +        end +      end +      out +    end +    alias_method :to_ary, :resolve + +    def grouped_by *groups +      add_footer = groups.include?(:footer) +      groups -= [:footer] +      out = HashWithIndifferentAccess[*groups.map{|g| [g, []]}.flatten(1)] +      out[:other] = [] +      if add_footer +        out[:footer] = [] +        groups << :footer +      end + +      each do |l| +        found = false +        groups.each do |g| +          if l.in_group_for_action?(g) +            out[g] << l +            found = true +            next +          end +        end +        out[:other] << l unless found +      end +      out +    end + +    private +    def returning_a_copy &block +      out = ActionLinks.new options +      out.instance_eval &block +      out +    end +  end + +  class IncompleteLinkDefinition < RuntimeError +  end + +  class InstanceDecorator < Draper::Decorator +    include AF83::Decorator::EnhancedDecorator +    extend AF83::Decorator::EnhancedDecorator::ClassMethods +  end +end diff --git a/lib/af83/decorator/enhanced_decorator.rb b/lib/af83/decorator/enhanced_decorator.rb new file mode 100644 index 000000000..904d1b2da --- /dev/null +++ b/lib/af83/decorator/enhanced_decorator.rb @@ -0,0 +1,145 @@ +module AF83::Decorator::EnhancedDecorator +  module ClassMethods +    def action_link args={} +      raise "You are using `action_link` inside a with_instance_decorator block, but not on the instance decorator itself.\n Use `instance_decorator.action_link` or move outside of the block, as this may lead to an unforeseen behaviour." if @_with_instance_decorator +      args[:if] = @_condition if args[:if].nil? + +      options, link_options = parse_options args + +      link = AF83::Decorator::Link.new(link_options) +      instance_exec(link, &options[:before_block]) if options[:before_block] +      yield link if block_given? +      raise AF83::Decorator::IncompleteLinkDefinition.new(link.errors) unless link.complete? + +      weight = options[:weight] || 1 +      @_action_links ||= [] +      @_action_links[weight] ||= [] +      @_action_links[weight] << link +    end + +    ### Here we define some shortcuts that match dthe default behaviours +    def create_action_link args={}, &block +      opts = { +        on: :index, +        primary: :index, +        policy: :create, +        before_block: -> (l){ +          l.content { h.t('actions.add') } +          l.href    { [:new, object.klass.name.underscore.singularize] } +        } +      } +      action_link opts.update(args), &block +    end + +    def show_action_link args={}, &block +      opts = { +        on: :index, +        primary: :index, +        before_block: -> (l){ +          l.content { h.t('actions.show') } +          l.href { [object] } +        } +      } +      action_link opts.update(args), &block +    end + +    def edit_action_link args={}, &block +      opts = { +        primary: %i(show index), +        policy: :edit, +        before_block: -> (l){ +          l.content { h.t('actions.edit') } +          l.href { [:edit, object] } +        } +      } +      action_link opts.update(args), &block +    end + +    def destroy_action_link args={}, &block +      opts = { +        policy: :destroy, +        footer: true, +        secondary: :show, +        before_block: -> (l){ +          l.content { h.destroy_link_content } +          l.href { [object] } +          l.method :delete +          l.data {{ confirm: h.t('actions.destroy_confirm') }} +        } +      } +      action_link opts.update(args), &block +    end + +    def t key +      eval  "-> (l){ h.t('#{key}') }" +    end + +    def with_condition condition, &block +      @_condition = condition +      instance_eval &block +      @_condition = nil +    end + +    def action_links action +      (@_action_links || []).flatten.compact.select{|l| l.for_action?(action)} +    end + +    def parse_options args +      options = {} +      %i(weight primary secondary footer on action actions policy feature if groups group before_block).each do |k| +        options[k] = args.delete(k) if args.has_key?(k) +      end +      link_options = args.dup + +      actions = options.delete :actions +      actions ||= options.delete :on +      actions ||= [options.delete(:action)] +      actions = [actions] unless actions.is_a?(Array) +      link_options[:_actions] = actions.compact + +      link_options[:_groups] = options.delete(:groups) +      link_options[:_groups] ||= {} +      if single_group = options.delete(:group) +        if(single_group.is_a?(Symbol) || single_group.is_a?(String)) +          link_options[:_groups][single_group] = true +        else +          link_options[:_groups].update single_group +        end +      end +      link_options[:_groups][:primary] ||= options.delete :primary +      link_options[:_groups][:secondary] ||= options.delete :secondary +      link_options[:_groups][:footer] ||= options.delete :footer + +      link_options[:_if] = options.delete(:if) +      link_options[:_policy] = options.delete(:policy) +      link_options[:_feature] = options.delete(:feature) +      [options, link_options] +    end +  end + +  def action_links action=:index, opts={} +    @action = action&.to_sym +    links = AF83::Decorator::ActionLinks.new links: self.class.action_links(action), context: self, action: action +    group = opts[:group] +    links = links.for_group opts[:group] +    links +  end + +  def primary_links action=:index +    action_links(action, group: :primary) +  end + +  def secondary_links action=:index +    action_links(action, group: :secondary) +  end + +  def check_policy policy +    _object = policy.to_s == "create" ? object.klass : object +    method = "#{policy}?" +    h.policy(_object).send(method) +  end + +  def check_feature feature +    h.has_feature? feature +  end +end diff --git a/lib/af83/decorator/link.rb b/lib/af83/decorator/link.rb new file mode 100644 index 000000000..7d2896e6a --- /dev/null +++ b/lib/af83/decorator/link.rb @@ -0,0 +1,153 @@ +class AF83::Decorator::Link +  REQUIRED_ATTRIBUTES = %i(href content) + +  attr_reader :context +  attr_reader :action + +  def initialize options={} +    @options = {} +    options.each do |k, v| +      send "#{k}", v +    end +  end + +  def bind_to_context context, action +    @context = context +    @action = action +    self +  end + +  def method *args +    link_method *args +  end + +  def class *args +    link_class args +  end + +  def method_missing name, *args, &block +    if block_given? +      @options[name] = block +    elsif args.size == 0 +      out = @options[name] +      out = context.instance_exec(self, &out)  if out.is_a?(Proc) +      out +    else +      # we can use l.foo("bar") or l.foo = "bar" +      if name.to_s =~ /\=$/ +        _name = name.to_s.gsub(/=$/, '') +        return send(_name, *args, &block) +      end +      @options[name] = args.first +    end +  end + +  def options +    @options.symbolize_keys +  end + +  def complete? +    @missing_attributes = REQUIRED_ATTRIBUTES.select{|a| !@options[a].present?} +    @missing_attributes.empty? +  end + +  def enabled_actions +    @options[:_actions].map(&:to_s) || [] +  end + +  def for_action? action=nil +    action ||= @action +    enabled_actions.empty? || enabled_actions.include?(action.to_s) +  end + +  def actions_for_group group +    val = @options[:_groups][group] +    val.is_a?(Array) ? val.map(&:to_s) : val +  end + +  def in_group_for_action? group +    vals = actions_for_group(group) +    if vals.is_a?(Array) +      return vals.include?(@action.to_s) +    elsif vals.is_a?(String) || vals.is_a?(Symbol) +      vals.to_s == @action.to_s +    else +      !!vals +    end +  end + +  def primary? +    in_group_for_action? :primary +  end + +  def secondary? +    in_group_for_action? :secondary +  end + +  def enabled? +    enabled = false +    if @options[:_if].nil? +      enabled = true +    elsif @options[:_if].is_a?(Proc) +      enabled = context.instance_exec(&@options[:_if]) +    else +      enabled = !!@options[:_if] +    end + +    enabled = enabled && check_policy(@options[:_policy]) if @options[:_policy].present? +    enabled = enabled && check_feature(@options[:_feature]) if @options[:_feature].present? +    enabled +  end + +  def check_policy(policy) +    @context.check_policy policy +  end + +  def check_feature(feature) +    @context.check_feature feature +  end + +  def errors +    "Missing attributes: #{@missing_attributes.to_sentence}" +  end + +  def add_class val +    @options[:link_class] ||= [] +    @options[:link_class] << val +    @options[:link_class].flatten! +  end + +  def extra_class +    (options[:link_class] || []).join(' ') +  end + +  def html_options +    out = {} +    options.each do |k, v| +      out[k] = self.send(k) unless k == :content || k == :href || k.to_s =~ /^_/ +    end +    out[:method] = link_method +    out[:class] = extra_class +    out.delete(:link_class) +    out[:class] += " disabled" if disabled +    out[:disabled] = !!disabled +    out +  end + +  def to_html +    if block_given? +      link = AF83::Decorator::Link.new(@options).bind_to_context(context, @action) +      yield link +      return link.to_html +    end +    if type&.to_sym == :button +      HTMLElement.new( +        :button, +        content, +        html_options +      ).to_html +    else +      context.h.link_to content, href, html_options +    end +  end +end diff --git a/spec/decorators/api_key_decorator_spec.rb b/spec/decorators/api_key_decorator_spec.rb deleted file mode 100644 index 9451a3974..000000000 --- a/spec/decorators/api_key_decorator_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'spec_helper' - -describe ApiKeyDecorator do -end diff --git a/spec/decorators/referential_decorator_spec.rb b/spec/decorators/referential_decorator_spec.rb index 9e34a0109..efc438132 100644 --- a/spec/decorators/referential_decorator_spec.rb +++ b/spec/decorators/referential_decorator_spec.rb @@ -21,8 +21,8 @@ RSpec.describe ReferentialDecorator, type: [:helper, :decorator] do      context 'unarchived referential' do        context 'no rights' do -        it 'has only a Calendar action' do -          expect_action_link_hrefs.to eq([referential_time_tables_path(object)]) +        it 'has only show and Calendar actions' do +          expect_action_link_hrefs.to eq([[object], referential_time_tables_path(object)])          end        end @@ -31,8 +31,9 @@ RSpec.describe ReferentialDecorator, type: [:helper, :decorator] do          let( :user ){ build_stubbed :allmighty_user }          it 'has only default actions' do -          expect_action_link_elements.to be_empty +          expect_action_link_elements.to eq ["Consulter", "Calendriers", "Dupliquer"]            expect_action_link_hrefs.to eq([ +            [object],              referential_time_tables_path(object),              new_referential_path(from: object)            ]) @@ -41,16 +42,36 @@ RSpec.describe ReferentialDecorator, type: [:helper, :decorator] do        context 'all rights and same organisation' do          let( :user ){ build_stubbed :allmighty_user, organisation: referential.organisation } +        let( :action){ :index } +        context "on index" do +          it 'has corresponding actions' do +            expect_action_link_elements(action).to eq ["Consulter", "Editer", "Calendriers", "Dupliquer", "Valider", "Conserver","<span class=\"fa fa-trash mr-xs\"></span>Supprimer"] +            expect_action_link_hrefs(action).to eq([ +              [object], +              [:edit, object], +              referential_time_tables_path(object), +              new_referential_path(from: object), +              referential_select_compliance_control_set_path(object), +              archive_referential_path(object), +              referential_path(object) +            ]) +          end +        end -        it 'has all actions' do -          expect_action_link_elements.to eq(%w{Purger}) -          expect_action_link_hrefs.to eq([ -            referential_time_tables_path(object), -            new_referential_path(from: object), -            referential_select_compliance_control_set_path(object), -            archive_referential_path(object), -            referential_path(object) -          ]) +        context "on show" do +          let( :action){ :show } +          it 'has corresponding actions' do +            expect_action_link_elements(action).to eq ["Editer", "Calendriers", "Dupliquer", "Valider", "Conserver", "Purger", "<span class=\"fa fa-trash mr-xs\"></span>Supprimer"] +            expect_action_link_hrefs(action).to eq([ +              [:edit, object], +              referential_time_tables_path(object), +              new_referential_path(from: object), +              referential_select_compliance_control_set_path(object), +              archive_referential_path(object), +              "#", +              referential_path(object) +            ]) +          end          end        end      end @@ -58,16 +79,17 @@ RSpec.describe ReferentialDecorator, type: [:helper, :decorator] do      context 'archived referential' do        before { referential.archived_at = 42.seconds.ago }        context 'no rights' do -        it 'has only a Calendar action' do -          expect_action_link_hrefs.to eq([referential_time_tables_path(object)]) +        it 'has only ahow and calendar actions' do +          expect_action_link_hrefs.to eq([[object], referential_time_tables_path(object)])          end        end        context 'all rights and different organisation' do          let( :user ){ build_stubbed :allmighty_user }          it 'has only default actions' do -          expect_action_link_elements.to be_empty +          expect_action_link_elements.to eq ["Consulter", "Calendriers", "Dupliquer"]            expect_action_link_hrefs.to eq([ +            [object],              referential_time_tables_path(object),              new_referential_path(from: object)            ]) diff --git a/spec/helpers/table_builder_helper_spec.rb b/spec/helpers/table_builder_helper_spec.rb index 8b383d88d..5bddbb16f 100644 --- a/spec/helpers/table_builder_helper_spec.rb +++ b/spec/helpers/table_builder_helper_spec.rb @@ -6,6 +6,13 @@ module TableBuilderHelper  end  describe TableBuilderHelper, type: :helper do +  let(:features){ [] } +  before do +    allow_any_instance_of(AF83::Decorator::Link).to receive(:check_feature){|f| +      features.include?(f) +    } +  end +    describe "#table_builder_2" do      it "builds a table" do        referential = build_stubbed(:workbench_referential) @@ -36,9 +43,8 @@ describe TableBuilderHelper, type: :helper do          id: referentials[0].workbench.id        }) -      referentials = ModelDecorator.decorate( -        referentials, -        with: ReferentialDecorator +      referentials = ReferentialDecorator.decorate( +        referentials        )        expected = <<-HTML @@ -77,15 +83,21 @@ describe TableBuilderHelper, type: :helper do              <td class="actions">                  <div class="btn-group">                      <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div> -                    <ul class="dropdown-menu"> -                        <li><a href="/referentials/#{referential.id}">Consulter</a></li> -                        <li><a href="/referentials/#{referential.id}/edit">Editer</a></li> -                        <li><a href="/referentials/#{referential.id}/time_tables">Calendriers</a></li> -                        <li><a href="/referentials/new?from=#{referential.id}">Dupliquer</a></li> -                        <li><a href="/referentials/#{referential.id}/select_compliance_control_set">Valider</a></li> -                        <li><a rel="nofollow" data-method="put" href="/referentials/#{referential.id}/archive">Conserver</a></li> -                        <li class="delete-action"><a data-confirm="Etes vous sûr de vouloir supprimer ce jeu de données ?" rel="nofollow" data-method="delete" href="/referentials/#{referential.id}"><span class="fa fa-trash mr-xs"></span>Supprimer</a></li> -                    </ul> +                    <div class="dropdown-menu"> +                        <ul class="primary"> +                            <li class=""><a href="/referentials/#{referential.id}">Consulter</a></li> +                            <li class=""><a href="/referentials/#{referential.id}/edit">Editer</a></li> +                        </ul> +                        <ul class="other"> +                            <li class=""><a href="/referentials/#{referential.id}/time_tables">Calendriers</a></li> +                            <li class=""><a href="/referentials/new?from=#{referential.id}">Dupliquer</a></li> +                            <li class=""><a href="/referentials/#{referential.id}/select_compliance_control_set">Valider</a></li> +                            <li class=""><a rel="nofollow" data-method="put" href="/referentials/#{referential.id}/archive">Conserver</a></li> +                        </ul> +                        <ul class="footer"> +                            <li class=" delete-action"><a data-confirm="Etes vous sûr de vouloir supprimer ce jeu de données ?" rel="nofollow" data-method="delete" href="/referentials/#{referential.id}"><span class="fa fa-trash mr-xs"></span>Supprimer</a></li> +                        </ul> +                    </div>                  </div>              </td>          </tr> @@ -149,7 +161,7 @@ describe TableBuilderHelper, type: :helper do            )          ],          selectable: true, -        links: [:show, :edit], +        action: :index,          cls: 'table has-filter has-search'        ) @@ -195,9 +207,9 @@ describe TableBuilderHelper, type: :helper do          referential_id: referential.id        }) -      companies = ModelDecorator.decorate( +      companies = CompanyDecorator.decorate(          companies, -        with: CompanyDecorator +        context: { referential: referential }        )        stub_policy_scope(company) @@ -223,9 +235,11 @@ describe TableBuilderHelper, type: :helper do              <td class="actions">                  <div class="btn-group">                      <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div> -                    <ul class="dropdown-menu"> -                        <li><a href="/referentials/#{referential.id}/companies/#{company.id}">Consulter</a></li> -                    </ul> +                    <div class="dropdown-menu"> +                        <ul class="primary"> +                            <li class=""><a href="/referentials/#{referential.id}/companies/#{company.id}">Consulter</a></li> +                        </ul> +                    </div>                  </div>              </td>          </tr> @@ -307,9 +321,8 @@ describe TableBuilderHelper, type: :helper do          referential_id: referential.id        }) -      companies = ModelDecorator.decorate( +      companies = CompanyDecorator.decorate(          companies, -        with: CompanyDecorator,          context: { referential: line_referential }        )        stub_policy_scope(company) @@ -336,9 +349,11 @@ describe TableBuilderHelper, type: :helper do              <td class="actions">                  <div class="btn-group">                      <div class="btn dropdown-toggle" data-toggle="dropdown"><span class="fa fa-cog"></span></div> -                    <ul class="dropdown-menu"> -                        <li><a href="/referentials/#{referential.id}/companies/#{company.id}">Consulter</a></li> -                    </ul> +                    <div class="dropdown-menu"> +                        <ul class="primary"> +                            <li class=""><a href="/line_referentials/#{line_referential.id}/companies/#{company.id}">Consulter</a></li> +                        </ul> +                    </div>                  </div>              </td>          </tr> @@ -374,7 +389,6 @@ describe TableBuilderHelper, type: :helper do            ),          ],          sortable: false, -        links: [:show, :edit, :delete],          cls: 'table has-search'        ) @@ -425,7 +439,7 @@ describe TableBuilderHelper, type: :helper do          let(:selectable){ false }          it "sets all rows as non selectable" do            items.each do |i| -            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name) +            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name, :index)              klass = "#{TableBuilderHelper.item_row_class_name([referential])}-#{i.id}"              selector = "tr.#{klass} [type=checkbox]"              expect(tr).to_not have_selector selector @@ -437,7 +451,7 @@ describe TableBuilderHelper, type: :helper do          let(:selectable){ true }          it "adds a checkbox in all rows" do            items.each do |i| -            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name) +            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name, :index)              klass = "#{TableBuilderHelper.item_row_class_name([referential])}-#{i.id}"              selector = "tr.#{klass} [type=checkbox]"              expect(tr).to have_selector selector @@ -449,14 +463,14 @@ describe TableBuilderHelper, type: :helper do          let(:selectable){ ->(i){ i.id != item.id } }          it "adds a checkbox in all rows" do            items.each do |i| -            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name) +            tr = helper.send(:tr, i, columns, selectable, links, overhead, model_name, :index)              klass = "#{TableBuilderHelper.item_row_class_name([referential])}-#{i.id}"              selector = "tr.#{klass} [type=checkbox]"              expect(tr).to have_selector selector            end          end          it "disables this rows checkbox" do -          tr = helper.send(:tr, item, columns, selectable, links, overhead, model_name) +          tr = helper.send(:tr, item, columns, selectable, links, overhead, model_name, :index)            klass = "#{TableBuilderHelper.item_row_class_name([referential])}-#{item.id}"            selector = "tr.#{klass} [type=checkbox][disabled]"            expect(tr).to have_selector selector diff --git a/spec/lib/af83/decorator/decorator_link_spec.rb b/spec/lib/af83/decorator/decorator_link_spec.rb new file mode 100644 index 000000000..0b2939421 --- /dev/null +++ b/spec/lib/af83/decorator/decorator_link_spec.rb @@ -0,0 +1,79 @@ +RSpec.describe AF83::Decorator::Link, type: :decorator do +  describe "#complete?" do +    context "on a imcomplete link" do +      it "should be false" do +        expect(AF83::Decorator::Link.new.complete?).to be_falsy +        expect(AF83::Decorator::Link.new(content: "foo").complete?).to be_falsy +        expect(AF83::Decorator::Link.new(href: "foo").complete?).to be_falsy +      end +    end + +    context "on a complete link" do +      it "should be true" do +        expect(AF83::Decorator::Link.new(href: "foo", content: "foo").complete?).to be_truthy +      end +    end +  end + +  describe "#class" do +    let(:link){ +      AF83::Decorator::Link.new(href: "foo", content: "foo", class: "initial_class") +    } + +    it "should override exisiting class" do +      expect(link.html_options[:class]).to eq "initial_class" +      link.class "new_class" +      expect(link.html_options[:class]).to eq "new_class" +      link.class = "another_class" +      expect(link.html_options[:class]).to eq "another_class" +      link.class = %w(foo bar) +      expect(link.html_options[:class]).to eq "foo bar" +    end +  end + +  describe "#add_class" do +    let(:link){ +      AF83::Decorator::Link.new(href: "foo", content: "foo", class: "initial_class") +    } + +    it "should add to exisiting class" do +      expect(link.html_options[:class]).to eq "initial_class" +      link.add_class "new_class" +      expect(link.html_options[:class]).to eq "initial_class new_class" +      link.add_class "another_class" +      expect(link.html_options[:class]).to eq "initial_class new_class another_class" +      link.add_class %w(foo bar) +      expect(link.html_options[:class]).to eq "initial_class new_class another_class foo bar" +    end +  end + +  describe "#type" do + +    let(:link){ +      AF83::Decorator::Link.new(href: "foo", content: "foo") +    } + +    let(:context){ +      Class.new do +        def h +          Class.new do +            def link_to *args +              HTMLElement.new(:a, 'foo', {}).to_html +            end +          end.new +        end +      end.new +    } + +    it "should allow for buttons" do +      link.type = :button +      expect(link.to_html).to match /\<button.*\<\/button\>/ +    end + +    it "should fallback to <a>" do +      link.type = :spaghetti +      link.bind_to_context context, :show +      expect(link.to_html).to match /\<a.*\<\/a\>/ +    end +  end +end diff --git a/spec/lib/af83/decorator/decorator_spec.rb b/spec/lib/af83/decorator/decorator_spec.rb new file mode 100644 index 000000000..61a849b9d --- /dev/null +++ b/spec/lib/af83/decorator/decorator_spec.rb @@ -0,0 +1,824 @@ +RSpec.describe AF83::Decorator, type: :decorator do +  describe(:parse_options) do +    let(:options){ +      {primary: true, secondary: %i(index show), policy: :blublu, weight: 12} +    } +    let(:link_options){ +      {foo: :foo, bar: :bar} +    } +    let(:args){ options.dup.update(link_options.dup) } +    it "should separate options from link_options" do +      _options, _link_options = AF83::Decorator.instance_decorator.send :parse_options, args +      expect(_options).to eq({weight: 12}) +      link_options.each do |k, v| +        expect(_link_options[k]).to eq v +      end +      expect(_link_options[:_groups][:primary]).to eq true +      expect(_link_options[:_groups][:secondary]).to eq %i(index show) +      expect(_link_options[:_policy]).to eq :blublu +    end +  end + +  link_should_match_options = ->(link, options){ +    options.each do |k, v| +      expect(link.send(k)).to eq v +    end +  } + +  context "as an collection decorator" do +    let(:link_options) do +      { +        href: "/foo/bar", +        content: "Blublu" +      } +    end + +    let(:decorator) do +      klass = Class.new(AF83::Decorator) +      klass.action_link link_options +      klass +    end + +    let(:decorated) do +      3.times { create :line } +      decorator.decorate(Chouette::Line.all) +    end + +    it "should return the links" do +      links = decorated.action_links +      instance_exec links.first, link_options, &link_should_match_options +    end +  end + +  context "as an instance decorator" do +    describe("with the actual decorator") do +      before(:each) do +        Draper::HelperProxy.any_instance.stub(:policy){ +          klass = Class.new do +            def method_missing *args +              true +            end +          end.new +        } +      end + +      let(:decorated) do +        line = create :line +        line.decorate(context: {line_referential: line.line_referential}) +      end + +      it "should return the links" do +        expect{ decorated.action_links }.to_not raise_error +      end +    end + +    describe(:action_links) do +      let(:decorated) do +        obj = create :line +        decorator.decorate(obj) +      end + +      context "without links" do +        let(:decorator) do +          Class.new(AF83::Decorator) +        end + +        it "should return no link" do +          links = decorated.action_links +          expect(links.size).to eq 0 +        end +      end + +      context "with a single link" do +        let(:link_options) do +          { +            href: "/foo/bar", +            content: "Blublu" +          } +        end + +        context "incompletetly defined" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link href: "bar" +            end +            klass +          end + +          it "should raise an error" do +            expect{decorator}.to raise_error(AF83::Decorator::IncompleteLinkDefinition) +          end +        end + +        context "defined inline" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link link_options +            end +            klass +          end + +          it "should return the defined link" do +            links = decorated.action_links +            expect(links.size).to eq 1 +            instance_exec links.first, link_options, &link_should_match_options +          end +        end + +        context "defined in a block" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link do |l| +                l.href link_options[:href] +                l.content link_options[:content] +              end +            end +            klass +          end + +          it "should return the defined link" do +            links = decorated.action_links +            expect(links.size).to eq 1 +            instance_exec links.first, link_options, &link_should_match_options +          end +        end + +        context "with proc attributes" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link do |l| +                l.href { context[:href] } +                l.content "Blublu" +              end +            end +            klass +          end + +          let(:decorated) do +            obj = create :line +            decorator.decorate(obj, context: {href: link_options[:href]}) +          end + +          it "should return the defined link" do +            links = decorated.action_links +            expect(links.size).to eq 1 +            expect(links.first.href).to eq link_options[:href] +          end +        end + +        context "with a method attributes" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link do |l| +                l.href "/foo/bar" +                l.content "Blublu" +                l.method :put +              end +            end +            klass +          end + +          let(:decorated) do +            obj = create :line +            decorator.decorate(obj, context: {href: link_options[:href]}) +          end + +          it "should return the defined method" do +            links = decorated.action_links +            expect(links.size).to eq 1 +            expect(links.first.method).to eq :put +          end +        end +      end + +      context "with 2 links" do +        let(:link_options_1) do +          { +            href: "/foo/bar", +            content: "Blublu" +          } +        end + +        let(:link_options_2) do +          { +            href: "/foo/bar/baz", +            content: "Foo" +          } +        end + +        context "without weight" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link link_options_1 +              instance_decorator.action_link link_options_2 +            end +            klass +          end + +          it "should return links in the sequence they were defined" do +            links = decorated.action_links +            expect(links.size).to eq 2 +            instance_exec links.first, link_options_1, &link_should_match_options +            instance_exec links.last, link_options_2, &link_should_match_options +          end +        end + +        context "with weight" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link link_options_1.update(weight: 10) +              instance_decorator.action_link link_options_2 +            end +            klass +          end + +          it "should return links in the correct sequence" do +            links = decorated.action_links +            expect(links.size).to eq 2 +            instance_exec links.first, link_options_2, &link_should_match_options +            instance_exec links.last, link_options_1, &link_should_match_options +          end +        end + +        context "scoped by action" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link link_options_1.update(action: :index) +              instance_decorator.action_link link_options_2 +            end +            klass +          end + +          it "should only return links defined for the given action" do +            links = decorated.action_links(:show) +            expect(links.size).to eq 1 +            instance_exec links.first, link_options_2, &link_should_match_options +          end +        end + +        context "with a policy" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link href: "foo", content: "foo", policy: :edit +            end +            klass +          end + +          context "when the policy is not met" do +            before(:each) do +              Draper::HelperProxy.any_instance.stub(:policy){ +                klass = Class.new do +                  def edit? +                    false +                  end +                end.new +              } +            end + +            it "should not return the link" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 0 +            end +          end + +          context "when the policy is met" do +            before(:each) do +              Draper::HelperProxy.any_instance.stub(:policy){ +                klass = Class.new do +                  def edit? +                    true +                  end +                end.new +              } +            end + +            it "should not return the link" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 1 +            end +          end +        end + +        context "with a feature" do +          let(:decorator) do +            klass = Class.new(AF83::Decorator) +            klass.with_instance_decorator do |instance_decorator| +              instance_decorator.action_link href: "foo", content: "foo", feature: :foo +            end +            klass +          end + +          context "when the feature is not present" do +            before(:each) do +              Draper::HelperProxy.any_instance.stub(:has_feature?){false} +            end + +            it "should not return the link" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 0 +            end +          end + +          context "when the feature is present" do +            before(:each) do +              Draper::HelperProxy.any_instance.stub(:has_feature?){true} +            end + +            it "should not return the link" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 1 +            end +          end +        end + +        context "with a condition" do +          context "set with 'with_condition'" do +            context "as a value" do +              context "when the condition is true" do +                let(:decorator) do +                  klass = Class.new(AF83::Decorator) +                  klass.with_instance_decorator do |instance_decorator| +                    instance_decorator.with_condition true do +                      action_link href: "foo", content: "foo" +                    end +                  end +                  klass +                end + +                it "should return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 1 +                end +              end + +              context "when the condition is false" do +                let(:decorator) do +                  klass = Class.new(AF83::Decorator) +                  klass.with_instance_decorator do |instance_decorator| +                    instance_decorator.with_condition false do +                      action_link href: "foo", content: "foo" +                    end +                  end +                  klass +                end + +                it "should not return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 0 +                end +              end +            end + +            context "as a Proc" do +              let(:decorator) do +                klass = Class.new(AF83::Decorator) +                klass.with_instance_decorator do |instance_decorator| +                  instance_decorator.with_condition ->{context[:show_link]} do +                    action_link href: "foo", content: "foo" +                  end +                end +                klass +              end + +              context "when the condition is true" do +                let(:decorated) do +                  obj = create :line +                  decorator.decorate(obj, context: {show_link: true}) +                end + +                it "should return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 1 +                end +              end + +              context "when the condition is false" do +                let(:decorated) do +                  obj = create :line +                  decorator.decorate(obj, context: {show_link: false}) +                end + +                it "should not return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 0 +                end +              end +            end +          end + +          context "set inline" do +            context "as a value" do +              context "when the condition is true" do +                let(:decorator) do +                  klass = Class.new(AF83::Decorator) +                  klass.with_instance_decorator do |instance_decorator| +                    instance_decorator.action_link link_options_1.update(if: true) +                  end +                  klass +                end + +                it "should return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 1 +                end +              end + +              context "when the condition is false" do +                let(:decorator) do +                  klass = Class.new(AF83::Decorator) +                  klass.with_instance_decorator do |instance_decorator| +                    instance_decorator.action_link link_options_1.update(if: false) +                  end +                  klass +                end + +                it "should not return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 0 +                end +              end +            end + +            context "as a Proc" do +              let(:decorator) do +                klass = Class.new(AF83::Decorator) +                klass.with_instance_decorator do |instance_decorator| +                  instance_decorator.action_link link_options_1.update(if: ->{context[:show_link]}) +                end +                klass +              end + +              context "when the condition is true" do +                let(:decorated) do +                  obj = create :line +                  decorator.decorate(obj, context: {show_link: true}) +                end + +                it "should return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 1 +                end +              end + +              context "when the condition is false" do +                let(:decorated) do +                  obj = create :line +                  decorator.decorate(obj, context: {show_link: false}) +                end + +                it "should not return the link" do +                  links = decorated.action_links(:show) +                  expect(links.size).to eq 0 +                end +              end +            end +          end +        end + +        context "scoped by action" do +          context "with a single action" do +            let(:decorator) do +              klass = Class.new(AF83::Decorator) +              klass.with_instance_decorator do |instance_decorator| +                instance_decorator.action_link link_options_1.update(action: :index) +                instance_decorator.action_link link_options_2 +              end +              klass +            end + +            it "should only return links defined for the given action" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 1 +              instance_exec links.first, link_options_2, &link_should_match_options +            end +          end + +          context "with several actions" do +            let(:decorator) do +              klass = Class.new(AF83::Decorator) +              klass.with_instance_decorator do |instance_decorator| +                instance_decorator.action_link link_options_1.update(actions: %i(index edit)) +                instance_decorator.action_link link_options_2.update(actions: %i(show edit)) +              end +              klass +            end + +            it "should only return links defined for the given action" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 1 +              instance_exec links.first, link_options_2, &link_should_match_options +            end +          end + +          context "with the keyword 'on'" do +            let(:decorator) do +              klass = Class.new(AF83::Decorator) +              klass.with_instance_decorator do |instance_decorator| +                instance_decorator.action_link link_options_1.update(on: %i(index edit)) +                instance_decorator.action_link link_options_2.update(on: :show) +              end +              klass +            end + +            it "should only return links defined for the given action" do +              links = decorated.action_links(:show) +              expect(links.size).to eq 1 +              instance_exec links.first, link_options_2, &link_should_match_options +            end +          end +        end +      end +    end + +    describe '#primary' do +      let(:decorator) do +        Class.new(AF83::Decorator) +      end + +      let(:decorated) do +        obj = create :line +        decorator.decorate(obj) +      end + +      it "should return a new object everytime" do +        actions = decorated.action_links +        primary = actions.primary +        expect(actions.options[:groups]).to be_nil +        expect(primary.options[:groups]).to_not be_nil +      end +    end + +    describe(:primary_links) do +      let(:decorated) do +        obj = create :line +        decorator.decorate(obj) +      end + +      context "without links" do +        let(:decorator) do +          Class.new(AF83::Decorator) +        end + +        it "should return no link" do +          links = decorated.action_links +          expect(links.size).to eq 0 +        end +      end + +      context "with a single link" do +        let(:link_options) do +          { +            href: "/foo/bar/baz", +            content: "Blublu", +            primary: primary +          } +        end + +        let(:decorator) do +          klass = Class.new(AF83::Decorator) +          klass.with_instance_decorator do |instance_decorator| +            instance_decorator.action_link link_options +          end +          klass +        end + +        context "always primary" do +          let(:primary){ true } + +          it "should return the link" do +            links = decorated.primary_links(:show) +            expect(links.size).to eq 1 +          end +        end + +        context "primary on this action" do +          let(:primary){ :show } + +          it "should return the link" do +            links = decorated.primary_links(:show) +            expect(links.size).to eq 1 +          end +        end + +        context "primary on this action among others" do +          let(:primary){ %i(show edit) } + +          it "should return the link" do +            links = decorated.action_links(:show, group: :primary) +            expect(links.size).to eq 1 +          end +        end + +        context "primary on other actions" do +          let(:primary){  %i(index edit) } + +          it "should not return the link" do +            links = decorated.action_links(:show, group: :primary) +            expect(links.size).to eq 0 +          end +        end + +        context "primary on another action" do +          let(:primary){  :index } + +          it "should not return the link" do +            links = decorated.primary_links(:show) +            expect(links.size).to eq 0 +          end +        end + +        context "never primary" do +          let(:primary){ nil } + +          it "should not return the link" do +            links = decorated.primary_links(:show) +            expect(links.size).to eq 0 +          end +        end +      end +    end + +    describe("in a group") do +      let(:decorated) do +        obj = create :line +        decorator.decorate(obj) +      end + +      context "without links" do +        let(:decorator) do +          Class.new(AF83::Decorator) +        end + +        it "should return no link" do +          links = decorated.action_links +          expect(links.size).to eq 0 +        end +      end + + +      context "with a single link" do +        let(:link_options) do +          { +            href: "/foo/bar/baz", +            content: "Blublu", +            groups: {foo: group} +          } +        end + +        let(:decorator) do +          klass = Class.new(AF83::Decorator) +          klass.with_instance_decorator do |instance_decorator| +            instance_decorator.action_link link_options +          end +          klass +        end + +        context "always in" do +          let(:group){ true } + +          it "should return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 1 +          end + +          context "define with group" do +            let(:link_options) do +              { +                href: "/foo/bar/baz", +                content: "Blublu", +                group: :foo +              } +            end + +            let(:decorator) do +              klass = Class.new(AF83::Decorator) +              klass.with_instance_decorator do |instance_decorator| +                instance_decorator.action_link link_options +              end +              klass +            end + +            it "should return the link" do +              links = decorated.action_links(:show, group: :foo) +              expect(links.size).to eq 1 +            end + +            it "should not return the link" do +              links = decorated.action_links(:show, group: :bar) +              expect(links.size).to eq 0 +            end +          end +        end + +        context "primary on this action" do +          let(:group){ :show } + +          it "should return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 1 +          end +        end + +        context "in this action among others" do +          let(:group){ %i(show edit) } + +          it "should return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 1 +          end +        end + +        context "in other actions" do +          let(:group){  %i(index edit) } + +          it "should not return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 0 +          end +        end + +        context "in another action" do +          let(:group){  :index } + +          it "should not return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 0 +          end +        end + +        context "never" do +          let(:group){ nil } + +          it "should not return the link" do +            links = decorated.action_links(:show, group: :foo) +            expect(links.size).to eq 0 +          end +        end +      end + +      describe(:grouped_by) do +        let(:link_options_1) do +          { +            href: "/foo/bar", +            content: "Blublu", +            primary: true +          } +        end + +        let(:link_options_2) do +          { +            href: "/foo/bar/baz", +            content: "Foo", +            groups: {secondary: :show} +          } +        end + +        let(:link_options_3) do +          { +            href: "/foo/bar/baz/bat", +            content: "Foo", +            groups: {foo: :show} +          } +        end + +        let(:link_options_4) do +          { +            href: "/footer", +            content: "Foo", +            footer: true +          } +        end + +        let(:decorator) do +          klass = Class.new(AF83::Decorator) +          klass.with_instance_decorator do |instance_decorator| +            instance_decorator.action_link link_options_1 +            instance_decorator.action_link link_options_2 +            instance_decorator.action_link link_options_3 +            instance_decorator.action_link link_options_4 +          end +          klass +        end + +        it "should return links in their groups" do +          links = decorated.action_links(:show).grouped_by(:primary, :secondary, :blu, :footer) +          expect(links.size).to eq 5 +          instance_exec links[:primary].first, link_options_1, &link_should_match_options +          instance_exec links[:secondary].first, link_options_2, &link_should_match_options +          expect(links[:blu].size).to eq 0 +          instance_exec links[:other].first, link_options_3, &link_should_match_options +          instance_exec links[:footer].first, link_options_4, &link_should_match_options +        end +      end +    end +  end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2d13d3802..cde252236 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -100,7 +100,6 @@ RSpec.configure do |config|    # The different available types are documented in the features, such as in    # https://relishapp.com/rspec/rspec-rails/docs    config.infer_spec_type_from_file_location! -  end  Shoulda::Matchers.configure do |config| diff --git a/spec/support/decorator_helpers.rb b/spec/support/decorator_helpers.rb index 9d450deb1..b2c41e842 100644 --- a/spec/support/decorator_helpers.rb +++ b/spec/support/decorator_helpers.rb @@ -5,18 +5,31 @@ module Support          subject{ object.decorate }          let( :policy ){ ::Pundit.policy(user_context, object) }          let( :user_context ){ UserContext.new(user, referential: referential) } - +        let( :features ){ [] } +        let( :filtered_action_links){}          before do            allow_any_instance_of(Draper::HelperProxy).to receive(:policy).and_return policy +          allow_any_instance_of(AF83::Decorator::Link).to receive(:check_feature){|f| +            features.include?(f) +          }          end        end      end -    def expect_action_link_hrefs -      expect( subject.action_links.select(&Link.method(:===)).map(&:href) ) +    def expect_action_link_hrefs(action=:index) +      if subject.action_links.is_a? AF83::Decorator::ActionLinks +        expect( subject.action_links(action).map(&:href) ) +      else +        expect( subject.action_links.select(&Link.method(:===)).map(&:href) ) +      end      end -    def expect_action_link_elements -      expect( subject.action_links.select(&HTMLElement.method(:===)).map(&:content) ) + +    def expect_action_link_elements(action=:index) +      if subject.action_links.is_a? AF83::Decorator::ActionLinks +        expect( subject.action_links(action).map(&:content) ) +      else +        expect( subject.action_links.select(&HTMLElement.method(:===)).map(&:content) ) +      end      end    end  end diff --git a/spec/views/lines/index.html.slim_spec.rb b/spec/views/lines/index.html.slim_spec.rb index 498784912..fb436c545 100644 --- a/spec/views/lines/index.html.slim_spec.rb +++ b/spec/views/lines/index.html.slim_spec.rb @@ -19,7 +19,9 @@ describe "/lines/index", :type => :view do      deactivated_line      allow(view).to receive(:collection).and_return(lines)      allow(view).to receive(:current_referential).and_return(line_referential) +    allow(view).to receive(:params).and_return({action: :index})      controller.request.path_parameters[:line_referential_id] = line_referential.id +    controller.request.path_parameters[:action] = "index"      render    end diff --git a/spec/views/referentials/show.html.erb_spec.rb b/spec/views/referentials/show.html.erb_spec.rb index f1fa7188a..4a2afe2ca 100644 --- a/spec/views/referentials/show.html.erb_spec.rb +++ b/spec/views/referentials/show.html.erb_spec.rb @@ -1,4 +1,49 @@  require 'spec_helper'  describe "referentials/show", type: :view do + +  let!(:referential) do +    referential = create(:referential) +    assign :referential, referential.decorate(context: { +      current_organisation: referential.organisation +    }) +  end +  let(:permissions){ [] } +  let(:current_organisation) { organisation } +  let(:current_offer_workbench) { create :workbench, organisation: current_organisation} +  let(:readonly){ false } + +  before :each do +    assign :reflines, [] +    allow(view).to receive(:current_offer_workbench).and_return(current_offer_workbench) +    allow(view).to receive(:current_organisation).and_return(current_organisation) +    allow(view).to receive(:current_user).and_return(current_user) + +    allow(view).to receive(:resource).and_return(referential) +    allow(view).to receive(:has_feature?).and_return(true) +    allow(view).to receive(:user_signed_in?).and_return true +    controller.request.path_parameters[:id] = referential.id +    allow(view).to receive(:params).and_return({action: :show}) + +    allow(referential).to receive(:referential_read_only?){ readonly } +    render template: "referentials/show", layout: "layouts/application" +  end + +  it "should not present edit button" do +    expect(rendered).to_not have_selector("a[href=\"#{view.edit_referential_path(referential)}\"]") +  end + +  with_permission "referentials.update" do +    it "should present edit button" do +      expect(rendered).to have_selector("a[href=\"#{view.edit_referential_path(referential)}\"]") +    end + +    context "with a readonly referential" do +      let(:readonly){ true } +      it "should not present edit button" do +        expect(rendered).to_not have_selector("a[href=\"#{view.edit_referential_path(referential)}\"]") +      end +    end +  end +  end diff --git a/spec/views/stop_areas/index.html.slim_spec.rb b/spec/views/stop_areas/index.html.slim_spec.rb index 520cecc1a..6e66c5ab9 100644 --- a/spec/views/stop_areas/index.html.slim_spec.rb +++ b/spec/views/stop_areas/index.html.slim_spec.rb @@ -13,6 +13,7 @@ describe "/stop_areas/index", :type => :view do      allow(view).to receive(:link_with_search).and_return("#")      allow(view).to receive(:collection).and_return(stop_areas)      allow(view).to receive(:current_referential).and_return(stop_area_referential) +    allow(view).to receive(:params).and_return({action: :index})      controller.request.path_parameters[:stop_area_referential_id] = stop_area_referential.id      render    end | 
