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 |
