diff options
59 files changed, 716 insertions, 360 deletions
| diff --git a/INSTALL.md b/INSTALL.md index 6e497b580..28ffdeb4d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,31 +1,40 @@  # Installation Guide -This guide is based on mac/OS with [Homebrew](https://brew.sh/) and [RVM](https://rvm.io/) -  ## Ruby -Get a correct `.ruby-version` (Can we remove it from `.gitignore`?) -and install that version. +Example with [rvm](https://rvm.io/) (other solutions : rbenv, packages..): -Example with [rvm](https://rvm.io/): +```sh +rvm install 2.3.1 +``` -        rvm install 2.3.1 +## Node and Yarn -Add the bundler gem +Yarn needs node. If you use Node Version Manager [NVM](https://github.com/creationix/nvm)  you can rely on the content of `.nvmrc`. Otherwise please make sure to use a compatible version, still best to use the same as indicated by `.nvrmc`. -        gem install bundler +* Install node -Go into your local repro and install the gems +```sh +nvm install 6.12.0 +``` -        bundle +* Install [yarn](https://yarnpkg.com/lang/en/docs/install/) -## Node and Yarn +```sh +// On macOS +brew install yarn -Yarn needs a node version ≥ 6, if you use Node Version Manager [NVM](https://github.com/creationix/nvm)  you can rely on the content of `.nvmrc`. +// On Debian/ubuntu +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list +sudo apt-get update && sudo apt-get install yarn +``` -Otherwise please make sure to use a compatible version, still best to use the same as indicated by `.nvrmc`. +* Install nodes packages -Then install yarn (`brew install yarn` does nicely on macOS). +```sh +yarn install +```  ### Installation Caveats @@ -33,14 +42,16 @@ Then install yarn (`brew install yarn` does nicely on macOS).  `libv8` might cause you troubles, depending on your local configuration. If you have `libv8` installed (probably because of `node.js`) you might need to tell bundler/Rubygems to use the system version. - -        bundle config build.libv8 --with-system-v8 -        bundle - +```sh +bundle config build.libv8 --with-system-v8 +bundle +```  or -        gem install libv8 -v '<version>' -- --with-system-v8 -        bundle +```sh +gem install libv8 -v '<version>' -- --with-system-v8 +bundle +```  You will get the correct value of `<version>` from bundler's error message. @@ -50,35 +61,18 @@ Even after `libv8` installation working, the gem `therubyracer` might not like t  In that case however we can let the gem make its own choice: -        gem uninstall libv8 -        gem install therubyracer -v '<version>' +```sh +gem uninstall libv8 +gem install therubyracer -v '<version>' +```  The version to be installed is indicated in the error message bundler gave us in the first place.  This will install an appropriate `libv8` version and we can continue with `bundle`. -## Rails - -### Dependencies - -As documented [here](https://github.com/dryade/georuby-ext/issues/2) we need some more libs before we can start the `rake` setup tasks. On mac/OS the easiest way is just to install `postgis` now with `homebrew` as this will -install all needed libraries. - -Also if on Linux you might discover a problem as late as when launching `rake`. -In case of a stacktrace similar to this one - -``` -$ bundle exec rake --trace -T -rake aborted! -LoadError: library names list must not be empty -``` - -you need to install `libproj4-dev` on your system. - - -### Postgres +## Postgres -#### Create user +### Create user        createuser -s -U $USER -P chouette                    ^    ^      ^ @@ -88,85 +82,110 @@ you need to install `libproj4-dev` on your system.  When promted for the password enter the highly secure string `chouette`. +## Rails -#### Create database +### Dependencies -      bundle exec rake db:create -      bundle exec rake db:migrate +As documented [here](https://github.com/dryade/georuby-ext/issues/2) we need some more libs before we can start the `rake` setup tasks. -      RAILS_ENV=test bundle exec rake db:create -      RAILS_ENV=test bundle exec rake db:migrate -#### Install node.js packages +On mac/OS : -      bundle exec rake npm:install +```sh +brew install postgis +``` -#### Check installation +On debian/ubuntu system : -* Run tests +```sh +sudo apt-get install libproj-dev postgis +``` -      bundle exec rake spec -      bundle exec rake teaspoon +### Install gems -* Start local server +Add the bundler gem -      bundle exec rails server +```sh +gem install bundler +``` -### Authentication +Go into your local repository and install the gems -See `config.chouette_authentication_settings`. +```sh +bundle install +``` -Use the database authentication or get an invitation to [STIF Portail](http://stif-portail-dev.af83.priv/). +#### Nokogiri on macOS -### Run seed +http://www.nokogiri.org/tutorials/installing_nokogiri.html tells us that `xz` can cause troubles, here is what to do +``` +brew unlink xz +gem install nokogiri # or bundle install +brew link xz +``` -#### Basic Database Content +### Database -      bundle exec rake db:seed +#### Create database +```sh +bundle exec rake db:create db:migrate +RAILS_ENV=test bundle exec rake db:create db:migrate +``` -Two users are created : stif-boiv@af83.com/secret and stif-boiv+transporteur@af83.com/secret +#### Load seed datas -#### Synchronize With STIF +```sh +bundle exec rake db:seed +``` -If you have access to STIF CodifLigne and Reflex : +#### Synchronise datas with lines and stop areas referentials  * Launch Sidekiq -      bundle exec sidekiq +```sh +bundle exec sidekiq +```  * Execute the Synchronization Tasks -      bundle exec rake codifligne:sync -      bundle exec rake reflex:sync +```sh +bundle exec rake codifligne:sync +bundle exec rake reflex:sync +```  **N.B.** These are asynchronious tasks, you can observe the launched jobs in your [Sidekiq Console](http://localhost:3000/sidekiq)  #### Data in various Apartments (Referentials) -To create `Referential` objects with some data (`Route`, `JourneyPattern`, `VehicleJourney`, etc) : +To create `Referential` objects with some data (`Route`, `JourneyPattern`, `VehicleJourney`, etc), you need to wait codifligne and reflex jobs finished. And then you can launch : -      bundle exec rake referential:create +```sh +bundle exec rake referential:create +``` -# Troubleshooting +### Check installation -## Postgres +#### Run tests -If Postgres complains about illegal type `hstore` in your tests that is probably because the shared extension is not installed, here is what to do: +```sh +bundle exec rake spec +bundle exec rake teaspoon +``` + +If Postgres complains about illegal type `hstore` or `unaccent` in your tests that is probably because the shared extension is not installed, here is what to do:        bundle exec rake db:test:purge  Thanks to `lib/tasks/extensions.rake`. -## macOS -### Nokogiri -http://www.nokogiri.org/tutorials/installing_nokogiri.html tells us that `xz` can cause troubles, here is what to do  +#### Start local server +```sh +bin/webpack-dev-server // Launch webpack server to compile assets on the fly +bundle exec rails server // Launch rails server  ``` -brew unlink xz -gem install nokogiri # or bundle install -brew link xz -``` +You need to have an account on [STIF Portail](http://stif-portail-dev.af83.priv/) to connect to the Rails application. @@ -1,20 +1,15 @@  # Chouette2 [](https://travis-ci.org/afimb/chouette2) [](https://gemnasium.com/afimb/chouette2) [](https://codeclimate.com/github/afimb/chouette2)  Chouette2 is an open source web project in Ruby/Rails to edit and view transport offer data. It is designed as an [SaaS](http://en.wikipedia.org/wiki/Software_as_a_service) platform and can : -* Exchange transport data : [Neptune](http://www.normes-donnees-tc.org/format-dechange/donnees-theoriques/neptune/), [GTFS](https://developers.google.com/transit/gtfs/reference?hl=fr), [NeTEx](http://www.normes-donnees-tc.org/format-dechange/donnees-theoriques/netex/), CSV +* Exchange transport data + * [Neptune](http://www.normes-donnees-tc.org/format-dechange/donnees-theoriques/neptune/), + * [GTFS](https://developers.google.com/transit/gtfs/reference?hl=fr), + * [NeTEx](http://www.normes-donnees-tc.org/format-dechange/donnees-theoriques/netex/)  * Edit transport data  * Be requested via a read-only [Restful API](https://en.wikipedia.org/wiki/Representational_state_transfer)  * [Import, Export and Validate transport data asynchronously](http://github.com/afimb/chouette)  * Use a [multi-tenancy database](http://en.wikipedia.org/wiki/Multitenancy) -Chouette2 uses Rest Web Service API from [chouette](http://github.com/afimb/chouette) (another git project) to import, export and validate various transport data. - -Feel free to test and access to the free SaaS web site at [http://appli.chouette.mobi](http://appli.chouette.mobi/chouette2/users/sign_in). Two types of access are granted : -* A demo organisation with an existing Public Transport dataset -  * login : demo@chouette.mobi -  * password : chouette -* Create your own organisation : follow the link "Sign up" ("S'inscrire") -  Release Notes  ------------- @@ -23,162 +18,28 @@ The release notes (in French) can be found in the [CHANGELOG](./CHANGELOG.md) fi  Requirements  ------------ -* [Chouette IEV 3.X Web Service](https://github.com/afimb/chouette) (requires Postgresql and Java) -* Ruby 1.9.3 or 2.1.6 -* Bundler 1.10.3 -* Proj 4.8.0 - -External Deps -------------- - -Install Chouette IEV 3.X Web Service  [see how to](https://github.com/afimb/chouette/readme.md) -Next steps assumes that : -* a Chouette IEV Web Service is running (on localhost, port 8080) -* a Postgres database exists (chouette2 on localhost, port 5432) with a postgres user (chouette, with password chouette) - -On Debian/Ubuntu/Kubuntu OS : assumes that the depot contains the correct version -```sh -sudo apt-get install libpq-dev -sudo apt-get install git -sudo apt-get install unzip -sudo apt-get install proj-bin -sudo apt-get install libproj-dev -sudo apt-get install make -sudo apt-get install libmagic-dev -``` - -If Linux distribution doesn't publish an RVM package, -install [RVM from sources](./doc/install/rvm.md) - -Install ruby 2.1.6 -```sh -rvm  install ruby-2.1.6 -rvm --default use 2.1.6 -``` - -Install bundler 1.10.3 -```sh -gem install bundler -v 1.10.3 -``` - -Installation ------------- - -On Debian, chouette can also be installed as a package : see [debian packages](http://packages.chouette.cityway.fr/debian/chouette) - -Install web application - -Get git source code : -```sh -cd -git clone -b V3_1 git://github.com/afimb/chouette2 -cd chouette2 -``` -Download gem librairies -```sh -bundle install -``` -Update database schema -```sh -RAILS_ENV=production bundle exec rake db:migrate -``` -Prepare static resources (assets) -```sh -RAILS_ENV=production bundle exec rake assets:clobber assets:precompile -``` - -Configuration -------------- - -Configure for Generating URLs in Action Mailer Views. -* Edit [production.rb](./config/environments/production.rb) and change ```config.action_mailer.default_url_options``` -* see [Action Mailer Configuration documentation](http://guides.rubyonrails.org/action_mailer_basics.html) - -Configure assets access (Must be true if you use Webrick, or false if you use Apache or Nginx). -* Edit [production.rb](./config/environments/production.rb) and change ```config.serve_static_files``` - -Configure SMTP settings. -* Edit [production.rb](./config/environments/production.rb) and change ```ActionMailer::Base.smtp_settings``` -* see [Action Mailer Configuration documentation](http://guides.rubyonrails.org/action_mailer_basics.html) - -Configure e-mail send address visible on the e-mail sent when a user registers, re-initialises its password, ... -* Edit [production.rb](./config/environments/production.rb) and change ```config.mailer_sender``` - -Configure Rails secret key. -* Edit [secrets.yml](./config/secrets.yml) and uncomment and set```secret_key_base``` -* see [Rails documentation](http://guides.rubyonrails.org/4_1_release_notes.html#config-secrets-yml) - -Configure address of the Chouette IEV Web Service. -* Edit [secrets.yml](./config/secrets.yml) and uncomment and set```api_endpoint``` - -Configure Google Analytics Key. -* Edit [secrets.yml](./config/secrets.yml) and change```google_analytic_tracker``` -* see [Google Analytics](https://www.google.fr/intl/fr/analytics/) +* [Import, Export and Validation Operations](https://github.com/af83/stif-boiv-iev) are in another project in Java -Configure IGN Géoportail Key. -* Edit [secrets.yml](./config/secrets.yml) and uncomment and set```geoportail_api_key``` -* see [API Géoportail documentation](http://api.ign.fr/accueil) - -Configure the way that Chouette2 sends e-mail. -* Edit [devise_async.rb](./config/initializer/devise_async.rb) and uncomment and set```Devise::Async.enabled``` ( true if you want to use asynchronously and false otherwise ) -* see [Devise Async specification](https://github.com/mhfs/devise-async) - -Configure OSRM Backend URL -* Edit [secrets.yml](./config/secrets.yml) and change```osrm_endpoint``` -* see [Project-OSRM](https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy) - -Run ---- - -Launch the task if you want to send mail asynchronously (See previous chapter to desactivate it) -```sh -RAILS_ENV=production bundle exec rake jobs:work -``` -This task may be added in system start-up configuration - -Launch rails server with [WEBrick](http://guides.rubyonrails.org/command_line.html#server-with-different-backends) ( default RoR web server, note: webrick runs on default port 3000) -```sh -RAILS_ENV=production bundle exec rails server -``` - -This task may be added in system start-up configuration. -Instead of using WEBrick, Rails application may be deployed on [Phusion Passenger](https://www.phusionpassenger.com/) with an [Apache](http://httpd.apache.org/) or [NGinx](http://nginx.com/) front-end, to make server faster and more robust. - -Apache like NGinx can serve static resources, -so change parameter ```serve_static_files``` to false in [production.rb](./config/environments/production.rb) - -Test ----- +Install +------- -```sh -bundle exec rake db:create -bundle exec rake db:migrate -bundle exec rake spec -``` +See [installation manual](./INSTALL.md)  More Information  ---------------- -Complete docs (in French) can be found on the [project website](http://www.chouette.mobi/developpeurs).  Some technical articles are available [on the wiki](../../wiki) too.  API Documentation  ----------------- -The description (in French) of the read-only restful API is described in : -* [User manual file](./doc/interfaces/Chouette_API_REST_v1.2.pdf) -* [XSD file](./doc/interfaces/api_rest_v1.xsd) +TODO  License  -------  This project is licensed under the CeCILL-B license, a copy of which can be found in the [LICENSE](./LICENSE.md) file. -Project-OSRM Licence -------------------------- - -Project-OSRM is licensed under the [ODbL](http://opendatacommons.org/licenses/odbl/) licence. -  Release Notes  ------------- @@ -188,8 +49,3 @@ Support  -------  Users looking for support should file an issue on the GitHub [issue tracking page](../../issues), or file a [pull request](../../pulls) if you have a fix available. - -Credits -------- - -Thanks to Ingolf for his [photo](https://www.flickr.com/photos/ingolfbln/7663851694) under CC BY-SA 2.0 license diff --git a/app/assets/stylesheets/components/_breadcrumb.sass b/app/assets/stylesheets/components/_breadcrumb.sass index 62f167eb4..1b30ca42b 100644 --- a/app/assets/stylesheets/components/_breadcrumb.sass +++ b/app/assets/stylesheets/components/_breadcrumb.sass @@ -1,3 +1,6 @@  .breadcrumbs +  white-space: nowrap +  text-overflow: ellipsis +  overflow: hidden    a -    color: white
\ No newline at end of file +    color: white diff --git a/app/assets/stylesheets/components/_buttons.sass b/app/assets/stylesheets/components/_buttons.sass index a59699383..a649a07ef 100644 --- a/app/assets/stylesheets/components/_buttons.sass +++ b/app/assets/stylesheets/components/_buttons.sass @@ -165,6 +165,13 @@ table, .table              .fa:first-child                margin-right: 0.5em +          & + li.delete-action +            > a, > button +              margin-top: 0 +              &:before +                display: none + +    &.table-2entries .t2e-item      > .th        position: relative diff --git a/app/assets/stylesheets/components/_main_nav.sass b/app/assets/stylesheets/components/_main_nav.sass index 9f7a8e244..f102c4617 100644 --- a/app/assets/stylesheets/components/_main_nav.sass +++ b/app/assets/stylesheets/components/_main_nav.sass @@ -243,6 +243,9 @@ $menuW: 300px            left: 0            top: 13px +        & > .menu-item +          max-width: 75% +                    .menu-item            padding: 0 10px @@ -324,8 +327,11 @@ $menuW: 300px        height: $menuH * 2        transition: 0.1s -    #menu_top > .menu-content > .menu-item-group -      display: none +    #menu_top > .menu-content +      & > .menu-item +        max-width: 90% +      & > .menu-item-group +        display: none      .sticky-content        height: $menuH @@ -345,16 +351,16 @@ $menuW: 300px              white-space: nowrap              max-height: 1.1em              margin: 0 -1.4em 0 0 -            padding: 0 1.4em 0 0 +            padding: 0 1.4em 5px 0              overflow: hidden              &:before                content: '[...]'                font-size: 0.65em                position: absolute -              z-index: 5 +              z-index: 6                right: 0 -              bottom: 4px +              bottom: 9px              &:after                content: '' diff --git a/app/controllers/concerns/activatable.rb b/app/controllers/concerns/activatable.rb new file mode 100644 index 000000000..1a34551a9 --- /dev/null +++ b/app/controllers/concerns/activatable.rb @@ -0,0 +1,11 @@ +module Activatable +  extend ActiveSupport::Concern + +  %w(activate deactivate).each do |action| +    define_method action do +      authorize resource, "#{action}?" +      resource.send "#{action}!" +      redirect_to request.referer || [current_referential, resource] +    end +  end +end diff --git a/app/controllers/lines_controller.rb b/app/controllers/lines_controller.rb index 571c73f4a..f446e1d37 100644 --- a/app/controllers/lines_controller.rb +++ b/app/controllers/lines_controller.rb @@ -1,6 +1,8 @@  class LinesController < ChouetteController    include ApplicationHelper +  include Activatable    include PolicyChecker +    defaults :resource_class => Chouette::Line    respond_to :html    respond_to :xml diff --git a/app/controllers/purchase_windows_controller.rb b/app/controllers/purchase_windows_controller.rb index a70535150..04b5736bb 100644 --- a/app/controllers/purchase_windows_controller.rb +++ b/app/controllers/purchase_windows_controller.rb @@ -6,11 +6,11 @@ class PurchaseWindowsController < ChouetteController    defaults :resource_class => Chouette::PurchaseWindow, collection_name: 'purchase_windows', instance_name: 'purchase_window'    belongs_to :referential +  requires_feature :purchase_windows +    def index      index! do -      scope = self.ransack_period_range(scope: @purchase_windows, error_message: t('compliance_check_sets.filters.error_period_filter'), query: :overlapping) -      @q = scope.ransack(params[:q]) -      @purchase_windows = decorate_purchase_windows(@q.result.paginate(page: params[:page], per_page: 30)) +      @purchase_windows = decorate_purchase_windows(@purchase_windows)      end    end @@ -45,14 +45,31 @@ class PurchaseWindowsController < ChouetteController        )    end +   def sort_column +    Chouette::PurchaseWindow.column_names.include?(params[:sort]) ? params[:sort] : 'name' +  end + +  def sort_direction +    %w[asc desc].include?(params[:direction]) ?  params[:direction] : 'asc' +  end + +  def collection +    return @purchase_windows if @purchase_windows +    @q = Chouette::PurchaseWindow.ransack(params[:q]) + +    purchase_windows = @q.result +    purchase_windows = purchase_windows.order(sort_column + ' ' + sort_direction) if sort_column && sort_direction +    @purchase_windows = purchase_windows.paginate(page: params[:page]) +  end +    def ransack_contains_date      date =[] -    if params[:q] && !params[:q]['date_ranges(1i)'].empty? -      ['date_ranges(1i)', 'date_ranges(2i)', 'date_ranges(3i)'].each do |key| +    if params[:q] && params[:q]['contains_date(1i)'].present? +      ['contains_date(1i)', 'contains_date(2i)', 'contains_date(3i)'].each do |key|          date << params[:q][key].to_i          params[:q].delete(key)        end -      params[:q]['date_ranges'] = Date.new(*date) rescue nil +      params[:q]['contains_date'] = @date = Date.new(*date) rescue nil      end    end  end diff --git a/app/controllers/referential_companies_controller.rb b/app/controllers/referential_companies_controller.rb index ca1ff67db..7e65a72cf 100644 --- a/app/controllers/referential_companies_controller.rb +++ b/app/controllers/referential_companies_controller.rb @@ -35,7 +35,8 @@ class ReferentialCompaniesController < ChouetteController    def collection      scope = referential.line_referential.companies      if params[:line_id] -      scope = referential.line_referential.lines.find(params[:line_id]).companies +      line_scope = referential.line_referential.lines.find(params[:line_id]).companies +      scope = line_scope if line_scope.exists?      end      @q = scope.search(params[:q]) diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index 498493f1e..b478d38fa 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -1,6 +1,7 @@  class StopAreasController < ChouetteController    include ApplicationHelper - +  include Activatable +      defaults :resource_class => Chouette::StopArea    belongs_to :stop_area_referential diff --git a/app/decorators/line_decorator.rb b/app/decorators/line_decorator.rb index ede670cbd..9c0cf7292 100644 --- a/app/decorators/line_decorator.rb +++ b/app/decorators/line_decorator.rb @@ -18,7 +18,8 @@ class LineDecorator < Draper::Decorator      links << Link.new(        content: h.t('lines.actions.show_company'), -      href: [context[:line_referential], object.company] +      href: [context[:line_referential], object.company], +      disabled: object.company.nil?      )      if h.policy(Chouette::Line).create? && @@ -41,6 +42,26 @@ class LineDecorator < Draper::Decorator        )      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" +      ) +    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" +      ) +    end +      if h.policy(object).destroy?        links << Link.new(          content: h.destroy_link_content('lines.actions.destroy'), diff --git a/app/decorators/purchase_window_decorator.rb b/app/decorators/purchase_window_decorator.rb index 13bc4d666..646fdea0d 100644 --- a/app/decorators/purchase_window_decorator.rb +++ b/app/decorators/purchase_window_decorator.rb @@ -18,7 +18,7 @@ class PurchaseWindowDecorator < Draper::Decorator          content: I18n.t('actions.destroy'),          href: h.referential_purchase_window_path(context[:referential].id, object),          method: :delete, -        data: { confirm: h.t('purchase_window.actions.destroy_confirm') } +        data: { confirm: h.t('purchase_windows.actions.destroy_confirm') }        )      end diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb index 0863b7f4b..d75ad1050 100644 --- a/app/decorators/referential_decorator.rb +++ b/app/decorators/referential_decorator.rb @@ -12,6 +12,13 @@ class ReferentialDecorator < Draper::Decorator        )      end +    if has_feature?(:purchase_windows) +      links << Link.new( +        content: h.t('purchase_windows.index.title'), +        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) diff --git a/app/decorators/referential_line_decorator.rb b/app/decorators/referential_line_decorator.rb index 654f68bf5..dceb3e2a9 100644 --- a/app/decorators/referential_line_decorator.rb +++ b/app/decorators/referential_line_decorator.rb @@ -24,15 +24,6 @@ class ReferentialLineDecorator < Draper::Decorator        )      ) -    if h.policy(object).destroy? -      links << Link.new( -        content: h.destroy_link_content('actions.destroy'), -        href: h.referential_line_path(context[:referential], object), -        method: :delete, -        data: { confirm: t('lines.actions.destroy_confirm') } -      ) -    end -      if !object.hub_restricted? ||          (object.hub_restricted? && object.routes.size < 2)        if h.policy(Chouette::Route).create? && diff --git a/app/decorators/stop_area_decorator.rb b/app/decorators/stop_area_decorator.rb index cf3612f79..32f6e1d2b 100644 --- a/app/decorators/stop_area_decorator.rb +++ b/app/decorators/stop_area_decorator.rb @@ -3,12 +3,12 @@ class StopAreaDecorator < Draper::Decorator    delegate_all -  def action_links(stop_area = nil) -    links = [] +  def common_action_links(stop_area = nil) +    top_links, bottom_links = [], []      stop_area ||= object      if h.policy(stop_area).update? -      links << Link.new( +      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, @@ -18,7 +18,7 @@ class StopAreaDecorator < Draper::Decorator      end      if h.policy(stop_area).destroy? -      links << Link.new( +      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, @@ -29,7 +29,35 @@ class StopAreaDecorator < Draper::Decorator        )      end -    links +    [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" +      ) +    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" +      ) +    end + +    top_links + links + bottom_links    end    def waiting_time_text diff --git a/app/decorators/stop_point_decorator.rb b/app/decorators/stop_point_decorator.rb index 196d6d490..27e1a7058 100644 --- a/app/decorators/stop_point_decorator.rb +++ b/app/decorators/stop_point_decorator.rb @@ -4,6 +4,6 @@ class StopPointDecorator < StopAreaDecorator    delegate_all    def action_links -    super(object.stop_area) +    common_action_links(object.stop_area).flatten    end  end diff --git a/app/helpers/links_helper.rb b/app/helpers/links_helper.rb index 4fb7a797d..088415dc3 100644 --- a/app/helpers/links_helper.rb +++ b/app/helpers/links_helper.rb @@ -1,5 +1,18 @@  module LinksHelper +  def custom_link_content(translation_key, klass, extra_class: nil) +    klass = ["fa", "fa-#{klass}", "mr-xs", extra_class].compact.join(" ") +    content_tag(:span, nil, class: klass) + t(translation_key) +  end +    def destroy_link_content(translation_key = 'actions.destroy') -    content_tag(:span, nil, class: 'fa fa-trash mr-xs') + t(translation_key) +    custom_link_content translation_key, 'trash' +  end + +  def deactivate_link_content(translation_key = 'actions.deactivate') +    custom_link_content translation_key, 'power-off', extra_class: "text-danger" +  end + +  def activate_link_content(translation_key = 'actions.activate') +    custom_link_content translation_key, 'power-off', extra_class: "text-success"    end  end diff --git a/app/helpers/table_builder_helper.rb b/app/helpers/table_builder_helper.rb index de78e903d..dede51920 100644 --- a/app/helpers/table_builder_helper.rb +++ b/app/helpers/table_builder_helper.rb @@ -368,6 +368,11 @@ module TableBuilderHelper    end    def gear_menu_link(link) +    klass = [] +    klass << link.extra_class if link.extra_class +    klass << 'delete-action' if link.method == :delete +    klass << 'disabled' if link.disabled +      content_tag(        :li,        link_to( @@ -377,7 +382,7 @@ module TableBuilderHelper        ) do          link.content        end, -      class: ('delete-action' if link.method == :delete) +      class: (klass.join(' ') if klass.present?)      )    end diff --git a/app/javascript/vehicle_journeys/components/Tools.js b/app/javascript/vehicle_journeys/components/Tools.js index 7621dfc10..1ef576529 100644 --- a/app/javascript/vehicle_journeys/components/Tools.js +++ b/app/javascript/vehicle_journeys/components/Tools.js @@ -17,7 +17,7 @@ export default class Tools extends Component {    hasPolicy(key) {      // Check if the user has the policy to disable or not the action -    return this.props.filters.policy[`vehicle_journeys.${key}`]  +    return this.props.filters.policy[`vehicle_journeys.${key}`]    }    render() { @@ -45,4 +45,4 @@ Tools.propTypes = {    vehicleJourneys : PropTypes.array.isRequired,    onCancelSelection: PropTypes.func.isRequired,    filters: PropTypes.object.isRequired -}
\ No newline at end of file +} diff --git a/app/models/chouette/line.rb b/app/models/chouette/line.rb index 93d4f5e8b..2d776e94b 100644 --- a/app/models/chouette/line.rb +++ b/app/models/chouette/line.rb @@ -79,5 +79,16 @@ module Chouette        line_referential.companies.where(id: ([company_id] + Array(secondary_company_ids)).compact)      end +    def deactivate! +      update_attribute :deactivated, true +    end + +    def activate! +      update_attribute :deactivated, false +    end + +    def activated? +      !deactivated +    end    end  end diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb index 8786c7252..5368d790a 100644 --- a/app/models/chouette/purchase_window.rb +++ b/app/models/chouette/purchase_window.rb @@ -16,6 +16,10 @@ module Chouette      scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) } +  def self.ransackable_scopes(auth_object = nil) +    [:contains_date] +  end +      def local_id        "IBOO-#{self.referential.id}-#{self.id}"      end diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index 3a9b44d59..2f8d7c096 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -328,5 +328,20 @@ module Chouette        end      end +    def activated? +      deleted_at.nil? +    end + +    def deactivated? +      !activated? +    end + +    def activate! +      update_attribute :deleted_at, nil +    end + +    def deactivate! +      update_attribute :deleted_at, Time.now +    end    end  end diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb index 67ea0b611..e5674fbe2 100644 --- a/app/policies/line_policy.rb +++ b/app/policies/line_policy.rb @@ -14,6 +14,14 @@ class LinePolicy < ApplicationPolicy      user.has_permission?('lines.destroy')    end +  def deactivate? +    !record.deactivated? && user.has_permission?('lines.change_status') +  end + +  def activate? +    record.deactivated? && user.has_permission?('lines.change_status') +  end +    def update?      user.has_permission?('lines.update')    end diff --git a/app/policies/stop_area_policy.rb b/app/policies/stop_area_policy.rb index faeebbc2a..e5921ef61 100644 --- a/app/policies/stop_area_policy.rb +++ b/app/policies/stop_area_policy.rb @@ -16,4 +16,12 @@ class StopAreaPolicy < ApplicationPolicy    def update?      user.has_permission?('stop_areas.update')    end + +  def deactivate? +    !record.deactivated? && user.has_permission?('stop_areas.change_status') +  end + +  def activate? +    record.deactivated? && user.has_permission?('stop_areas.change_status') +  end  end diff --git a/app/views/dashboards/_dashboard.html.slim b/app/views/dashboards/_dashboard.html.slim index b468fed27..075b94ddc 100644 --- a/app/views/dashboards/_dashboard.html.slim +++ b/app/views/dashboards/_dashboard.html.slim @@ -34,34 +34,21 @@            em.small.text-muted              = t('dasboard.calendars.none') -    .panel.panel-default -      .panel-heading -        h3.panel-title.with_actions -          = t('dasboard.purchase_windows.title') -          div -            = link_to '', purchase_windows_path, class: ' fa fa-chevron-right pull-right' -      - if @dashboard.current_organisation.purchase_windows.present? -        .list-group -          - @dashboard.current_organisation.purchase_windows.order("updated_at desc").limit(5).each do |purchase_window| -            = link_to purchase_window.name, referential_purchase_window_path(purchase_window.referential, purchase_window), class: 'list-group-item' -      - else -        .panel-body -          em.small.text-muted -            = t('dasboard.purchase_windows.none') -    .col-lg-6.col-md-6.col-sm-6.col-xs-12      .panel.panel-default -      .panel-heading -        h3.panel-title -          = t('dashboard.stop_area_referentials.title') -      .list-group -        - @dashboard.current_organisation.stop_area_referentials.each do |referential| -          = link_to referential.name, stop_area_referential_stop_areas_path(referential), class: 'list-group-item' +      - @dashboard.current_organisation.stop_area_referentials.each do |referential| +        .panel-heading +          h3.panel-title +            = referential.name +        .list-group +          = link_to Chouette::StopArea.model_name.human.pluralize.capitalize, stop_area_referential_stop_areas_path(referential), class: 'list-group-item'      .panel.panel-default -      .panel-heading -        h3.panel-title -          = t('dashboard.line_referentials.title') -      .list-group -        - @dashboard.current_organisation.line_referentials.all.each do |referential| -          = link_to referential.name, line_referential_lines_path(referential), class: 'list-group-item' +      - @dashboard.current_organisation.line_referentials.all.each do |referential| +        .panel-heading +          h3.panel-title +            = referential.name +        .list-group +            = link_to Chouette::Line.model_name.human.pluralize.capitalize, line_referential_lines_path(referential), class: 'list-group-item' +            = link_to Chouette::Company.model_name.human.pluralize.capitalize, line_referential_companies_path(referential), class: 'list-group-item' +            = link_to "Réseaux", line_referential_networks_path(referential), class: 'list-group-item' diff --git a/app/views/lines/_form.html.slim b/app/views/lines/_form.html.slim index de0308289..909d6512e 100644 --- a/app/views/lines/_form.html.slim +++ b/app/views/lines/_form.html.slim @@ -3,7 +3,7 @@      .col-lg-12        = f.input :name        = f.input :network_id, as: :select, :collection => @line_referential.networks, include_blank: false -      = f.input :company_id, as: :select, :collection => @line_referential.companies, include_blank: false +      = f.input :company_id, as: :select, :collection => @line_referential.companies, include_blank: true        = f.input :secondary_company_ids, :collection => @line_referential.companies, include_blank: false, input_html: { multiple: true, 'data-select2ed': true }, label: t('activerecord.attributes.line.secondary_company')        = f.input :published_name        = f.input :registration_number @@ -20,4 +20,3 @@    .separator    = f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'lines_form' - diff --git a/app/views/lines/index.html.slim b/app/views/lines/index.html.slim index 8b035b477..e94837ed5 100644 --- a/app/views/lines/index.html.slim +++ b/app/views/lines/index.html.slim @@ -41,7 +41,7 @@                ), \                TableBuilderHelper::Column.new( \                  key: 'companies.name', \ -                attribute: Proc.new { |n| n.try(:company).try(:name) } \ +                attribute: Proc.new { |n| n&.company&.name || "-" } \                ), \                TableBuilderHelper::Column.new( \                  key: :transport_mode, \ diff --git a/app/views/lines/show.html.slim b/app/views/lines/show.html.slim index d62fe30d6..83244f739 100644 --- a/app/views/lines/show.html.slim +++ b/app/views/lines/show.html.slim @@ -6,7 +6,7 @@          = link_to link.href,              method: link.method,              data: link.data, -            class: 'btn btn-primary' do +            class: "btn btn-primary #{link.disabled ? "disabled" : ""}" do                = link.content  - page_header_content_for @line diff --git a/app/views/purchase_windows/_filters.html.slim b/app/views/purchase_windows/_filters.html.slim index 9c83d20db..4d7c8ce26 100644 --- a/app/views/purchase_windows/_filters.html.slim +++ b/app/views/purchase_windows/_filters.html.slim @@ -8,7 +8,7 @@      .form-group        = f.label Chouette::PurchaseWindow.human_attribute_name(:date), class: 'control-label' -      = f.input :date_ranges, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', include_blank: true +      = f.input :contains_date, as: :date, label: false, wrapper_html: { class: 'date smart_date' }, class: 'form-control', default: @date, include_blank: @date ? false : true    .actions      = link_to t('actions.erase'), referential_purchase_windows_path, class: 'btn btn-link' diff --git a/app/views/purchase_windows/_form.html.slim b/app/views/purchase_windows/_form.html.slim index 8f3ba769d..8821ecc8a 100644 --- a/app/views/purchase_windows/_form.html.slim +++ b/app/views/purchase_windows/_form.html.slim @@ -2,7 +2,15 @@    .row      .col-lg-12        = f.input :name -      = f.input :color, as: :select, boolean_style: :inline, collection: Chouette::PurchaseWindow.color.values, input_html: {class: 'color_selector'} +      // = f.input :color, as: :select, boolean_style: :inline, collection: Chouette::PurchaseWindow.color.values, input_html: {class: 'color_selector '} +      div +        .form-group +          label.select.optional.col-sm-4.col-xs-5.control-label +            = @purchase_window.class.human_attribute_name :color +          div.col-sm-8.col-xs-7 +            span.fa.fa-circle style="color:#7F551B" + +      = f.input :color, as: :hidden, input_html: { value: '#7F551B' }    .separator diff --git a/app/views/purchase_windows/index.html.slim b/app/views/purchase_windows/index.html.slim index 38954b5dc..04f9fb0a8 100644 --- a/app/views/purchase_windows/index.html.slim +++ b/app/views/purchase_windows/index.html.slim @@ -28,7 +28,8 @@                ), \                TableBuilderHelper::Column.new( \                  key: :bounding_dates, \ -                attribute: Proc.new {|w| w.bounding_dates.nil? ? '-' : t('validity_range', debut: l(w.bounding_dates.begin, format: :short), end: l(w.bounding_dates.end, format: :short))}  \ +                attribute: Proc.new {|w| w.bounding_dates.nil? ? '-' : t('validity_range', debut: l(w.bounding_dates.begin, format: :short), end: l(w.bounding_dates.end, format: :short))},  \ +                sortable: false \                ) \              ],              links: [:show], diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index 9852fb0a3..96755359c 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -68,7 +68,7 @@                ), \                TableBuilderHelper::Column.new( \                  key: 'companies.name', \ -                attribute: Proc.new { |n| n.try(:company).try(:name) } \ +                attribute: Proc.new { |n| n&.company&.name || "-" } \                ) \              ],              links: [:show], diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index 52c1a9728..4ad9d524d 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -1,5 +1,11 @@  - breadcrumb :vehicle_journeys, @referential, @route  - content_for :page_header_title, t('vehicle_journeys.index.title', route: @route.name) +- if @route.opposite_route.present? +  - content_for :page_header_content do +    .row.mb-sm +     .col-lg-12.text-right +       = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary') +  .page_content    .container-fluid diff --git a/config/deploy.rb b/config/deploy.rb index 33771507f..d541f2581 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -9,8 +9,8 @@ set :deploy_to, "/var/www/stif-boiv"  set :use_sudo, false  default_run_options[:pty] = true  set :group_writable, true -set :bundle_cmd, "/var/lib/gems/2.2.0/bin/bundle" -set :rake, "#{bundle_cmd} exec /var/lib/gems/2.2.0/bin/rake" +set :bundle_cmd, "/var/lib/gems/2.3.0/bin/bundle" +set :rake, "#{bundle_cmd} exec rake"  set :keep_releases, -> { fetch(:kept_releases, 5) }  after "deploy:restart", "deploy:cleanup" @@ -29,7 +29,7 @@ require 'whenever/capistrano'  #after 'deploy:finalize_update', 'npm:install'  # Whenever -set :whenever_variables, ->{ "'environment=#{fetch :whenever_environment}&bundle_command=bin/bundle exec&additionnal_path=/var/lib/gems/2.2.0/bin'" } # invoke bin/bundle to use 'correct' ruby environment +set :whenever_variables, ->{ "'environment=#{fetch :whenever_environment}&bundle_command=bin/bundle exec&additionnal_path=/var/lib/gems/2.3.0/bin'" } # invoke bin/bundle to use 'correct' ruby environment  set :whenever_command, "sudo /usr/local/sbin/whenever-sudo" # use sudo to change www-data crontab  set :whenever_user, "www-data" # use www-data crontab diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml index 2706ba69d..f5f48db22 100644 --- a/config/locales/actions.en.yml +++ b/config/locales/actions.en.yml @@ -1,6 +1,8 @@  en:    actions:      edit: "Edit" +    activate: 'Activate' +    deactivate: 'Deactivate'      destroy: "Destroy"      delete: "Delete"      search: "Search" diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml index e796017c7..4b3ac6901 100644 --- a/config/locales/actions.fr.yml +++ b/config/locales/actions.fr.yml @@ -1,6 +1,8 @@  fr:    actions:      edit: 'Editer' +    activate: 'Activer' +    deactivate: 'Désactiver'      destroy: 'Supprimer'      delete: 'Supprimer'      search: "Chercher" diff --git a/config/locales/calendars.fr.yml b/config/locales/calendars.fr.yml index bd3051730..88cb275ff 100644 --- a/config/locales/calendars.fr.yml +++ b/config/locales/calendars.fr.yml @@ -31,7 +31,7 @@ fr:        destroy_confirm: Etes vous sûr de supprimer cet calendrier ?      errors:        overlapped_periods: Une autre période chevauche cette période -      short_period: Une période doit être d'un duréé de deux jours minimum +      short_period: "Une période doit être d'une durée de deux jours minimum"      index:        title: Calendriers        all: Tous diff --git a/config/locales/lines.en.yml b/config/locales/lines.en.yml index 78d5c36be..49a629557 100644 --- a/config/locales/lines.en.yml +++ b/config/locales/lines.en.yml @@ -6,8 +6,12 @@ en:        edit: "Edit this line"        edit_footnotes: "Edit line footnotes"        destroy: "Remove this line" -      destroy_confirm: "Are you sure you want destroy this line?" -      destroy_selection_confirm: "Are you sure you want destroy those lines?" +      activate: "Activate this line" +      deactivate: "Deactivate this line" +      activate_confirm: "Are you sure you want to activate this line ?" +      deactivate_confirm: "Are you sure you want tode activate this line ?" +      destroy_confirm: "Are you sure you want to destroy this line ?" +      destroy_selection_confirm: "Are you sure you want to destroy those lines ?"        import: "Import lines"        export_kml: "Export KML line"        export_kml_all: "Export KML lines" @@ -66,6 +70,7 @@ en:          networks:            name: "Network"          company_id: "Company" +        company: "Company"          secondary_company: "Secondary company"          companies:            name: "Company" diff --git a/config/locales/lines.fr.yml b/config/locales/lines.fr.yml index 36254d754..8fd409f1e 100644 --- a/config/locales/lines.fr.yml +++ b/config/locales/lines.fr.yml @@ -6,6 +6,10 @@ fr:        edit: "Editer cette ligne"        edit_footnotes: "Editer notes en bas de page"        destroy: "Supprimer cette ligne" +      activate: "Activer cette ligne" +      deactivate: "Désactiver cette ligne" +      activate_confirm: "Etes vous sûr d'activer cette ligne ?" +      deactivate_confirm: "Etes vous sûr de désactiver cette ligne ?"        destroy_confirm: "Etes vous sûr de supprimer cette ligne ?"        destroy_selection_confirm: "Etes vous sûr de supprimer cette sélection de lignes ?"        import: "Importer des lignes" @@ -67,6 +71,7 @@ fr:          networks:            name: "Réseau"          company_id: "Transporteur principal" +        company: "Transporteur principal"          secondary_company: "Transporteurs secondaires"          companies:            name: "Transporteur principal" diff --git a/config/locales/purchase_windows.en.yml b/config/locales/purchase_windows.en.yml index 5ffed305a..c0f402bf4 100644 --- a/config/locales/purchase_windows.en.yml +++ b/config/locales/purchase_windows.en.yml @@ -48,7 +48,7 @@ en:        title: purchase window %{name}    simple_form:      labels: -      purchase_windows: +      purchase_window:          date_value: Date          add_a_date: Add a date          add_a_date_range: Add a date range @@ -66,7 +66,7 @@ en:          name: Name          date_ranges: Date ranges          referential: Referential -        color: Color +        color: Associated Color          bounding_dates: Bounding Dates      errors:        models: diff --git a/config/locales/purchase_windows.fr.yml b/config/locales/purchase_windows.fr.yml index df5d45d82..589546c32 100644 --- a/config/locales/purchase_windows.fr.yml +++ b/config/locales/purchase_windows.fr.yml @@ -29,7 +29,7 @@ fr:        destroy_confirm: Etes vous sûr de supprimer cet calendrier commercial ?      errors:        overlapped_periods: Une autre période chevauche cette période -      short_period: Une période doit être d'un duréé de deux jours minimum +      short_period: "Une période doit être d'une durée de deux jours minimum"      index:        title: Calendriers commerciaux        all: Tous @@ -48,7 +48,7 @@ fr:        title: Calendrier commercial %{name}    simple_form:      labels: -      calendar: +      purchase_window:          date_value: Date          add_a_date: Ajouter une date          add_a_date_range: Ajouter un intervalle de dates @@ -67,7 +67,7 @@ fr:          short_name: Nom court          date_ranges: Intervalles de dates          referential: Jeu de données -        color: Couleur +        color: Couleur associée          bounding_dates: Période englobante      errors:        models: diff --git a/config/locales/routes.en.yml b/config/locales/routes.en.yml index d82ba98dd..7b82e788b 100644 --- a/config/locales/routes.en.yml +++ b/config/locales/routes.en.yml @@ -13,6 +13,7 @@ en:        export_hub_all: "Export HUB routes"        add_stop_point: "Add stop point"        new_stop_point: "Create new stop" +      opposite_route_timetable: "Timetable back"      new:        title: "Add a new route"      edit: diff --git a/config/locales/routes.fr.yml b/config/locales/routes.fr.yml index 457345ae8..1d151e60b 100644 --- a/config/locales/routes.fr.yml +++ b/config/locales/routes.fr.yml @@ -13,6 +13,7 @@ fr:        export_hub_all: "Export HUB des itinéraires"        add_stop_point: "Ajouter un arrêt"        new_stop_point: "Créer un arrêt pour l'ajouter" +      opposite_route_timetable: "Horaires retour"      new:        title: "Ajouter un itinéraire"      edit: diff --git a/config/locales/stop_areas.en.yml b/config/locales/stop_areas.en.yml index 3ef3835e2..4d84d1191 100644 --- a/config/locales/stop_areas.en.yml +++ b/config/locales/stop_areas.en.yml @@ -17,6 +17,10 @@ en:        new: "Add a new stop"        edit: "Edit this stop"        destroy: "Remove" +      activate: "Activate this stop" +      deactivate: "Deactivate this stop" +      activate_confirm: "Are you sure you want to activate this stop ?" +      deactivate_confirm: "Are you sure you want tode activate this stop ?"        deleted_at: "Activated"        destroy_confirm: "Are you sure you want destroy this stop and all of his children ?"        select_parent: "Create or modify the relation child -> parent" diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml index 69e3ba71e..eda1e4e3d 100644 --- a/config/locales/stop_areas.fr.yml +++ b/config/locales/stop_areas.fr.yml @@ -17,6 +17,10 @@ fr:        new: "Ajouter un arrêt"        edit: "Editer cet arrêt"        destroy: "Supprimer" +      activate: "Activer cet arrêt" +      deactivate: "Désactiver cet arrêt" +      activate_confirm: "Etes vous sûr d'activer cet arrêt ?" +      deactivate_confirm: "Etes vous sûr de désactiver cet arrêt ?"        deleted_at: "Activé"        destroy_confirm: "Etes vous sûr de supprimer cet arrêt ainsi que tous ses fils?"        select_parent: "Créer ou éditer la relation enfant -> parent" diff --git a/config/routes.rb b/config/routes.rb index d097d2d71..e05f5d365 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -86,14 +86,19 @@ ChouetteIhm::Application.routes.draw do      resources :compliance_control_blocks, :except => [:show, :index]    end +  deactivable = Proc.new do +    put :deactivate, on: :member +    put :activate, on: :member +  end +    resources :stop_area_referentials, :only => [:show] do      post :sync, on: :member -    resources :stop_areas +    resources :stop_areas, &deactivable    end    resources :line_referentials, :only => [:show, :edit, :update] do      post :sync, on: :member -    resources :lines +    resources :lines, &deactivable      resources :group_of_lines      resources :companies      resources :networks diff --git a/lib/link.rb b/lib/link.rb index 7683a808f..33995c2f7 100644 --- a/lib/link.rb +++ b/lib/link.rb @@ -1,10 +1,12 @@  class Link -  attr_reader :content, :href, :method, :data +  attr_reader :content, :href, :method, :data, :extra_class, :disabled -  def initialize(content: nil, href:, method: nil, data: nil) +  def initialize(content: nil, href:, method: nil, data: nil, extra_class: nil, disabled: false)      @content = content      @href = href      @method = method      @data = data +    @extra_class = extra_class +    @disabled = disabled    end  end diff --git a/spec/controllers/lines_controller_spec.rb b/spec/controllers/lines_controller_spec.rb new file mode 100644 index 000000000..ce5adbbdd --- /dev/null +++ b/spec/controllers/lines_controller_spec.rb @@ -0,0 +1,38 @@ +RSpec.describe LinesController, :type => :controller do +  login_user + +  let(:line_referential) { create :line_referential } +  let(:line) { create :line, line_referential: line_referential } + +  describe 'PUT deactivate' do +    let(:request){ put :deactivate, id: line.id, line_referential_id: line_referential.id } + +    it 'should redirect to 403' do +       expect(request).to redirect_to "/403" +    end + +    with_permission "lines.change_status" do +      it 'returns HTTP success' do +        expect(request).to redirect_to [line_referential, line] +        expect(line.reload).to be_deactivated +      end +    end +  end + +  describe 'PUT activate' do +    let(:request){ put :activate, id: line.id, line_referential_id: line_referential.id } +    before(:each){ +      line.deactivate! +    } +    it 'should redirect to 403' do +       expect(request).to redirect_to "/403" +    end + +    with_permission "lines.change_status" do +      it 'returns HTTP success' do +        expect(request).to redirect_to [line_referential, line] +        expect(line.reload).to be_activated +      end +    end +  end +end diff --git a/spec/controllers/stop_area_referentials_controller_spec.rb b/spec/controllers/stop_area_referentials_controller_spec.rb index c8d7e1736..384323334 100644 --- a/spec/controllers/stop_area_referentials_controller_spec.rb +++ b/spec/controllers/stop_area_referentials_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe StopAreaReferentialsController, :type => :controller do    describe 'PUT sync' do      let(:request){ put :sync, id: stop_area_referential.id } -    it { request.should redirect_to "/403" } +    it { expect(request).to redirect_to "/403" }      with_permission "stop_area_referentials.synchronize" do        it 'returns HTTP success' do diff --git a/spec/controllers/stop_areas_controller_spec.rb b/spec/controllers/stop_areas_controller_spec.rb new file mode 100644 index 000000000..2b5f8c3e2 --- /dev/null +++ b/spec/controllers/stop_areas_controller_spec.rb @@ -0,0 +1,38 @@ +RSpec.describe StopAreasController, :type => :controller do +  login_user + +  let(:stop_area_referential) { create :stop_area_referential } +  let(:stop_area) { create :stop_area, stop_area_referential: stop_area_referential } + +  describe 'PUT deactivate' do +    let(:request){ put :deactivate, id: stop_area.id, stop_area_referential_id: stop_area_referential.id } + +    it 'should redirect to 403' do +       expect(request).to redirect_to "/403" +    end + +    with_permission "stop_areas.change_status" do +      it 'returns HTTP success' do +        expect(request).to redirect_to [stop_area_referential, stop_area] +        expect(stop_area.reload).to be_deactivated +      end +    end +  end + +  describe 'PUT activate' do +    let(:request){ put :activate, id: stop_area.id, stop_area_referential_id: stop_area_referential.id } +    before(:each){ +      stop_area.deactivate! +    } +    it 'should redirect to 403' do +       expect(request).to redirect_to "/403" +    end + +    with_permission "stop_areas.change_status" do +      it 'returns HTTP success' do +        expect(request).to redirect_to [stop_area_referential, stop_area] +        expect(stop_area.reload).to be_activated +      end +    end +  end +end diff --git a/spec/factories/chouette_routes.rb b/spec/factories/chouette_routes.rb index 4e20059fe..7443d08bc 100644 --- a/spec/factories/chouette_routes.rb +++ b/spec/factories/chouette_routes.rb @@ -31,6 +31,13 @@ FactoryGirl.define do          end        end + +      trait :with_opposite do +        after(:create) do |route| +          opposite = create :route +          route.opposite_route = opposite +        end +      end      end      factory :route_with_after_commit do diff --git a/spec/factories/chouette_stop_areas.rb b/spec/factories/chouette_stop_areas.rb index 7f937e361..94517f856 100644 --- a/spec/factories/chouette_stop_areas.rb +++ b/spec/factories/chouette_stop_areas.rb @@ -8,5 +8,9 @@ FactoryGirl.define do      longitude {10.0 * rand}      association :stop_area_referential + +    trait :deactivated do +      deleted_at { 1.hour.ago } +    end    end  end diff --git a/spec/features/purchase_windows_permission_spec.rb b/spec/features/purchase_windows_permission_spec.rb index e74fb5c17..9f155a1e8 100644 --- a/spec/features/purchase_windows_permission_spec.rb +++ b/spec/features/purchase_windows_permission_spec.rb @@ -4,6 +4,10 @@ require 'spec_helper'  describe "PurchaseWindows", :type => :feature do    login_user +  before do +    @user.organisation.update features: %w{purchase_windows} +  end +    let(:purchase_window) { create :purchase_window, referential: first_referential}    describe 'permissions' do diff --git a/spec/features/purchase_windows_spec.rb b/spec/features/purchase_windows_spec.rb new file mode 100644 index 000000000..8edc85cc6 --- /dev/null +++ b/spec/features/purchase_windows_spec.rb @@ -0,0 +1,65 @@ +describe "PurchaseWindows", type: :feature do +  login_user + +  describe "#index" do +    with_permissions('purchase_windows.create') do +      it "allows users to create new purchase windows" do +        name = 'Test purchase window create' + +        visit(referential_purchase_windows_path(first_referential.id)) + +        click_link(I18n.t('purchase_windows.actions.new')) + +        fill_in('purchase_window[name]', with: name) +        select('#DD2DAA', from: 'purchase_window[color]') + +        click_link(I18n.t('simple_form.labels.purchase_window.add_a_date_range')) +        click_button(I18n.t('actions.submit')) + +        expect(page).to have_content(name) +      end +    end + +    with_permissions('purchase_windows.update') do +      it "allows users to update purchase windows" do +        actual_name = 'Existing purchase window' +        expected_name = 'Updated purchase window' +        create( +          :purchase_window, +          referential: first_referential, +          name: actual_name +        ) + +        visit(referential_purchase_windows_path(first_referential.id)) + +        click_link(actual_name) + +        click_link(I18n.t('purchase_windows.actions.edit')) +        fill_in('purchase_window[name]', with: expected_name) + +        click_button(I18n.t('actions.submit')) + +        expect(page).to have_content(expected_name) +      end +    end + +    with_permissions('purchase_windows.destroy') do +      it "allows users to destroy purchase windows" do +        name = 'Existing purchase window' +        create( +          :purchase_window, +          referential: first_referential, +          name: name +        ) + +        visit(referential_purchase_windows_path(first_referential.id)) + +        click_link(name) + +        click_link(I18n.t('purchase_windows.actions.destroy')) + +        expect(page).to_not have_content(name) +      end +    end +  end +end diff --git a/spec/support/integration_spec_helper.rb b/spec/support/integration_spec_helper.rb index 78efb9027..36306559d 100644 --- a/spec/support/integration_spec_helper.rb +++ b/spec/support/integration_spec_helper.rb @@ -1,15 +1,20 @@  module IntegrationSpecHelper -  def paginate_collection klass, decorator, page=1 -    ModelDecorator.decorate( klass.page(page), with: decorator ) +  def paginate_collection klass, decorator, page=1, context={} +    collection = klass.page(page) +    if decorator +      collection = ModelDecorator.decorate(collection, with: decorator, context: context) +    end +    collection    end    def build_paginated_collection factory, decorator, opts={} +    context = opts.delete(:context) || {}      count = opts.delete(:count) || 2      page = opts.delete(:page) || 1      klass = nil -    count.times { klass ||= create(factory, opts).class } -    paginate_collection klass, decorator, page +    count.times { klass = create(factory, opts).class } +    paginate_collection klass, decorator, page, context    end    module Methods @@ -30,20 +35,33 @@ RSpec.configure do |config|    config.include IntegrationSpecHelper, type: :view  end -RSpec::Matchers.define :have_link_for_each_item do |collection, name, href| +RSpec::Matchers.define :have_link_for_each_item do |collection, name, opts| +  opts = {href: opts} unless opts.is_a? Hash +  href = opts[:href] +  method = opts[:method] +  method_selector = method.present? ? "[data-method='#{method.downcase}']": ""    match do |actual|      collection.each do |item| -      expect(rendered).to have_selector("tr.#{TableBuilderHelper.item_row_class_name(collection)}-#{item.id} .actions a[href='#{href.call(item)}']", count: 1) +      @selector = "tr.#{TableBuilderHelper.item_row_class_name(collection)}-#{item.id} .actions a[href='#{href.call(item)}']#{method_selector}" +      expect(rendered).to have_selector(@selector, count: 1)      end    end    description { "have #{name} link for each item" } +  failure_message do +    "expected view to have #{name} link for each item, failed with selector: \"#{@selector}\"" +  end  end  RSpec::Matchers.define :have_the_right_number_of_links do |collection, count| -  match do |actual| +  match do      collection.each do |item| -      expect(rendered).to have_selector("tr.#{TableBuilderHelper.item_row_class_name(collection)}-#{item.id} .actions a", count: count) +      @selector = "tr.#{TableBuilderHelper.item_row_class_name(collection)}-#{item.id} .actions a" +      expect(rendered).to have_selector(@selector, count: count)      end    end    description { "have #{count} links for each item" } +  failure_message do +    actual = Capybara::Node::Simple.new(rendered).all(@selector).count +    "expected #{count} links for each item, got #{actual} for \"#{@selector}\"" +  end  end diff --git a/spec/views/lines/index.html.erb_spec.rb b/spec/views/lines/index.html.erb_spec.rb deleted file mode 100644 index dbc3cbdb7..000000000 --- a/spec/views/lines/index.html.erb_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe "/lines/index", :type => :view do - -  let!(:line_referential) { assign :line_referential, create(:line_referential) } -  let!(:network) { create :network } -  let!(:company) { create :company } -  let!(:lines) { assign :lines, Array.new(2) { create(:line, line_referential: line_referential, network: network, company: company) }.paginate } -  let!(:q) { assign :q, Ransack::Search.new(Chouette::Line) } - -  before :each do -    allow(view).to receive(:link_with_search).and_return("#") -  end - -  # it "should render a show link for each group" do -  #   render -  #   lines.each do |line| -  #     expect(rendered).to have_selector(".line a[href='#{view.line_referential_line_path(line_referential, line)}']", :text => line.name) -  #   end -  # end -  # -  # it "should render a link to create a new group" do -  #   render -  #   expect(view.content_for(:sidebar)).to have_selector(".actions a[href='#{new_line_referential_line_path(line_referential)}']") -  # end - -end diff --git a/spec/views/lines/index.html.slim_spec.rb b/spec/views/lines/index.html.slim_spec.rb new file mode 100644 index 000000000..498784912 --- /dev/null +++ b/spec/views/lines/index.html.slim_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe "/lines/index", :type => :view do +  let(:deactivated_line){ nil } +  let(:line_referential) { assign :line_referential, create(:line_referential) } +  let(:current_organisation) { current_user.organisation } +  let(:context) { +     { +       current_organisation: current_organisation, +       line_referential: line_referential +     } +   } +  let(:lines) do +    assign :lines, build_paginated_collection(:line, LineDecorator, line_referential: line_referential, context: context) +  end +  let!(:q) {  assign :q, Ransack::Search.new(Chouette::Line) } + +  before :each do +    deactivated_line +    allow(view).to receive(:collection).and_return(lines) +    allow(view).to receive(:current_referential).and_return(line_referential) +    controller.request.path_parameters[:line_referential_id] = line_referential.id +    render +  end + +  common_items = ->{ +    it { should have_link_for_each_item(lines, "show", -> (line){ view.line_referential_line_path(line_referential, line) }) } +    it { should have_link_for_each_item(lines, "network", -> (line){ view.line_referential_network_path(line_referential, line.network) }) } +    it { should have_link_for_each_item(lines, "company", -> (line){ view.line_referential_company_path(line_referential, line.company) }) } +  } + +  common_items.call() +  it { should have_the_right_number_of_links(lines, 3) } + +  with_permission "lines.change_status" do +    common_items.call() +    it { should have_link_for_each_item(lines, "deactivate", -> (line){ view.deactivate_line_referential_line_path(line_referential, line) }) } +    it { should have_the_right_number_of_links(lines, 4) } +  end + +  with_permission "lines.destroy" do +    common_items.call() +    it { +      should have_link_for_each_item(lines, "destroy", { +        href: ->(line){ view.line_referential_line_path(line_referential, line)}, +        method: :delete +      }) +    } +    it { should have_the_right_number_of_links(lines, 4) } +  end + +  context "with a deactivated item" do +    with_permission "lines.change_status" do +      let(:deactivated_line){ create :line, deactivated: true } + +      common_items.call() +      it "should display an activate link for the deactivated one" do +        lines.each do |line| +          if line == deactivated_line +            href = view.activate_line_referential_line_path(line_referential, line) +          else +            href = view.deactivate_line_referential_line_path(line_referential, line) +          end +          selector = "tr.#{TableBuilderHelper.item_row_class_name(lines)}-#{line.id} .actions a[href='#{href}']" +          expect(rendered).to have_selector(selector, count: 1) +        end +      end +      it { should have_the_right_number_of_links(lines, 4) } +    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 8daa5eb4b..520cecc1a 100644 --- a/spec/views/stop_areas/index.html.slim_spec.rb +++ b/spec/views/stop_areas/index.html.slim_spec.rb @@ -1,14 +1,15 @@  require 'spec_helper'  describe "/stop_areas/index", :type => :view do - -  let!(:stop_area_referential) { assign :stop_area_referential, create(:stop_area_referential) } -  let!(:stop_areas) do +  let(:deactivated_stop_area){ nil } +  let(:stop_area_referential) { assign :stop_area_referential, create(:stop_area_referential) } +  let(:stop_areas) do      assign :stop_areas, build_paginated_collection(:stop_area, StopAreaDecorator, stop_area_referential: stop_area_referential)    end    let!(:q) { assign :q, Ransack::Search.new(Chouette::StopArea) }    before :each do +    deactivated_stop_area      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) @@ -16,19 +17,60 @@ describe "/stop_areas/index", :type => :view do      render    end -  it { should have_link_for_each_item(stop_areas, "show", -> (stop_area){ view.stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) } +  common_items = ->{ +    it { should have_link_for_each_item(stop_areas, "show", -> (stop_area){ view.stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) } +  } + +  common_items.call()    it { should have_the_right_number_of_links(stop_areas, 1) }    with_permission "stop_areas.create" do -    it { should have_link_for_each_item(stop_areas, "show", -> (stop_area){ view.stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) } +    common_items.call()      it { should_not have_link_for_each_item(stop_areas, "create", -> (stop_area){ view.new_stop_area_referential_stop_area_path(stop_area_referential) }) }      it { should have_the_right_number_of_links(stop_areas, 1) }    end    with_permission "stop_areas.update" do -    it { should have_link_for_each_item(stop_areas, "show", -> (stop_area){ view.stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) } +    common_items.call()      it { should have_link_for_each_item(stop_areas, "edit", -> (stop_area){ view.edit_stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) }      it { should have_the_right_number_of_links(stop_areas, 2) }    end +  with_permission "stop_areas.change_status" do +    common_items.call() +    it { should have_link_for_each_item(stop_areas, "deactivate", -> (stop_area){ view.deactivate_stop_area_referential_stop_area_path(stop_area_referential, stop_area) }) } +    it { should have_the_right_number_of_links(stop_areas, 2) } +  end + +  with_permission "stop_areas.destroy" do +    common_items.call() +    it { +      should have_link_for_each_item(stop_areas, "destroy", { +        href: ->(stop_area){ view.stop_area_referential_stop_area_path(stop_area_referential, stop_area)}, +        method: :delete +      }) +    } +    it { should have_the_right_number_of_links(stop_areas, 2) } +  end + +  context "with a deactivated item" do +    with_permission "stop_areas.change_status" do +      let(:deactivated_stop_area){ create :stop_area, :deactivated, stop_area_referential: stop_area_referential } + +      common_items.call() +      it "should display an activate link for the deactivated one" do +        stop_areas.each do |stop_area| +          if stop_area == deactivated_stop_area +            href = view.activate_stop_area_referential_stop_area_path(stop_area_referential, stop_area) +          else +            href = view.deactivate_stop_area_referential_stop_area_path(stop_area_referential, stop_area) +          end +          selector = "tr.#{TableBuilderHelper.item_row_class_name(stop_areas)}-#{stop_area.id} .actions a[href='#{href}']" +          expect(rendered).to have_selector(selector, count: 1) +        end +      end +      it { should have_the_right_number_of_links(stop_areas, 2) } +    end +  end +  end diff --git a/spec/views/vehicle_journeys/index.html.slim_spec.rb b/spec/views/vehicle_journeys/index.html.slim_spec.rb new file mode 100644 index 000000000..7f0a9c5aa --- /dev/null +++ b/spec/views/vehicle_journeys/index.html.slim_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe "/vehicle_journeys/index", :type => :view do + +  let!(:referential) { assign :referential, create(:referential) } +  let!(:line) { assign :line, create(:line) } +  let!(:route) { assign :route, create(:route, line: line) } +  let!(:vehicle_journeys) do +    assign :vehicle_journeys, build_paginated_collection(:vehicle_journey, nil, route: route) +  end + +  before :each do +    allow(view).to receive(:link_with_search).and_return("#") +    allow(view).to receive(:collection).and_return(vehicle_journeys) +    allow(view).to receive(:current_referential).and_return(referential) +    controller.request.path_parameters[:referential_id] = referential.id +    render +  end + +  context "with an opposite_route" do +    let!(:route) { assign :route, create(:route, :with_opposite, line: line) } + +    it "should have an 'oppposite route timetable' button" do +      href = view.referential_line_route_vehicle_journeys_path(referential, line, route.opposite_route) +      oppposite_button_selector = "a[href=\"#{href}\"]" + +      expect(view.content_for(:page_header_content)).to have_selector oppposite_button_selector +    end +  end +end | 
