diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 4644a6bb..b42ccb06 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -11,6 +11,9 @@ linters: # Offense count: 945 LineLength: exclude: + - "app/views/admin/booths/_form.html.haml" + - "app/views/admin/booths/index.html.haml" + - "app/views/admin/booths/show.html.haml" - "app/views/admin/campaigns/_form.html.haml" - "app/views/admin/campaigns/index.html.haml" - "app/views/admin/cfps/_form.html.haml" @@ -179,6 +182,7 @@ linters: # Offense count: 223 InstanceVariables: exclude: + - "app/views/admin/booths/_change_state_dropdown.html.haml" - "app/views/admin/campaigns/_form.html.haml" - "app/views/admin/cfps/_booths_cfp.html.haml" - "app/views/admin/cfps/_form.html.haml" diff --git a/.travis.yml b/.travis.yml index 6c83258b..9191bbd7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty language: ruby cache: bundler rvm: - - 2.2.3 + - 2.4.0 before_install: - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" - "echo `phantomjs -v`" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..7e9792fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +OSEM is part of the openSUSE project. We follow all the [openSUSE Guiding Principles!](http://en.opensuse.org/openSUSE:Guiding_principles) If you think someone doesn't do that, please let us know at maintainers@osem.io diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0688999..5193725d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,59 +71,6 @@ You can access the app [localhost:3000](http://localhost:3000). Whatever you cha * If you are already a contributor and you get a positive review, you can merge your pull-request yourself * If you are not already a contributor please request a merge via the pull-request comments -### Getting Started - -* When you get involved with OSEM for the first time, you can choose issues labeled as [Junior]( https://github.com/openSUSE/osem/issues?q=is%3Aissue+is%3Aopen+label%3AJunior) -* Leave a comment on the issue that you want to work on it - * We expect you to work on it and show progress by either opening a PR or commenting on the issue - * If you change your mind, and do not want to work on the issue any more, please be fair to others and leave a comment to let us know - * Do **not** work on issues that are assigned to others. If you are uncertain, ask and wait for a **contributor** to reply -* Avoid working on issues that have no label - * If you have opened a new issue, please wait for a contributor to add relevant labels -* If an issue is a feature, we should first have a rough idea on how we want to implement it - * If there is already such a discussion on the issue, you can go ahead and pick this up - * If not, please first leave a comment on how you want to implement it and wait for contributors' feedback - -### Commits -* Commit title should be short and descriptive - * title (or summary line) is the first line of the commit message - * that says what the commit is doing - * in no more than 50 characters - * starting with a word like 'Fix' or 'Add' or 'Change' - * **without** a period (.) at the end - * followed by a blank line -* Commit messages are - * up to 72 characters - * with break lines -* Reference the issue(s) the commit closes - * https://help.github.com/articles/closing-issues-via-commit-messages - * If you haven't done so since the beginning, you should reference the issue when you squash your commits - -### Pull Requests workflow -Please open a pull request (PR) only when you have finished coding, and your changes are ready to be reviewed for merging. - -* Title - * Include a comprehensive title about what this PR is doing - * Referencing the issue number on the PR title is not giving any information about what this PR is about - * The title should be short (50 characters maximum); you can add more information in the description -* Description - * Add a couple of lines about what is the problem you are trying to solve and how you have addressed it - * Add bullet points about the new things you are introducing, if applicable - * Reference the issue(s) you are solving - * Add a screenshot of your change, if you are working on something that changes how the app looks like -* Automated checks - * We automatically run the [test suite](https://github.com/openSUSE/osem/blob/master/CONTRIBUTING.md#test-suite) and security checks on every PR - * Check back later to see if all checks were successful, if not, address them or leave a comment to ask for help -* Pushing new changes to your PR - * Always add **new** commits; this tremendously helps reviewers - * Do not squash commits, unless explicitly requested by the reviewer -* Take care of your PR - * Make sure you check the status of your PR regularly - * Address your reviews, make the necessary changes, ask if something is not clear to you - * Rebase against newest changes, when needed; we cannot properly review PRs that are not rebased - -Reviewing your PR might take some time, as we are all volunteers. Please be responsive and respectful. - ### Coding Style We are using [rubocop](https://github.com/bbatsov/rubocop) as a style checker. It is checking code style each time the test suite runs. You can run it locally with @@ -141,16 +88,6 @@ We are using [rspec](http://rspec.info/)+[capybara](http://jnicklas.github.io/ca vagrant exec bundle exec rspec ``` -### Review App of your PR - -OSEM uses [Review Apps](https://devcenter.heroku.com/articles/github-integration-review-apps) on Heroku. - -* The review app can be manually created by a maintainer, and when that happens you will see a relevant message in the PR - -* Please help reviewers by adding the necessary data relevant to your PR, -eg. if your PR is doing something related to conference registrations, go to the review app and make sure there is a conference with registrations. - - ### Email Notifications **Note**: We use [letter_opener](https://github.com/ryanb/letter_opener) in development environment. You can check out your mails by visiting [localhost:3000/letter_opener](http://localhost:3000/letter_opener). @@ -181,44 +118,6 @@ OSEM_GITHUB_SECRET='sample' If you don't already have a `.env` file you can use the `dotenv.example` as a template. -## Labels for issues and PRs -...and what they mean! - -1. **Bug** - * A bug in the application, something is wrong and needs to be fixed! - * Ideally the issue includes details on how to reproduce the bug - * Reproduce the bug in master branch, and send a PR that solves it -2. **Design** - * Related to the looks and/or usability of the application; needs attention from someone who understands front-end and UX - * If you are good with graphics and design, give it a shot! -3. **Documentation** - * Related to the documentation of our application, eg our INSTALL.md file or a wiki page with instructions on how to use the app, or part of it. - * If you are working on a documentation issue, make sure you are covering all cases. -4. **Epic** - * We may, or may not, solve this, thus it is epic. It's bigger than a feature request, because it fundamentally changes or affects the app, or a significant part of it. - * Do **not** work on this without prior discussion with the maintainers, it's called epic for a reason! -5. **Feature** - * This is a new feature for something new in the app! - * If an issue is labeled *Feature*, don't work on the issue, unless the maintainers have decided on how to proceed - * Ideally, leave a comment with your proposed solution in the issue and wait for feedback -6. **Grooming** - * This is working, but could look better, thus needs some attention and grooming. -7. **Hacktoberfest** - * This is for the issues included in the coding event of Hacktoberfest. You can ignore it, when the event is not on -8. **in progress** -9. **Junior** - * For new comers! RoR beginners or people unfamiliar with the application. Where you must start if you are interested in a mentoring program we participate in. -10. **need feedback** - * Maintainers' attention is needed to decide if this is something we want in the app, and/or how it should be implemented -11. **Operation** -12. **ready** -13. **Refactorization** - * Our code needs to be re-written; to avoid code duplication, or make the code more readable, or do things in a simpler way! -14. **Research** - * Ideas to explore; and think if there is anything we want to include in our app. -15. **GSoC** - * To group all the issues and PRs related to Google Summer of Code together. - ## Code of Conduct OSEM is part of the openSUSE project. We follow all the [openSUSE Guiding Principles!](http://en.opensuse.org/openSUSE:Guiding_principles) If you think someone doesn't do that, please let us know at maintainers@osem.io diff --git a/Gemfile b/Gemfile index 46c8731e..1b0a3c1d 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ if Gem::Version.new(Bundler::VERSION) < Gem::Version.new('1.8.4') end # as web framework -gem 'rails', '~> 4.2' +gem 'rails', '~> 4.2.8' # enables serving assets in production and setting your logger to standard out # both of which are required to run an application on a twelve-factor provider @@ -201,7 +201,7 @@ gem 'selectize-rails' # Use guard and spring for testing in development group :development do # to launch specs when files are modified - gem 'guard-rspec', '~> 4.2.8' + gem 'guard-rspec' gem 'spring-commands-rspec' gem 'haml_lint', '~> 0.24.0' # for static code analisys @@ -220,7 +220,7 @@ end group :test do # as test framework - gem 'rspec-rails' + gem 'rspec-rails', '~> 3.5', '>= 3.5.2' gem 'database_cleaner' gem 'capybara' gem 'poltergeist' diff --git a/Gemfile.lock b/Gemfile.lock index effbde5b..8f7ef5d6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,40 +12,39 @@ GEM remote: https://rubygems.org/ remote: https://rails-assets.org/ specs: - actionmailer (4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) + actionmailer (4.2.9) + actionpack (= 4.2.9) + actionview (= 4.2.9) + activejob (= 4.2.9) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.7.1) - actionview (= 4.2.7.1) - activesupport (= 4.2.7.1) + actionpack (4.2.9) + actionview (= 4.2.9) + activesupport (= 4.2.9) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.7.1) - activesupport (= 4.2.7.1) + actionview (4.2.9) + activesupport (= 4.2.9) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.2) + rails-html-sanitizer (~> 1.0, >= 1.0.3) active_model_serializers (0.9.4) activemodel (>= 3.2) - activejob (4.2.7.1) - activesupport (= 4.2.7.1) + activejob (4.2.9) + activesupport (= 4.2.9) globalid (>= 0.3.0) - activemodel (4.2.7.1) - activesupport (= 4.2.7.1) + activemodel (4.2.9) + activesupport (= 4.2.9) builder (~> 3.1) - activerecord (4.2.7.1) - activemodel (= 4.2.7.1) - activesupport (= 4.2.7.1) + activerecord (4.2.9) + activemodel (= 4.2.9) + activesupport (= 4.2.9) arel (~> 6.0) - activesupport (4.2.7.1) + activesupport (4.2.9) i18n (~> 0.7) - json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) @@ -58,7 +57,8 @@ GEM awesome_nested_set (>= 2.0) acts_as_list (0.4.0) activerecord (>= 3.0) - addressable (2.3.6) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) ahoy_matey (1.0.0) addressable browser (>= 0.4.0) @@ -67,7 +67,7 @@ GEM request_store user_agent_parser uuidtools - arel (6.0.3) + arel (6.0.4) ast (2.3.0) autoprefixer-rails (5.1.9) execjs @@ -88,7 +88,7 @@ GEM bootstrap3-datetimepicker-rails (3.0.3) momentjs-rails (>= 2.8.1) browser (0.6.0) - builder (3.2.2) + builder (3.2.3) byebug (9.0.6) cancancan (1.13.1) capybara (2.6.2) @@ -107,11 +107,6 @@ GEM activesupport (>= 3.2.0) carrierwave fastimage - celluloid (0.15.2) - timers (~> 1.1.0) - celluloid-io (0.15.0) - celluloid (>= 0.15.0) - nio4r (>= 0.5.0) chart-js-rails (0.0.6) railties (> 3.1) chronic (0.10.2) @@ -121,7 +116,7 @@ GEM aws_cf_signer rest-client cocoon (1.2.6) - coderay (1.1.0) + coderay (1.1.1) coffee-rails (4.1.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.1.x) @@ -143,7 +138,7 @@ GEM term-ansicolor (~> 1.3) thor (~> 0.19.1) tins (~> 1.6.0) - crack (0.4.2) + crack (0.4.3) safe_yaml (~> 1.0.0) currencies (0.4.2) daemons (1.1.9) @@ -163,7 +158,7 @@ GEM warden (~> 1.2.3) devise_ichain_authenticatable (0.3.1) devise (>= 2.2) - diff-lcs (1.2.5) + diff-lcs (1.3) docile (1.1.5) domain_name (0.5.20160310) unf (>= 0.0.5, < 1.0.0) @@ -184,27 +179,32 @@ GEM multipart-post (>= 1.2, < 3) fastimage (2.0.0) addressable (~> 2) - ffi (1.9.3) + ffi (1.9.18) font-awesome-rails (4.1.0.0) railties (>= 3.2, < 5.0) - formatador (0.2.4) + formatador (0.2.5) formtastic (3.1.3) actionpack (>= 3.2.13) formtastic-bootstrap (3.1.1) formtastic (>= 3.0) geocoder (1.2.2) - globalid (0.3.6) - activesupport (>= 4.1.0) + globalid (0.4.0) + activesupport (>= 4.2.0) gravtastic (3.2.6) - guard (2.6.0) + guard (2.14.1) formatador (>= 0.2.4) - listen (~> 2.7) + listen (>= 2.7, < 4.0) lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) pry (>= 0.9.12) + shellany (~> 0.0) thor (>= 0.18.1) - guard-rspec (4.2.8) + guard-compat (1.2.1) + guard-rspec (4.7.3) guard (~> 2.1) - rspec (>= 2.14, < 4.0) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) haml (4.0.5) tilt haml-rails (0.5.3) @@ -218,6 +218,7 @@ GEM rake (>= 10, < 13) rubocop (>= 0.47.0) sysexits (~> 1.1) + hashdiff (0.3.4) hashie (2.1.1) hike (1.2.3) hoptoad_notifier (2.4.11) @@ -228,8 +229,8 @@ GEM domain_name (~> 0.5) i18n (0.7.0) i18n_data (0.7.0) - inversion (0.12.3) - loggability (~> 0.4) + inversion (1.0.0) + loggability (~> 0.12) iso-639 (0.2.5) jquery-datatables-rails (2.2.3) jquery-rails @@ -252,26 +253,25 @@ GEM actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) - listen (2.7.2) - celluloid (>= 0.15.2) - celluloid-io (>= 0.15.0) - rb-fsevent (>= 0.9.3) - rb-inotify (>= 0.9) - loggability (0.11.0) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + loggability (0.14.0) loofah (2.0.3) nokogiri (>= 1.5.9) - lumberjack (1.0.5) - mail (2.6.3) - mime-types (>= 1.16, < 3) + lumberjack (1.0.12) + mail (2.6.6) + mime-types (>= 1.16, < 4) method_source (0.8.2) - mime-types (2.99.1) + mime-types (2.99.3) mimemagic (0.3.2) mina (0.3.8) open4 (~> 1.3.4) rake mini_magick (4.5.1) mini_portile2 (2.2.0) - minitest (5.10.1) + minitest (5.10.2) momentjs-rails (2.8.1) railties (>= 3.1) monetize (1.4.0) @@ -284,14 +284,17 @@ GEM monetize (~> 1.4.0) money (~> 6.7) railties (>= 3.0) - multi_json (1.11.2) + multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) mysql2 (0.4.2) + nenv (0.3.0) netrc (0.11.0) - nio4r (1.2.1) nokogiri (1.8.0) mini_portile2 (~> 2.2.0) + notiffany (0.1.1) + nenv (~> 0.1) + shellany (~> 0.0) oauth2 (0.9.4) faraday (>= 0.8, < 0.10) jwt (~> 1.0) @@ -347,26 +350,27 @@ GEM prawn_rails (0.0.11) prawn (>= 0.11.1) railties (>= 3.0.0) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - rack (1.6.4) + public_suffix (2.0.5) + rack (1.6.8) rack-openid (1.3.1) rack (>= 1.1.0) ruby-openid (>= 2.1.8) rack-test (0.6.3) rack (>= 1.0) - rails (4.2.7.1) - actionmailer (= 4.2.7.1) - actionpack (= 4.2.7.1) - actionview (= 4.2.7.1) - activejob (= 4.2.7.1) - activemodel (= 4.2.7.1) - activerecord (= 4.2.7.1) - activesupport (= 4.2.7.1) + rails (4.2.9) + actionmailer (= 4.2.9) + actionpack (= 4.2.9) + actionview (= 4.2.9) + activejob (= 4.2.9) + activemodel (= 4.2.9) + activerecord (= 4.2.9) + activesupport (= 4.2.9) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.7.1) + railties (= 4.2.9) sprockets-rails rails-assets-bootstrap (3.3.6) rails-assets-jquery (>= 1.9.1, < 3) @@ -401,24 +405,23 @@ GEM rails_stdout_logging rails_serve_static_assets (0.0.4) rails_stdout_logging (0.0.3) - railties (4.2.7.1) - actionpack (= 4.2.7.1) - activesupport (= 4.2.7.1) + railties (4.2.9) + actionpack (= 4.2.9) + activesupport (= 4.2.9) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake - rake (10.5.0) - rb-fsevent (0.9.4) - rb-inotify (0.9.4) - ffi (>= 0.5.0) - rdoc (4.1.1) - json (~> 1.4) - rdoc-generator-fivefish (0.1.0) - inversion (~> 0.12) - loggability (~> 0.6) - rdoc (~> 4.0) - yajl-ruby (~> 1.1) + rake (12.0.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + rdoc (5.1.0) + rdoc-generator-fivefish (0.3.0) + inversion (~> 1.0) + loggability (~> 0.12) + rdoc (~> 5.0) + yajl-ruby (~> 1.3) redcarpet (3.2.3) referer-parser (0.2.1) request_store (1.1.0) @@ -431,30 +434,31 @@ GEM rolify (5.1.0) rqrcode (0.10.1) chunky_png (~> 1.0) - rspec (3.0.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) rspec-activemodel-mocks (1.0.1) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.0.2) - rspec-support (~> 3.0.0) - rspec-expectations (3.0.2) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.0.0) - rspec-mocks (3.0.2) - rspec-support (~> 3.0.0) - rspec-rails (3.0.0) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-rails (3.6.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-support (~> 3.0.0) - rspec-support (3.0.2) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) rubocop (0.49.1) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) @@ -465,6 +469,7 @@ GEM ruby-oembed (0.8.14) ruby-openid (2.5.0) ruby-progressbar (1.8.1) + ruby_dep (1.5.0) rubyzip (1.2.1) safe_yaml (1.0.4) sass (3.2.19) @@ -474,6 +479,7 @@ GEM sprockets (~> 2.8, < 2.12) sprockets-rails (~> 2.0) selectize-rails (0.12.4) + shellany (0.0.1) shoulda-matchers (2.8.0) activesupport (>= 3.0.0) simplecov (0.11.2) @@ -507,11 +513,10 @@ GEM sysexits (1.2.0) term-ansicolor (1.3.2) tins (~> 1.0) - thor (0.19.1) + thor (0.19.4) thread_safe (0.3.6) tilt (1.4.1) timecop (0.7.1) - timers (1.1.0) tins (1.6.0) transitions (0.1.12) ttfunk (1.1.1) @@ -537,9 +542,10 @@ GEM binding_of_caller (>= 0.7.2) railties (>= 4.0) sprockets-rails (>= 2.0, < 4.0) - webmock (1.20.4) + webmock (3.0.1) addressable (>= 2.3.6) crack (>= 0.3.2) + hashdiff websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -548,7 +554,7 @@ GEM chronic (>= 0.6.3) xpath (2.0.0) nokogiri (~> 1.3) - yajl-ruby (1.2.0) + yajl-ruby (1.3.0) PLATFORMS ruby @@ -589,7 +595,7 @@ DEPENDENCIES formtastic (~> 3.1.1) formtastic-bootstrap gravtastic - guard-rspec (~> 4.2.8) + guard-rspec haml-rails haml_lint (~> 0.24.0) hoptoad_notifier (~> 2.3) @@ -616,7 +622,7 @@ DEPENDENCIES poltergeist prawn-qrcode (~> 0.2.2.1) prawn_rails - rails (~> 4.2) + rails (~> 4.2.8) rails-assets-bootstrap-markdown! rails-assets-date.format! rails-assets-holderjs! @@ -636,7 +642,7 @@ DEPENDENCIES rolify rqrcode rspec-activemodel-mocks - rspec-rails + rspec-rails (~> 3.5, >= 3.5.2) rubocop (~> 0.49.0) ruby-oembed sass-rails (>= 4.0.2) diff --git a/app/assets/stylesheets/osem-navbar.css.scss b/app/assets/stylesheets/osem-navbar.css.scss index 385cf93d..ef05c0a9 100644 --- a/app/assets/stylesheets/osem-navbar.css.scss +++ b/app/assets/stylesheets/osem-navbar.css.scss @@ -20,14 +20,6 @@ color: black; background-color: #eeeeee; } - input[type=text], - input[type=password] { - text-align: center; - padding: 5px; - display: block; - margin: auto; - width: 255px; - } .btn-group { width: 100%; text-align: center; diff --git a/app/assets/stylesheets/osem.css.scss b/app/assets/stylesheets/osem.css.scss index bc8b6388..5a331436 100644 --- a/app/assets/stylesheets/osem.css.scss +++ b/app/assets/stylesheets/osem.css.scss @@ -89,4 +89,8 @@ p.comment-body { .changeset{ display: none; -} \ No newline at end of file +} + +.box{ + height: 230px; +} diff --git a/app/controllers/admin/booths_controller.rb b/app/controllers/admin/booths_controller.rb new file mode 100644 index 00000000..f5096c34 --- /dev/null +++ b/app/controllers/admin/booths_controller.rb @@ -0,0 +1,94 @@ +module Admin + class BoothsController < Admin::BaseController + load_and_authorize_resource :conference, find_by: :short_title + load_and_authorize_resource through: :conference + + def index; end + + def show; end + + def new; end + + def create + @booth = @conference.booths.new(booth_params) + + @booth.submitter = current_user + + if @booth.save + redirect_to admin_conference_booths_path, + notice: 'Booth successfully created.' + else + flash[:error] = "Creating booth failed. #{@booth.errors.full_messages.to_sentence}." + render :new + end + end + + def edit; end + + def update + @booth.update_attributes(booth_params) + + if @booth.save + redirect_to admin_conference_booths_path, + notice: "Successfully updated booth for #{@booth.title}." + else + flash[:error] = "An error prohibited the Booth for #{@booth.title} "\ + "#{@booth.errors.full_messages.join('. ')}." + render :edit + end + end + + def destroy + if @booth.destroy + redirect_to admin_conference_booths_path, + notice: 'Booth successfully destroyed.' + else + redirect_to admin_conference_booths_path, + error: "Booth couldn't be deleted. #{@booth.errors.full_messages.join('. ')}." + end + end + + def accept + update_state(:accept, 'Booth accepted!') + end + + def to_accept + update_state(:to_accept, 'Booth to accept') + end + + def to_reject + update_state(:to_reject, 'Booth to reject') + end + + def reject + update_state(:reject, 'Booth rejected') + end + + def restart + update_state(:restart, 'Booth is submitted') + end + + def cancel + update_state(:cancel, 'Booth is canceled') + end + + private + + def update_state(transition, notice) + alert = @booth.update_state(transition, notice) + + if alert.blank? + flash[:notice] = notice + redirect_back_or_to(admin_conference_booths_path(conference_id: @conference.short_title)) && return + else + flash[:error] = alert + return redirect_back_or_to(admin_conference_booths_path(conference_id: @conference.short_title)) && return + end + end + + def booth_params + params.require(:booth).permit(:title, :description, :reasoning, :state, :picture, :conference_id, + :created_at, :updated_at, :submitter_relationship, :website_url, responsible_ids: []) + end + end +end diff --git a/app/controllers/physical_ticket_controller.rb b/app/controllers/physical_ticket_controller.rb index 8617e0d9..fb25ae63 100644 --- a/app/controllers/physical_ticket_controller.rb +++ b/app/controllers/physical_ticket_controller.rb @@ -1,7 +1,7 @@ class PhysicalTicketController < ApplicationController before_action :authenticate_user! load_resource :conference, find_by: :short_title - load_and_authorize_resource + load_and_authorize_resource find_by: :token authorize_resource :conference_registrations, class: Registration def index @@ -13,5 +13,15 @@ class PhysicalTicketController < ApplicationController @file_name = "ticket_for_#{@conference.short_title}" @user = @physical_ticket.user @ticket_layout = @conference.ticket_layout.to_sym + respond_to do |format| + format.html + format.pdf do + pdf = TicketPdf.new(@conference, @user, @physical_ticket, @ticket_layout, @file_name) + send_data pdf.render, + filename: @file_name, + type: 'application/pdf', + disposition: 'attachment' + end + end end end diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 351f9e9d..76d5c74a 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -5,17 +5,19 @@ class SubscriptionsController < ApplicationController def create @subscription = current_user.subscriptions.build(conference_id: @conference.id) - if @subscription.save! - redirect_to root_path, notice: "You have been subscribed to receive email notifications for #{@conference.short_title}." + if @subscription.save + redirect_to root_path, notice: "You have subscribed to receive email notifications for #{@conference.title}." else - redirect_to root_path, error: subscription.errors.full_messages.to_sentence + redirect_to root_path, error: @subscription.errors.full_messages.to_sentence end end def destroy @subscription = current_user.subscriptions.find_by(conference_id: @conference.id) + + redirect_to(root_path, error: "You are not subscribed to #{@conference.title}.") && return unless @subscription if @subscription.destroy - redirect_to root_path, notice: "You have been unsubscribed and now you will not be receiving email notifications for #{@conference.short_title}." + redirect_to root_path, notice: "You have unsubscribed and you will not be receiving email notifications for #{@conference.title}." else redirect_to root_path, error: @subscription.errors.full_messages.to_sentence end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index fe81dfad..0abf6088 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -162,6 +162,13 @@ module ApplicationHelper include_blank: false, label: 'Speakers', input_html: { class: 'select-help-toggle', multiple: 'true' } end + def responsibles_selector_input(form) + users = User.active.pluck(:id, :name, :username, :email).map { |user| [user[0], user[1].blank? ? user[2] : user[1], user[2], user[3]] }.sort_by { |user| user[1].downcase } + form.input :responsibles, as: :select, + collection: options_for_select(users.map {|user| ["#{user[1]} (#{user[2]}) #{user[3]}", user[0]]}, @booth.responsibles.map(&:id)), + include_blank: false, label: 'Responsibles', input_html: { class: 'select-help-toggle', multiple: 'true' } + end + def event_types(conference) conference.program.event_types.map { |et| et.title.pluralize }.to_sentence end diff --git a/app/mailers/mailbot.rb b/app/mailers/mailbot.rb index 3d70f18d..f5b7f375 100644 --- a/app/mailers/mailbot.rb +++ b/app/mailers/mailbot.rb @@ -8,6 +8,22 @@ class Mailbot < ActionMailer::Base conference.email_settings.registration_body)) end + def ticket_confirmation_mail(ticket_purchase) + @ticket_purchase = ticket_purchase + @conference = ticket_purchase.conference + @user = ticket_purchase.user + + PhysicalTicket.last(ticket_purchase.quantity).each do |physical_ticket| + pdf = TicketPdf.new(@conference, @user, physical_ticket, @conference.ticket_layout.to_sym, "ticket_for_#{@conference.short_title}_#{physical_ticket.id}") + attachments["ticket_for_#{@conference.short_title}_#{physical_ticket.id}"] = pdf.render + end + + mail(to: @user.email, + from: @conference.contact.email, + template_name: 'ticket_confirmation_template', + subject: "#{@conference.title} | Ticket Confirmation and PDF!") + end + def acceptance_mail(event) conference = event.program.conference diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb index b0366198..716aecb0 100644 --- a/app/models/admin_ability.rb +++ b/app/models/admin_ability.rb @@ -119,6 +119,7 @@ class AdminAbility commercialable_id: conf_ids can :manage, Registration, conference_id: conf_ids can :manage, RegistrationPeriod, conference_id: conf_ids + can :manage, Booth, conference_id: conf_ids can :manage, Question, conference_id: conf_ids can :manage, Question do |question| !(question.conferences.pluck(:id) & conf_ids).empty? @@ -169,6 +170,7 @@ class AdminAbility conf_ids_for_cfp.include?(conf.id) end can [:index, :show, :update], Resource, conference_id: conf_ids_for_cfp + can :manage, Booth, conference_id: conf_ids_for_cfp can :manage, Event, program: { conference_id: conf_ids_for_cfp } can :manage, EventType, program: { conference_id: conf_ids_for_cfp } can :manage, Track, program: { conference_id: conf_ids_for_cfp } diff --git a/app/models/booth.rb b/app/models/booth.rb new file mode 100644 index 00000000..5c20550a --- /dev/null +++ b/app/models/booth.rb @@ -0,0 +1,76 @@ +class Booth < ActiveRecord::Base + include ActiveRecord::Transitions + has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } + + belongs_to :conference + has_many :booth_requests, dependent: :destroy + has_many :users, through: :booth_requests + + has_one :submitter_booth_user, -> { where(role: 'submitter') }, class_name: 'BoothRequest' + has_one :submitter, through: :submitter_booth_user, source: :user + + has_many :responsibles_booth_user, -> { where(role: 'responsible') }, class_name: 'BoothRequest' + has_many :responsibles, through: :responsibles_booth_user, source: :user + + validates :title, + uniqueness: { case_sensitive: false }, + presence: true + + validates :description, + :reasoning, + :state, + :responsibles, + :conference_id, + :website_url, + :submitter_relationship, + presence: true + + mount_uploader :picture, PictureUploader, mount_on: :logo_link + + state_machine initial: :new do + state :new + state :withdrawn + state :to_accept + state :accepted + state :to_reject + state :rejected + state :canceled + + event :restart do + transitions to: :new, from: [:withdrawn, :to_accept, :to_reject, :canceled] + end + event :withdraw do + transitions to: :withdrawn, from: [:new, :to_accept, :accepted, :to_reject, :rejected] + end + event :to_accept do + transitions to: :to_accept, from: [:new, :to_reject] + end + event :to_reject do + transitions to: :to_reject, from: [:new, :to_accept] + end + event :accept do + transitions to: :accepted, from: [:new, :to_accept] + end + event :reject do + transitions to: :rejected, from: [:new, :to_reject] + end + event :cancel do + transitions to: :canceled, from: [:accepted, :rejected, :to_accept, :to_reject] + end + end + + def transition_possible?(transition) + self.class.state_machine.events_for(current_state).include?(transition) + end + + def update_state(transition, _notice) + alert = '' + begin + send(transition) + save + rescue Transitions::InvalidTransition => e + alert = "Update state failed. #{e.message}" + end + alert + end +end diff --git a/app/models/booth_request.rb b/app/models/booth_request.rb new file mode 100644 index 00000000..438b4167 --- /dev/null +++ b/app/models/booth_request.rb @@ -0,0 +1,9 @@ +class BoothRequest < ActiveRecord::Base + belongs_to :booth + belongs_to :user + + validates :role, + presence: true + + ROLES = %w[submitter responsible].freeze +end diff --git a/app/models/conference.rb b/app/models/conference.rb index db6b4745..b528d755 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -28,6 +28,7 @@ class Conference < ActiveRecord::Base has_many :supporters, through: :ticket_purchases, source: :user has_many :tickets, dependent: :destroy has_many :resources, dependent: :destroy + has_many :booths, dependent: :destroy has_many :lodgings, dependent: :destroy has_many :registrations, dependent: :destroy diff --git a/app/models/physical_ticket.rb b/app/models/physical_ticket.rb index 6142c875..689d118a 100644 --- a/app/models/physical_ticket.rb +++ b/app/models/physical_ticket.rb @@ -4,4 +4,19 @@ class PhysicalTicket < ActiveRecord::Base has_one :conference, through: :ticket_purchase has_one :user, through: :ticket_purchase has_many :ticket_scannings + + before_create :set_token + + private + + def set_token + self.token = generate_token + end + + def generate_token + loop do + token = SecureRandom.hex(10) + break token unless PhysicalTicket.exists?(token: token) + end + end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index aa688ae8..984c46f3 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,5 +1,4 @@ class Subscription < ActiveRecord::Base - validates :user_id, uniqueness: { scope: [:conference_id] } belongs_to :conference belongs_to :user diff --git a/app/models/ticket_purchase.rb b/app/models/ticket_purchase.rb index a19678a7..a2ed2747 100644 --- a/app/models/ticket_purchase.rb +++ b/app/models/ticket_purchase.rb @@ -69,6 +69,7 @@ class TicketPurchase < ActiveRecord::Base PhysicalTicket.transaction do quantity.times { physical_tickets.create } end + Mailbot.ticket_confirmation_mail(self).deliver_later end end diff --git a/app/models/user.rb b/app/models/user.rb index 54a9deba..f3f03ab7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -56,6 +56,9 @@ class User < ActiveRecord::Base has_many :voted_events, through: :votes, source: :events has_many :subscriptions, dependent: :destroy has_many :tracks, foreign_key: 'submitter_id' + has_many :booth_requests + has_many :booth_requests, dependent: :destroy + has_many :booths, through: :booth_requests accepts_nested_attributes_for :roles scope :admin, -> { where(is_admin: true) } diff --git a/app/pdfs/ticket_pdf.rb b/app/pdfs/ticket_pdf.rb new file mode 100644 index 00000000..1c26ec77 --- /dev/null +++ b/app/pdfs/ticket_pdf.rb @@ -0,0 +1,81 @@ +class TicketPdf < Prawn::Document + def initialize(conference, user, physical_ticket, ticket_layout, file_name) + super(page_layout: ticket_layout, page_size: 'A4', filename: file_name) + @user = user + @physical_ticket = physical_ticket + @conference = conference + + @left = bounds.left + @right = bounds.right + @mid_vertical = (bounds.top - bounds.bottom) / 2 + @mid_horizontal = (bounds.right - bounds.left) / 2 + @x = 0 + + draw_first_square + draw_second_square + draw_third_square + draw_fourth_square + end + + def draw_first_square + move_down @mid_vertical + dash(2, space: 1) + stroke_horizontal_rule + stroke_vertical_line bounds.top, bounds.bottom, at: @mid_horizontal + move_up @mid_vertical + draw_text 'TICKET HOLDER', at: [@x, cursor - 30], size: 17 + dash(2, space: 0) + stroke_rectangle [@x, cursor - 50], 230, 150 + move_down 80 + draw_text 'NAME', at: [@x + 10, cursor], size: 13 + fill_color '808080' + draw_text @user.name.to_s, at: [@x + 10, cursor - 25], size: 20 + fill_color '000000' + draw_text 'EMAIL', at: [@x + 10, cursor - 50], size: 13 + fill_color '808080' + draw_text @user.email.to_s, at: [@x + 10, cursor - 75], size: 20 + fill_color '000000' + move_up 20 + end + + def draw_second_square + if @conference.picture? + if 7 * @conference.picture.image[:width] > 12 * @conference.picture.image[:height] + image "#{Rails.root}/public#{@conference.picture_url}", at: [@mid_horizontal + 30, cursor], width: 120 + else + image "#{Rails.root}/public#{@conference.picture_url}", at: [@mid_horizontal + 30, cursor], height: 70 + end + else + image "#{Rails.root}/public/img/osem-logo.png", at: [@mid_horizontal + 30, cursor], height: 70 + end + move_down 70 + draw_text @conference.title.to_s, at: [@mid_horizontal + 30, cursor - 30], size: 12 + draw_text @conference.organization.name.to_s, at: [@mid_horizontal + 30, cursor - 50], size: 12 + draw_text @conference.venue.name.to_s, at: [@mid_horizontal + 30, cursor - 70] + move_up 130 + move_down @mid_vertical + end + + def draw_third_square + draw_text 'EVENT', at: [@x, cursor - 40], size: 15 + fill_color '808080' + draw_text @conference.title.to_s, at: [@x, cursor - 60], size: 12 + draw_text @conference.start_date.strftime('%B %d, %Y').to_s, at: [@x, cursor - 80], size: 12 + move_down 80 + fill_color '000000' + draw_text 'TICKET', at: [@x, cursor - 30], size: 15 + fill_color '808080' + draw_text @physical_ticket.ticket.title.to_s, at: [@x, cursor - 50], size: 12 + move_down 50 + fill_color '000000' + draw_text 'TICKET REF.', at: [@x, cursor - 30], size: 15 + fill_color '808080' + draw_text @physical_ticket.ticket_purchase.id.to_s, at: [@x, cursor - 50], size: 12 + move_down 50 + fill_color '000000' + draw_text 'Powered By OSEM', at: [(@mid_horizontal - @left - 100) / 2, cursor - 100], size: 11 + move_up 180 + end + + def draw_fourth_square; end +end diff --git a/app/views/admin/booths/_change_state_dropdown.html.haml b/app/views/admin/booths/_change_state_dropdown.html.haml new file mode 100644 index 00000000..c0418d67 --- /dev/null +++ b/app/views/admin/booths/_change_state_dropdown.html.haml @@ -0,0 +1,29 @@ +- if booth.transition_possible? :accept + %li= link_to 'Accept booth', + accept_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "accept_booth_#{booth.id}" + +- if booth.transition_possible? :reject + %li= link_to 'Reject booth', + reject_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, confirm: 'Are you sure?', id: "reject_booth_#{booth.id}" + +- if booth.transition_possible? :to_reject + %li= link_to 'To reject booth', + to_reject_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, confirm: 'Are you sure?', id: "to_reject_booth_#{booth.id}" + +- if booth.transition_possible? :restart + %li= link_to 'Start review', + restart_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "restart_booth_#{booth.id}" + +- if booth.transition_possible? :to_accept + %li= link_to 'To accept booth', + to_accept_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "to_accept_booth_#{booth.id}" + +- if booth.transition_possible? :cancel + %li= link_to 'Cancel booth', + cancel_admin_conference_booth_path(@conference.short_title, booth), + method: :patch, id: "cancel_booth_#{booth.id}" diff --git a/app/views/admin/booths/_form.html.haml b/app/views/admin/booths/_form.html.haml new file mode 100644 index 00000000..f70079fa --- /dev/null +++ b/app/views/admin/booths/_form.html.haml @@ -0,0 +1,33 @@ +.row + .col-md-12 + .page-header + %title Request a Booth +.row + .col-md-8 + = semantic_form_for(@booth, url: @booth.new_record? ? admin_conference_booths_path(@conference.short_title) : admin_conference_booth_path(@conference.short_title, @booth.id), html: { multipart: true }) do |f| + = f.input :title, as: :string, autofocus: true, required: true + = f.input :description, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + hint: 'This field becomes public upon request acceptance' + = f.input :reasoning, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'How it fits the conference' + = f.input :submitter_relationship, input_html: { rows: 5, data: { provide: 'markdown-editable' } }, required: true, + label: 'Submitter\'s relation', + hint: 'e.g. employee, comunity manager, etc' + = f.input :website_url + = responsibles_selector_input f + = image_tag f.object.picture.thumb.url if f.object.picture? + = f.input :picture + + %p.text-right + - if @booth.new_record? + = f.submit 'Create Booth Request', class: 'btn btn-success' + - else + = f.submit 'Update Booth Request', class: 'btn btn-success' + +:javascript + $(document).ready(function() { + $('#booth_responsible_ids').selectize({ + plugins: ['remove_button'], + minItems: 2 + } ) + }); diff --git a/app/views/admin/booths/edit.html.haml b/app/views/admin/booths/edit.html.haml new file mode 100644 index 00000000..9f9a5a61 --- /dev/null +++ b/app/views/admin/booths/edit.html.haml @@ -0,0 +1,5 @@ +%h1 + Editing + = @booth.title + += render 'form' diff --git a/app/views/admin/booths/index.html.haml b/app/views/admin/booths/index.html.haml new file mode 100644 index 00000000..15833bd7 --- /dev/null +++ b/app/views/admin/booths/index.html.haml @@ -0,0 +1,59 @@ +.row + .col-md-12 + .page-header + %h1 + Booths + = "(#{@booths.length})" if @booths.any? + .pull-right + - if can? :create, Booth + = link_to 'Add Booth', new_admin_conference_booth_path(@conference.short_title), class: 'button btn btn-primary' + %p.text-muted + All the booth requests +.row + .col-md-12 + .margin-booth-table + %table.table.table-striped.table-bordered.table-hover.datatable + %thead + %th + %b ID + %th + %b Logo + %th + %b Title + %th + %b Submitter + %th + %b Responsibles + %th + %b State + %th + %b Actions + - @booths.each do |booth| + %tr + %td + = booth.id + %td + - if booth.logo_link + = image_tag(booth.picture.thumb.url, width: '20%') + %td + = link_to booth.title, admin_conference_booth_path(@conference.short_title, booth) + %td + = link_to booth.submitter.name, admin_user_path(booth.submitter) if booth.submitter + %td + .responsibles + - booth.responsibles.each do |responsible| + = link_to responsible.name, admin_user_path(responsible) + %td + .btn-group + %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } + = booth.state.humanize + %span.caret + %ul.dropdown-menu{ role: 'menu' } + = render 'change_state_dropdown', booth: booth + %td + .btn-group{ role: "group" } + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, booth.id), + class: 'btn btn-primary' + = link_to 'Delete', admin_conference_booth_path(@conference.short_title, booth.id), + method: :delete, class: 'btn btn-danger', + data: {confirm: "Do you really want to delete this booth request?"} diff --git a/app/views/admin/booths/new.html.haml b/app/views/admin/booths/new.html.haml new file mode 100644 index 00000000..db1c4736 --- /dev/null +++ b/app/views/admin/booths/new.html.haml @@ -0,0 +1,3 @@ +%h1 New booth + += render 'form' diff --git a/app/views/admin/booths/show.html.haml b/app/views/admin/booths/show.html.haml new file mode 100644 index 00000000..8c692bc4 --- /dev/null +++ b/app/views/admin/booths/show.html.haml @@ -0,0 +1,57 @@ +.row + .col-md-12 + %h3 + = image_tag(@booth.picture.thumb.url, size: '20%', alt: '') + = @booth.title + .btn-group.pull-right + = link_to 'Edit', edit_admin_conference_booth_path(@conference.short_title, @booth), class: 'btn btn-mini btn-primary' + +.row + .col-md-12 + %table.table + %tr + %td.col-md-2 + %b Description + %td + = markdown(@booth.description) + %tr + %td.col-md-2 + %b Reasoning + %td + = markdown(@booth.reasoning) + %tr + %td.col-md-2 + %b Website + %td + - if @booth.website_url.present? + = link_to @booth.website_url, @booth.website_url + %tr + %td.col-md-2 + %b Submitter + %td + = link_to @booth.submitter.name, admin_user_path(@booth.submitter) + %tr + %td.col-md-2 + %b Submitter's relationship + %td + = @booth.submitter_relationship + %tr + %td.col-md-2 + %b Responsibles + %td + - @booth.responsibles.each do |responsibles| + .responsibles + = link_to responsibles.name, admin_user_path(responsibles) + ( + = responsibles.email + ) + %tr + %td.col-md-2 + %b Submitted on + %td + = @booth.created_at + %tr + %td.col-md-2 + %b Last updated on + %td + = @booth.updated_at diff --git a/app/views/admin/conferences/_doughnut_chart.html.haml b/app/views/admin/conferences/_doughnut_chart.html.haml index c8ef55fc..f4a8f0ba 100644 --- a/app/views/admin/conferences/_doughnut_chart.html.haml +++ b/app/views/admin/conferences/_doughnut_chart.html.haml @@ -1,6 +1,23 @@ .text-center %h4 #{title} - %canvas.doughnut_chart{ 'data-chart' => data.to_json } + %canvas.doughnut_chart{ id: "dough_#{title}", 'data-chart' => data.to_json } - if data - data.each do |key, value| %span{ 'style' => "border-bottom: 3px solid #{value['color']}" } #{key}: #{value['value']} + +:javascript + $(document).ready( function(){ + + var d = $("#dough_#{title}"); + var dt = d.get(0).getContext('2d'); + + $(window).resize( respondCanvas ); + + function respondCanvas(){ + dt.canvas.width = 150; + dt.canvas.height = 150; + } + + respondCanvas(); + + }); diff --git a/app/views/admin/conferences/_line_chart.html.haml b/app/views/admin/conferences/_line_chart.html.haml index cd49df10..12d825e7 100644 --- a/app/views/admin/conferences/_line_chart.html.haml +++ b/app/views/admin/conferences/_line_chart.html.haml @@ -27,3 +27,21 @@ %span{ 'style' => "border-bottom: 3px solid #{conference[:color]};", 'data-chart' => "#{name}" } %input{ 'type' => 'checkbox', 'name' => "#{conference[:short_title]}" } #{conference[:short_title]} + +:javascript + $(document).ready( function(){ + + var c = $("#line_chart_#{name}"); + var ct = c.get(0).getContext('2d'); + var container = $(c).parent(); + + $(window).resize( respondCanvas ); + + function respondCanvas(){ + c.attr('width', $(container).width() ); + c.attr('height', $(container).height() ); + } + + respondCanvas(); + + }); diff --git a/app/views/admin/physical_ticket/index.html.haml b/app/views/admin/physical_ticket/index.html.haml index fe128932..96c5911f 100644 --- a/app/views/admin/physical_ticket/index.html.haml +++ b/app/views/admin/physical_ticket/index.html.haml @@ -32,12 +32,12 @@ .btn-group = link_to 'Show', conference_physical_ticket_path(@conference.short_title, - physical_ticket.id), + physical_ticket.token), class: 'btn btn-primary' = link_to 'Generate PDF', conference_physical_ticket_path(@conference.short_title, - physical_ticket.id, - format: :pdf), + physical_ticket.token, + format: :pdf), class: 'button btn btn-default btn-info' - else %h5 No Tickets sold! diff --git a/app/views/admin/versions/_object_desc_and_link.html.haml b/app/views/admin/versions/_object_desc_and_link.html.haml index 83542ef1..d29e5f9b 100644 --- a/app/views/admin/versions/_object_desc_and_link.html.haml +++ b/app/views/admin/versions/_object_desc_and_link.html.haml @@ -80,6 +80,12 @@ = link_if_alive version, 'contact details', edit_admin_conference_contact_path(conference_id: Conference.find(version.conference_id).short_title) +- when 'Booth' + = 'booth' + - booth = current_or_last_object_state(version.item_type, version.item_id) + = link_if_alive version, booth.title, + admin_conference_booth_path(conference_id: Conference.find(version.conference_id).short_title, id: version.item_id ) + - when 'Program' = link_if_alive version, 'program', admin_conference_program_path(conference_id: Conference.find(version.conference_id).short_title) diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index 48066cd7..6683ea23 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -60,13 +60,13 @@ .dropdown-menu - if ENV['OSEM_ICHAIN_ENABLED'] == 'true' = form_tag User.ichain_login_url do - = text_field_tag 'username', nil, id: 'user_ichain_email_dd', placeholder: 'Username' - = password_field_tag 'password', nil, id: 'user_ichain_password_dd', placeholder: 'Password' + = text_field_tag 'username', nil, id: 'user_ichain_email_dd', class: 'form-control', placeholder: 'Username' + = password_field_tag 'password', nil, id: 'user_ichain_password_dd', class: 'form-control', placeholder: 'Password' %button.btn.btn-success.btn-block Sign in - else = form_tag new_user_session_path do - = text_field_tag 'user[login]', nil, id: 'user_login_dd', placeholder: 'Username / E-Mail' - = password_field_tag 'user[password]', nil, id: 'user_password_dd', placeholder: 'Password' + = text_field_tag 'user[login]', nil, id: 'user_login_dd', class: 'form-control', placeholder: 'Username / E-Mail' + = password_field_tag 'user[password]', nil, id: 'user_password_dd', class: 'form-control', placeholder: 'Password' %p.text-right %small %label{for: 'user_remember_me'} Remember me diff --git a/app/views/mailbot/ticket_confirmation_template.text.erb b/app/views/mailbot/ticket_confirmation_template.text.erb new file mode 100644 index 00000000..08f5175a --- /dev/null +++ b/app/views/mailbot/ticket_confirmation_template.text.erb @@ -0,0 +1,8 @@ +Dear <%= @user.name %>, + +Thanks! You have successfully booked <%= @ticket_purchase.quantity %> <%= @ticket_purchase.ticket.title %> ticket(s) for the event <%= @conference.title %>. Your transaction id is <%= @ticket_purchase.id %>. + +Please, find the ticket(s) pdf attached. + +Best wishes, +<%= @conference.title %> Team diff --git a/app/views/physical_ticket/index.html.haml b/app/views/physical_ticket/index.html.haml index b077400b..cb0710ad 100644 --- a/app/views/physical_ticket/index.html.haml +++ b/app/views/physical_ticket/index.html.haml @@ -24,11 +24,11 @@ .btn-group = link_to 'Show', conference_physical_ticket_path(@conference.short_title, - physical_ticket.id), + physical_ticket.token), class: 'btn btn-primary' = link_to 'Generate PDF', conference_physical_ticket_path(@conference.short_title, - physical_ticket.id, + physical_ticket.token, format: :pdf), class: 'button btn btn-default btn-info' - else diff --git a/app/views/physical_ticket/show.html.haml b/app/views/physical_ticket/show.html.haml index e69de29b..067f0a9b 100644 --- a/app/views/physical_ticket/show.html.haml +++ b/app/views/physical_ticket/show.html.haml @@ -0,0 +1,77 @@ +.container + .row + .col-md-12 + .page-header + %h1 + Ticket for + = @conference.title + %p.text-muted + - if @conference.venue + at + %strong + #{@conference.venue.name}, + #{@conference.venue.street}, + #{@conference.venue.city} / #{@conference.venue.country_name}. + %small + = date_string(@conference.start_date, @conference.end_date) + .row + .col-md-5.box.well + %h3.text-center + Ticket Holder + %p.text-left + %strong + Name + %br + = @user.name + %br + %br + %strong + Email + %br + = @user.email + .col-md-5.col-md-offset-2.box.well + - if @conference.picture? + - width = @conference.picture.image[:width] + - height = @conference.picture.image[:height] + - if 10 * width > 15 * height + = image_tag(@conference.picture_url, width: '150') + - else + = image_tag(@conference.picture_url, height: '100') + - else + = image_tag('/img/osem-logo.png', class: 'img-responsive') + %p.text-left + %br + %strong + Organization + %br + = @conference.organization.name + .col-md-5.box.well + %p.text-left + %strong + Event + %br + = @conference.title + %br + = @conference.start_date.strftime('%B %d, %Y') + %br + %br + %strong + Ticket + %br + = @physical_ticket.ticket.title + %br + %br + %strong + Ticket Ref. + %br + = @physical_ticket.ticket_purchase.id + %br + .col-md-5.col-md-offset-2.box.well + .row + .col-md-12 + %p.text-left + = link_to 'Generate PDF', + conference_physical_ticket_path(@conference.short_title, + @physical_ticket.token, + format: :pdf), + class: 'button btn btn-default btn-info' diff --git a/app/views/physical_ticket/show.pdf.prawn b/app/views/physical_ticket/show.pdf.prawn deleted file mode 100644 index 92206baf..00000000 --- a/app/views/physical_ticket/show.pdf.prawn +++ /dev/null @@ -1,62 +0,0 @@ -prawn_document(filename: @file_name, page_layout: @ticket_layout, :page_size =>'A4' ) do |pdf| - # Vertical Layout - top = pdf.bounds.top - bottom = pdf.bounds.bottom - left = pdf.bounds.left - right = pdf.bounds.right - mid_vertical = (pdf.bounds.top-pdf.bounds.bottom)/2 - mid_horizontal = (pdf.bounds.right-pdf.bounds.left)/2 - x = 0 - - pdf.move_down mid_vertical - pdf.dash(2, :space => 1) - pdf.stroke_horizontal_rule - pdf.stroke_vertical_line pdf.bounds.top, pdf.bounds.bottom, :at => mid_horizontal - pdf.move_up mid_vertical - pdf.draw_text "TICKET HOLDER", :at => [x,pdf.cursor-30], :size => 17 - pdf.dash(2, :space => 0) - pdf.stroke_rectangle [x, pdf.cursor-50], 230, 150 - pdf.move_down 80 - pdf.draw_text "NAME", :at => [x+10,pdf.cursor], :size => 13 - pdf.fill_color "808080" - pdf.draw_text "#{@user.name}", :at => [x+10,pdf.cursor-25], size: 20 - pdf.fill_color "000000" - pdf.draw_text "EMAIL", :at => [x+10,pdf.cursor-50], :size => 13 - pdf.fill_color "808080" - pdf.draw_text "#{@user.email}", :at => [x+10,pdf.cursor-75], size: 20 - pdf.fill_color "000000" - pdf.move_up 20 - if @conference.picture? - if 7 * @conference.picture.image[:width] > 12 * @conference.picture.image[:height] - pdf.image "#{Rails.root}/public#{@conference.picture_url}", :at => [mid_horizontal+30, pdf.cursor], :width => 120 - else - pdf.image "#{Rails.root}/public#{@conference.picture_url}", :at => [mid_horizontal+30, pdf.cursor], :height => 70 - end - else - pdf.image "#{Rails.root}/public/img/osem-logo.png", :at => [mid_horizontal+30, pdf.cursor], :height => 70 - end - pdf.move_down 70 - pdf.draw_text "#{@conference.title}", :at => [mid_horizontal+30,pdf.cursor-30], :size => 12 - pdf.draw_text "#{@conference.organization.name}", :at => [mid_horizontal+30,pdf.cursor-50], :size => 12 - pdf.draw_text "#{@conference.venue.name}", :at => [mid_horizontal+30,pdf.cursor-70] - pdf.move_up 130 - pdf.move_down mid_vertical - pdf.draw_text "EVENT", :at => [x,pdf.cursor-40], :size => 15 - pdf.fill_color "808080" - pdf.draw_text "#{@conference.title}", :at => [x,pdf.cursor-60], size: 12 - pdf.draw_text "#{@conference.start_date.strftime('%B %d, %Y')}", :at => [x,pdf.cursor-80], size: 12 - pdf.move_down 80 - pdf.fill_color "000000" - pdf.draw_text "TICKET", :at => [x,pdf.cursor-30], :size => 15 - pdf.fill_color "808080" - pdf.draw_text "#{@physical_ticket.ticket.title}", :at => [x,pdf.cursor-50], size: 12 - pdf.move_down 50 - pdf.fill_color "000000" - pdf.draw_text "TICKET REF.", :at => [x,pdf.cursor-30], :size => 15 - pdf.fill_color "808080" - pdf.draw_text "#{@physical_ticket.ticket_purchase.id}", :at => [x,pdf.cursor-50], size: 12 - pdf.move_down 50 - pdf.fill_color "000000" - pdf.draw_text "Powered By OSEM", :at => [(mid_horizontal-left-100)/2,pdf.cursor-100], :size => 11 - pdf.move_up 180 -end diff --git a/bootstrap.sh b/bootstrap.sh index 197b6be2..f0ac49fb 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -2,7 +2,7 @@ pushd /vagrant echo -e "\ninstalling required software packages...\n" -zypper -q -n install update-alternatives ruby2.2-devel make gcc gcc-c++ \ +zypper -q -n install update-alternatives ruby2.4-devel make gcc gcc-c++ \ libxml2-devel libxslt-devel nodejs screen mariadb \ libmysqld-devel sqlite3-devel ImageMagick @@ -10,7 +10,7 @@ echo -e "\ndisabling versioned gem binary names...\n" echo 'install: --no-format-executable' >> /etc/gemrc echo -e "\ninstalling bundler...\n" -gem.ruby2.2 install bundler +gem.ruby2.4 install bundler echo -e "\ninstalling your bundle...\n" su - vagrant -c "cd /vagrant/; bundle install --quiet" diff --git a/config/routes.rb b/config/routes.rb index 1643fc6b..5a17d314 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,19 @@ Osem::Application.routes.draw do get '/volunteers' => 'volunteers#index', as: 'volunteers_info' patch '/volunteers' => 'volunteers#update', as: 'volunteers_update' + resources :booths do + member do + patch :accept + patch :restart + patch :withdrawn + patch :to_accept + patch :reject + patch :reset + patch :to_reject + patch :cancel + end + end + resources :registrations, except: [:create, :new] do member do patch :toggle_attendance diff --git a/db/migrate/20170516190048_create_booths.rb b/db/migrate/20170516190048_create_booths.rb new file mode 100644 index 00000000..d2aa0be6 --- /dev/null +++ b/db/migrate/20170516190048_create_booths.rb @@ -0,0 +1,16 @@ +class CreateBooths < ActiveRecord::Migration + def change + create_table :booths do |t| + t.string :title + t.text :description + t.text :reasoning + t.string :state + t.string :logo_link + t.string :website_url + t.text :submitter_relationship + t.references :conference + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170530112510_create_booth_requests.rb b/db/migrate/20170530112510_create_booth_requests.rb new file mode 100644 index 00000000..75e73a93 --- /dev/null +++ b/db/migrate/20170530112510_create_booth_requests.rb @@ -0,0 +1,11 @@ +class CreateBoothRequests < ActiveRecord::Migration + def change + create_table :booth_requests do |t| + t.references :booth, index: true, foreign_key: true + t.references :user, index: true, foreign_key: true + t.string :role + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170721001700_add_index_to_physical_tickets.rb b/db/migrate/20170721001700_add_index_to_physical_tickets.rb new file mode 100644 index 00000000..46a0b8c0 --- /dev/null +++ b/db/migrate/20170721001700_add_index_to_physical_tickets.rb @@ -0,0 +1,6 @@ +class AddIndexToPhysicalTickets < ActiveRecord::Migration + def change + add_column :physical_tickets, :token, :string + add_index :physical_tickets, :token, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index f1a213a5..d49dfa03 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,10 +11,10 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170711102511) do +ActiveRecord::Schema.define(version: 20170721001700) do create_table "ahoy_events", force: :cascade do |t| - t.uuid "visit_id", limit: 16 + t.integer "visit_id" t.integer "user_id" t.string "name" t.text "properties" @@ -31,6 +31,30 @@ ActiveRecord::Schema.define(version: 20170711102511) do t.datetime "updated_at" end + create_table "booth_requests", force: :cascade do |t| + t.integer "booth_id" + t.integer "user_id" + t.string "role" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "booth_requests", ["booth_id"], name: "index_booth_requests_on_booth_id" + add_index "booth_requests", ["user_id"], name: "index_booth_requests_on_user_id" + + create_table "booths", force: :cascade do |t| + t.string "title" + t.text "description" + t.text "reasoning" + t.string "state" + t.string "logo_link" + t.string "website_url" + t.text "submitter_relationship" + t.integer "conference_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "campaigns", force: :cascade do |t| t.integer "conference_id" t.string "name" @@ -293,8 +317,11 @@ ActiveRecord::Schema.define(version: 20170711102511) do t.integer "ticket_purchase_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "token" end + add_index "physical_tickets", ["token"], name: "index_physical_tickets_on_token", unique: true + create_table "programs", force: :cascade do |t| t.integer "conference_id" t.integer "rating", default: 0 diff --git a/spec/controllers/admin/booths_controller_spec.rb b/spec/controllers/admin/booths_controller_spec.rb new file mode 100644 index 00000000..569862d1 --- /dev/null +++ b/spec/controllers/admin/booths_controller_spec.rb @@ -0,0 +1,148 @@ +require 'spec_helper' + +describe Admin::BoothsController do + + let(:admin) { create(:admin) } + let(:conference) { create(:conference) } + let(:booth) { create(:booth, title: 'Title', conference: conference) } + let(:admin) { create(:admin) } + + context 'not logged in user' do + + describe 'GET index' do + it 'does not render admin/booths#index' do + get :index, conference_id: conference.short_title + expect(response).to redirect_to(user_session_path) + end + end + + describe 'GET show' do + it 'does not render admin/booths#show' do + get :show, id: booth.id, conference_id: conference.short_title + expect(response).to redirect_to(user_session_path) + end + end + end + + context 'user is admin' do + before :each do + sign_in admin + end + + describe 'GET index' do + before { get :index, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booths)).to eq([booth]) + end + + it 'renders index template' do + expect(response).to render_template('index') + end + end + + describe 'GET new' do + before { get :new, conference_id: conference.short_title } + + it 'assigns attributes for booths' do + expect(assigns(:booth)).to be_a_new(Booth) + end + + it 'renders new template' do + expect(response).to render_template('new') + end + end + + describe 'POST #create' do + context 'successfully created' do + before { post :create, booth: attributes_for(:booth), conference_id: conference.short_title } + + it 'creates a new booth' do + expected = expect do + post :create, booth: attributes_for(:booth), conference_id: conference.short_title + end + expected.to change { Booth.count }.by(1) + end + + it 'redirects to admin booth index' do + expect(response).to redirect_to(admin_conference_booths_path) + end + + it 'has responsibles' do + expect(booth.responsibles.count).to_not eq(0) + end + + it 'shows success message' do + expect(flash[:notice]).to match('Booth successfully created.') + end + end + + context 'create action fails' do + before { post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title } + + it 'does not create any record' do + expected = expect do + post :create, booth: attributes_for(:booth, title: ''), conference_id: conference.short_title + end + expected.to_not change(Booth, :count) + end + + it 'redirects to new' do + expect(response).to render_template('new') + end + + it 'shows flash message' do + expect(flash[:error]).to eq("Creating booth failed. Title can't be blank.") + end + end + end + + describe 'GET #edit' do + before { get :edit, id: booth.id, conference_id: conference.short_title } + + it 'renders edit template' do + expect(response).to render_template('edit') + end + + it 'assigns booth variable' do + expect(assigns(:booth)).to eq booth + end + end + + describe 'PATCH #update' do + context 'updates suchessfully' do + before { patch :update, id: booth.id, booth: attributes_for(:booth, title: 'different'), conference_id: conference.short_title } + it 'redirects to admin booth index path' do + expect(response).to redirect_to admin_conference_booths_path + end + + it 'shows success message' do + expect(flash[:notice]).to match 'Successfully updated booth.' + end + + it 'updates booth' do + booth.reload + expect(booth.title).to eq('different') + end + end + end + + describe 'DELETE #destroy' do + context 'deletes successfully' do + before { delete :destroy, id: booth.id, conference_id: conference.short_title } + + it 'booth deleted' do + expect(Booth.count).to eq(0) + end + + it 'redirects to admin booth index path' do + expect(response).to redirect_to(admin_conference_booths_path) + end + + it 'show success message' do + expect(flash[:notice]).to match('Booth successfully destroyed.') + end + end + end + end +end diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb index 964346ef..d5cdc67e 100644 --- a/spec/controllers/admin/roles_controller_spec.rb +++ b/spec/controllers/admin/roles_controller_spec.rb @@ -55,7 +55,7 @@ describe Admin::RolesController do end describe 'POST #toggle' do - before:each do + before :each do sign_in admin post :toggle_user, conference_id: conference.short_title, user: { email: 'user1@osem.io' }, diff --git a/spec/controllers/physical_ticket_controller_spec.rb b/spec/controllers/physical_ticket_controller_spec.rb index 7a430479..863e27af 100644 --- a/spec/controllers/physical_ticket_controller_spec.rb +++ b/spec/controllers/physical_ticket_controller_spec.rb @@ -9,7 +9,7 @@ describe PhysicalTicketController do describe 'GET #show' do before :each do sign_in user - get :show, id: physical_ticket.id, conference_id: conference.short_title + get :show, id: physical_ticket.token, conference_id: conference.short_title end it 'assigns ticket_layout' do diff --git a/spec/controllers/subscriptions_controller_spec.rb b/spec/controllers/subscriptions_controller_spec.rb index 193ae23d..ac20a34b 100644 --- a/spec/controllers/subscriptions_controller_spec.rb +++ b/spec/controllers/subscriptions_controller_spec.rb @@ -24,7 +24,7 @@ describe SubscriptionsController do it 'shows success message in flash notice' do post :create, conference_id: conference.short_title - expect(flash[:notice]).to match("You have been subscribed to receive email notifications for #{conference.short_title}") + expect(flash[:notice]).to match("You have subscribed to receive email notifications for #{conference.title}") end it 'subscribes user to conference' do @@ -47,7 +47,7 @@ describe SubscriptionsController do it 'shows success message in flash notice' do delete :destroy, conference_id: conference.short_title - expect(flash[:notice]).to match("You have been unsubscribed and now you will not be receiving email notifications for #{conference.short_title}.") + expect(flash[:notice]).to match("You have unsubscribed and you will not be receiving email notifications for #{conference.title}.") end end end diff --git a/spec/factories/booth_request.rb b/spec/factories/booth_request.rb new file mode 100644 index 00000000..fba0cace --- /dev/null +++ b/spec/factories/booth_request.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :booth_request do + booth + user + role 'responsible' + + end +end diff --git a/spec/factories/booths.rb b/spec/factories/booths.rb new file mode 100644 index 00000000..b1cd1956 --- /dev/null +++ b/spec/factories/booths.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + factory :booth do + title { Faker::Hipster.sentence } + description { Faker::Lorem.paragraph } + reasoning { Faker::Lorem.paragraph } + website_url { Faker::Internet.url } + submitter_relationship { Faker::Lorem.paragraph } + + conference + + responsible_ids { [create(:user).id] } + end +end diff --git a/spec/features/cfp_ability_spec.rb b/spec/features/cfp_ability_spec.rb index 98d9ccf5..d40a54a4 100644 --- a/spec/features/cfp_ability_spec.rb +++ b/spec/features/cfp_ability_spec.rb @@ -249,6 +249,16 @@ feature 'Has correct abilities' do visit admin_conference_roles_path(conference.short_title) expect(current_path).to eq(admin_conference_roles_path(conference.short_title)) + visit admin_conference_booths_path(conference.short_title) + expect(current_path).to eq(admin_conference_booths_path(conference.short_title)) + + visit new_admin_conference_booth_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_booth_path(conference.short_title)) + + create(:booth, conference: conference) + visit edit_admin_conference_booth_path(conference.short_title, conference.booths.first) + expect(current_path).to eq(edit_admin_conference_booth_path(conference.short_title, conference.booths.first)) + visit admin_conference_resources_path(conference.short_title) expect(current_path).to eq(admin_conference_resources_path(conference.short_title)) diff --git a/spec/features/organizer_ability_spec.rb b/spec/features/organizer_ability_spec.rb index 959452b1..3ed6ee39 100644 --- a/spec/features/organizer_ability_spec.rb +++ b/spec/features/organizer_ability_spec.rb @@ -237,6 +237,16 @@ feature 'Has correct abilities' do visit edit_admin_conference_target_path(conference.short_title, conference.targets.first) expect(current_path).to eq(edit_admin_conference_target_path(conference.short_title, conference.targets.first)) + visit admin_conference_booths_path(conference.short_title) + expect(current_path).to eq(admin_conference_booths_path(conference.short_title)) + + visit new_admin_conference_booth_path(conference.short_title) + expect(current_path).to eq(new_admin_conference_booth_path(conference.short_title)) + + create(:booth, conference: conference) + visit edit_admin_conference_booth_path(conference.short_title, conference.booths.first) + expect(current_path).to eq(edit_admin_conference_booth_path(conference.short_title, conference.booths.first)) + visit admin_conference_program_tracks_path(conference.short_title) expect(current_path).to eq(admin_conference_program_tracks_path(conference.short_title)) diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index b5620475..9dda06bf 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -28,23 +28,23 @@ feature Event do scenario 'rejects a proposal', feature: true, js: true do visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'New' click_link "reject_event_#{@event.id}" - expect(flash).to eq('Event rejected!') + expect(page).to have_content 'Event rejected!' @event.reload expect(@event.state).to eq('rejected') end scenario 'accepts a proposal', feature: true, js: true do visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'New' click_link "accept_event_#{@event.id}" - expect(flash).to eq('Event accepted!') - expect(page.has_content?('Unconfirmed')).to be true + expect(page).to have_content 'Event accepted!' + expect(page).to have_content 'Unconfirmed' @event.reload expect(@event.state).to eq('unconfirmed') end @@ -52,11 +52,11 @@ feature Event do scenario 'restarts review of a proposal', feature: true, js: true do @event.reject!(@options) visit admin_conference_program_events_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_button 'Rejected' click_link "restart_event_#{@event.id}" - expect(flash).to eq('Review started!') + expect(page).to have_content 'Review started!' @event.reload expect(@event.state).to eq('new') end @@ -83,7 +83,7 @@ feature Event do fill_in 'event_abstract', with: 'Lorem ipsum abstract' click_button 'Create Proposal' - expect(flash).to eq('Proposal was successfully submitted.') + expect(page).to have_content 'Proposal was successfully submitted.' expect(Event.count).to eq(expected_count_event) expect(User.count).to eq(expected_count_user) @@ -101,7 +101,7 @@ feature Event do select('Easy', from: 'event[difficulty_level_id]') click_button 'Update Proposal' - expect(flash).to eq('Proposal was successfully updated.') + expect(page).to have_content 'Proposal was successfully updated.' end scenario 'signed_in user submits a valid proposal', feature: true, js: true do @@ -119,7 +119,7 @@ feature Event do fill_in 'event_description', with: 'Lorem ipsum description' click_button 'Create Proposal' - expect(flash).to eq('Proposal was successfully submitted.') + expect(page).to have_content 'Proposal was successfully submitted.' expect(current_path).to eq(conference_program_proposals_path(conference.short_title)) expect(Event.count).to eq(expected_count) @@ -128,11 +128,10 @@ feature Event do scenario 'confirms a proposal', feature: true, js: true do sign_in participant visit conference_program_proposals_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' expect(@event.state).to eq('unconfirmed') click_link "confirm_proposal_#{@event.id}" - expect(flash) - .to eq('The proposal was confirmed. Please register to attend the conference.') + expect(page).to have_content 'The proposal was confirmed. Please register to attend the conference.' expect(current_path).to eq(new_conference_conference_registration_path(conference.short_title)) @event.reload expect(@event.state).to eq('confirmed') @@ -142,9 +141,9 @@ feature Event do sign_in participant @event.confirm! visit conference_program_proposals_path(conference.short_title) - expect(page.has_content?('Example Proposal')).to be true + expect(page).to have_content 'Example Proposal' click_link "delete_proposal_#{@event.id}" - expect(flash).to eq('Proposal was successfully withdrawn.') + expect(page).to have_content 'Proposal was successfully withdrawn.' @event.reload expect(@event.state).to eq('withdrawn') end diff --git a/spec/models/booth_spec.rb b/spec/models/booth_spec.rb new file mode 100644 index 00000000..5e86a393 --- /dev/null +++ b/spec/models/booth_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe 'Booth' do + subject { create(:booth) } + let!(:conference) { create(:conference) } + + describe 'validation' do + it 'has a valid factory' do + expect(build(:booth)).to be_valid + end + + it { is_expected.to validate_presence_of(:reasoning) } + it { is_expected.to validate_presence_of(:description) } + it { is_expected.to validate_presence_of(:responsibles) } + it { is_expected.to validate_presence_of(:submitter_relationship) } + it { is_expected.to validate_presence_of(:website_url) } + + it 'is not valid without a title' do + is_expected.to validate_presence_of(:title) + end + end + + describe 'association' do + it { is_expected.to belong_to(:conference) } + it { is_expected.to have_many(:booth_requests) } + end + + describe '#transition_possible?(transition)' do + shared_examples 'transition_possible?(transition)' do |state, transition, expected| + it "returns #{expected} for #{transition} transition, when the booth is #{state}}" do + my_booth = create(:booth, state: state) + expect(my_booth.transition_possible?(transition.to_sym)).to eq expected + end + end + + states = [:new, :withdrawn, :to_accept, :accepted, :to_reject, :rejected, :canceled] + transitions = [:restart, :withdraw, :accept, :reject, :to_accept, :to_reject, :cancel] + + states_transitions = { new: { restart: false, withdraw: true, accept: true, to_accept: true, to_reject: true, reject: true, cancel: false }, + withdrawn: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false }, + to_accept: { restart: true, withdraw: true, accept: true, to_accept: false, to_reject: true, reject: false, cancel: true }, + to_reject: { restart: true, withdraw: true, accept: false, to_accept: true, to_reject: false, reject: true, cancel: true }, + accepted: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true }, + rejected: { restart: false, withdraw: true, accept: false, to_accept: false, to_reject: false, reject: false, cancel: true }, + canceled: { restart: true, withdraw: false, accept: false, to_accept: false, to_reject: false, reject: false, cancel: false } } + + states.each do |state| + transitions.each do |transition| + it_behaves_like 'transition_possible?(transition)', state, transition, states_transitions[state.to_sym][transition.to_sym] + end + end + end + +end