From 636692f9921bd695d726695d2d46c91f5a6e56f3 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 11 Apr 2014 10:03:19 +0200 Subject: move engines into engines directory Also renamed help to support so it's harder to confuse it with documentation --- Gemfile | 4 +- Gemfile.lock | 4 +- billing/Gemfile | 23 -- billing/README.md | 49 ---- billing/Rakefile | 40 --- .../app/controllers/billing_admin_controller.rb | 29 --- billing/app/controllers/billing_base_controller.rb | 22 -- .../app/controllers/credit_card_info_controller.rb | 35 --- billing/app/controllers/customer_controller.rb | 64 ----- billing/app/controllers/payments_controller.rb | 34 --- .../app/controllers/subscriptions_controller.rb | 63 ----- billing/app/helpers/billing_helper.rb | 51 ---- billing/app/helpers/braintree_form_helper.rb | 64 ----- billing/app/helpers/braintree_helper.rb | 5 - billing/app/models/customer.rb | 58 ----- billing/app/views/billing_admin/show.html.haml | 7 - .../app/views/credit_card_info/confirm.html.haml | 5 - billing/app/views/credit_card_info/edit.html.haml | 17 -- .../app/views/customer/_customer_data.html.haml | 16 -- billing/app/views/customer/_transaction.html.haml | 0 billing/app/views/customer/confirm.html.haml | 14 -- billing/app/views/customer/edit.html.haml | 23 -- billing/app/views/customer/new.html.haml | 24 -- billing/app/views/customer/show.html.haml | 27 -- .../views/payments/_non_customer_fields.html.haml | 16 -- .../views/payments/_transaction_details.html.haml | 15 -- billing/app/views/payments/confirm.html.haml | 26 -- billing/app/views/payments/index.html.haml | 5 - billing/app/views/payments/new.html.haml | 17 -- .../subscriptions/_subscription_details.html.haml | 26 -- billing/app/views/subscriptions/create.html.haml | 9 - billing/app/views/subscriptions/destroy.html.haml | 7 - billing/app/views/subscriptions/index.html.haml | 8 - billing/app/views/subscriptions/new.html.haml | 15 -- billing/app/views/subscriptions/show.html.haml | 6 - billing/config/initializers/braintree.rb | 23 -- billing/config/locales/en.yml | 11 - billing/config/routes.rb | 25 -- billing/leap_web_billing.gemspec | 21 -- billing/lib/braintree_test_app.rb | 36 --- billing/lib/leap_web_billing.rb | 4 - billing/lib/leap_web_billing/engine.rb | 10 - billing/script/rails | 8 - billing/test/broken/admin_customer_test.rb | 31 --- billing/test/broken/customer_creation_test.rb | 84 ------- billing/test/broken/subscription_test.rb | 49 ---- billing/test/factories.rb | 25 -- .../test/functional/customer_controller_test.rb | 124 ---------- .../test/functional/customers_controller_test.rb | 61 ----- .../test/functional/payments_controller_test.rb | 50 ---- .../functional/subscriptions_controller_test.rb | 16 -- billing/test/support/braintree_integration_test.rb | 18 -- billing/test/support/customer_test_helper.rb | 11 - billing/test/test_helper.rb | 15 -- billing/test/unit/customer_test.rb | 38 --- .../test/unit/customer_with_payment_info_test.rb | 40 --- engines/billing/Gemfile | 23 ++ engines/billing/README.md | 49 ++++ engines/billing/Rakefile | 40 +++ .../app/controllers/billing_admin_controller.rb | 29 +++ .../app/controllers/billing_base_controller.rb | 22 ++ .../app/controllers/credit_card_info_controller.rb | 35 +++ .../billing/app/controllers/customer_controller.rb | 64 +++++ .../billing/app/controllers/payments_controller.rb | 34 +++ .../app/controllers/subscriptions_controller.rb | 63 +++++ engines/billing/app/helpers/billing_helper.rb | 51 ++++ .../billing/app/helpers/braintree_form_helper.rb | 64 +++++ engines/billing/app/helpers/braintree_helper.rb | 5 + engines/billing/app/models/customer.rb | 58 +++++ .../billing/app/views/billing_admin/show.html.haml | 7 + .../app/views/credit_card_info/confirm.html.haml | 5 + .../app/views/credit_card_info/edit.html.haml | 17 ++ .../app/views/customer/_customer_data.html.haml | 16 ++ .../app/views/customer/_transaction.html.haml | 0 .../billing/app/views/customer/confirm.html.haml | 14 ++ engines/billing/app/views/customer/edit.html.haml | 23 ++ engines/billing/app/views/customer/new.html.haml | 24 ++ engines/billing/app/views/customer/show.html.haml | 27 ++ .../views/payments/_non_customer_fields.html.haml | 16 ++ .../views/payments/_transaction_details.html.haml | 15 ++ .../billing/app/views/payments/confirm.html.haml | 26 ++ engines/billing/app/views/payments/index.html.haml | 5 + engines/billing/app/views/payments/new.html.haml | 17 ++ .../subscriptions/_subscription_details.html.haml | 26 ++ .../app/views/subscriptions/create.html.haml | 9 + .../app/views/subscriptions/destroy.html.haml | 7 + .../app/views/subscriptions/index.html.haml | 8 + .../billing/app/views/subscriptions/new.html.haml | 15 ++ .../billing/app/views/subscriptions/show.html.haml | 6 + engines/billing/config/initializers/braintree.rb | 23 ++ engines/billing/config/locales/en.yml | 11 + engines/billing/config/routes.rb | 25 ++ engines/billing/leap_web_billing.gemspec | 21 ++ engines/billing/lib/braintree_test_app.rb | 36 +++ engines/billing/lib/leap_web_billing.rb | 4 + engines/billing/lib/leap_web_billing/engine.rb | 10 + engines/billing/script/rails | 8 + engines/billing/test/broken/admin_customer_test.rb | 31 +++ .../billing/test/broken/customer_creation_test.rb | 84 +++++++ engines/billing/test/broken/subscription_test.rb | 49 ++++ engines/billing/test/factories.rb | 25 ++ .../test/functional/customer_controller_test.rb | 124 ++++++++++ .../test/functional/customers_controller_test.rb | 61 +++++ .../test/functional/payments_controller_test.rb | 50 ++++ .../functional/subscriptions_controller_test.rb | 16 ++ .../test/support/braintree_integration_test.rb | 18 ++ .../billing/test/support/customer_test_helper.rb | 11 + engines/billing/test/test_helper.rb | 15 ++ engines/billing/test/unit/customer_test.rb | 38 +++ .../test/unit/customer_with_payment_info_test.rb | 40 +++ engines/support/Gemfile | 15 ++ engines/support/README.md | 1 + engines/support/Rakefile | 44 ++++ engines/support/app/assets/javascripts/tickets.js | 4 + .../support/app/controllers/tickets_controller.rb | 153 ++++++++++++ .../app/designs/ticket/by_includes_post_by.js | 13 + .../ticket/by_includes_post_by_and_created_at.js | 12 + ..._includes_post_by_and_is_open_and_created_at.js | 12 + ..._includes_post_by_and_is_open_and_updated_at.js | 12 + .../ticket/by_includes_post_by_and_updated_at.js | 12 + .../app/helpers/auto_tickets_path_helper.rb | 53 ++++ engines/support/app/helpers/tickets_helper.rb | 76 ++++++ .../app/models/account_extension/tickets.rb | 13 + engines/support/app/models/ticket.rb | 105 ++++++++ engines/support/app/models/ticket_comment.rb | 43 ++++ engines/support/app/models/ticket_selection.rb | 71 ++++++ .../support/app/views/tickets/_comment.html.haml | 20 ++ .../support/app/views/tickets/_edit_form.html.haml | 48 ++++ .../app/views/tickets/_new_comment_form.html.haml | 13 + engines/support/app/views/tickets/_tabs.html.haml | 23 ++ .../support/app/views/tickets/_ticket.html.haml | 6 + engines/support/app/views/tickets/index.html.haml | 19 ++ engines/support/app/views/tickets/new.html.haml | 30 +++ engines/support/app/views/tickets/show.html.haml | 12 + .../config/initializers/account_lifecycle.rb | 3 + engines/support/config/locales/en.yml | 22 ++ engines/support/config/routes.rb | 8 + engines/support/leap_web_help.gemspec | 18 ++ engines/support/lib/leap_web_help.rb | 4 + engines/support/lib/leap_web_help/engine.rb | 4 + engines/support/lib/tasks/leap_web_help_tasks.rake | 4 + engines/support/script/rails | 8 + engines/support/test/factories.rb | 18 ++ .../test/functional/tickets_controller_test.rb | 273 +++++++++++++++++++++ .../support/test/integration/navigation_test.rb | 9 + engines/support/test/leap_web_help_test.rb | 7 + engines/support/test/test_helper.rb | 15 ++ .../support/test/unit/account_extension_test.rb | 12 + engines/support/test/unit/ticket_comment_test.rb | 59 +++++ engines/support/test/unit/ticket_test.rb | 88 +++++++ help/Gemfile | 15 -- help/README.md | 1 - help/Rakefile | 44 ---- help/app/assets/javascripts/tickets.js | 4 - help/app/controllers/tickets_controller.rb | 153 ------------ help/app/designs/ticket/by_includes_post_by.js | 13 - .../ticket/by_includes_post_by_and_created_at.js | 12 - ..._includes_post_by_and_is_open_and_created_at.js | 12 - ..._includes_post_by_and_is_open_and_updated_at.js | 12 - .../ticket/by_includes_post_by_and_updated_at.js | 12 - help/app/helpers/auto_tickets_path_helper.rb | 53 ---- help/app/helpers/tickets_helper.rb | 76 ------ help/app/models/account_extension/tickets.rb | 13 - help/app/models/ticket.rb | 105 -------- help/app/models/ticket_comment.rb | 43 ---- help/app/models/ticket_selection.rb | 71 ------ help/app/views/tickets/_comment.html.haml | 20 -- help/app/views/tickets/_edit_form.html.haml | 48 ---- help/app/views/tickets/_new_comment_form.html.haml | 13 - help/app/views/tickets/_tabs.html.haml | 23 -- help/app/views/tickets/_ticket.html.haml | 6 - help/app/views/tickets/index.html.haml | 19 -- help/app/views/tickets/new.html.haml | 30 --- help/app/views/tickets/show.html.haml | 12 - help/config/initializers/account_lifecycle.rb | 3 - help/config/locales/en.yml | 22 -- help/config/routes.rb | 8 - help/leap_web_help.gemspec | 18 -- help/lib/leap_web_help.rb | 4 - help/lib/leap_web_help/engine.rb | 4 - help/lib/tasks/leap_web_help_tasks.rake | 4 - help/script/rails | 8 - help/test/factories.rb | 18 -- help/test/functional/tickets_controller_test.rb | 273 --------------------- help/test/integration/navigation_test.rb | 9 - help/test/leap_web_help_test.rb | 7 - help/test/test_helper.rb | 15 -- help/test/unit/account_extension_test.rb | 12 - help/test/unit/ticket_comment_test.rb | 59 ----- help/test/unit/ticket_test.rb | 88 ------- lib/extensions/couchrest.rb | 2 +- lib/tasks/test.rake | 19 +- test/factories.rb | 4 +- test/test_helper.rb | 2 +- 194 files changed, 2897 insertions(+), 2902 deletions(-) delete mode 100644 billing/Gemfile delete mode 100644 billing/README.md delete mode 100644 billing/Rakefile delete mode 100644 billing/app/controllers/billing_admin_controller.rb delete mode 100644 billing/app/controllers/billing_base_controller.rb delete mode 100644 billing/app/controllers/credit_card_info_controller.rb delete mode 100644 billing/app/controllers/customer_controller.rb delete mode 100644 billing/app/controllers/payments_controller.rb delete mode 100644 billing/app/controllers/subscriptions_controller.rb delete mode 100644 billing/app/helpers/billing_helper.rb delete mode 100644 billing/app/helpers/braintree_form_helper.rb delete mode 100644 billing/app/helpers/braintree_helper.rb delete mode 100644 billing/app/models/customer.rb delete mode 100644 billing/app/views/billing_admin/show.html.haml delete mode 100644 billing/app/views/credit_card_info/confirm.html.haml delete mode 100644 billing/app/views/credit_card_info/edit.html.haml delete mode 100644 billing/app/views/customer/_customer_data.html.haml delete mode 100644 billing/app/views/customer/_transaction.html.haml delete mode 100644 billing/app/views/customer/confirm.html.haml delete mode 100644 billing/app/views/customer/edit.html.haml delete mode 100644 billing/app/views/customer/new.html.haml delete mode 100644 billing/app/views/customer/show.html.haml delete mode 100644 billing/app/views/payments/_non_customer_fields.html.haml delete mode 100644 billing/app/views/payments/_transaction_details.html.haml delete mode 100644 billing/app/views/payments/confirm.html.haml delete mode 100644 billing/app/views/payments/index.html.haml delete mode 100644 billing/app/views/payments/new.html.haml delete mode 100644 billing/app/views/subscriptions/_subscription_details.html.haml delete mode 100644 billing/app/views/subscriptions/create.html.haml delete mode 100644 billing/app/views/subscriptions/destroy.html.haml delete mode 100644 billing/app/views/subscriptions/index.html.haml delete mode 100644 billing/app/views/subscriptions/new.html.haml delete mode 100644 billing/app/views/subscriptions/show.html.haml delete mode 100644 billing/config/initializers/braintree.rb delete mode 100644 billing/config/locales/en.yml delete mode 100644 billing/config/routes.rb delete mode 100644 billing/leap_web_billing.gemspec delete mode 100644 billing/lib/braintree_test_app.rb delete mode 100644 billing/lib/leap_web_billing.rb delete mode 100644 billing/lib/leap_web_billing/engine.rb delete mode 100755 billing/script/rails delete mode 100644 billing/test/broken/admin_customer_test.rb delete mode 100644 billing/test/broken/customer_creation_test.rb delete mode 100644 billing/test/broken/subscription_test.rb delete mode 100644 billing/test/factories.rb delete mode 100644 billing/test/functional/customer_controller_test.rb delete mode 100644 billing/test/functional/customers_controller_test.rb delete mode 100644 billing/test/functional/payments_controller_test.rb delete mode 100644 billing/test/functional/subscriptions_controller_test.rb delete mode 100644 billing/test/support/braintree_integration_test.rb delete mode 100644 billing/test/support/customer_test_helper.rb delete mode 100644 billing/test/test_helper.rb delete mode 100644 billing/test/unit/customer_test.rb delete mode 100644 billing/test/unit/customer_with_payment_info_test.rb create mode 100644 engines/billing/Gemfile create mode 100644 engines/billing/README.md create mode 100644 engines/billing/Rakefile create mode 100644 engines/billing/app/controllers/billing_admin_controller.rb create mode 100644 engines/billing/app/controllers/billing_base_controller.rb create mode 100644 engines/billing/app/controllers/credit_card_info_controller.rb create mode 100644 engines/billing/app/controllers/customer_controller.rb create mode 100644 engines/billing/app/controllers/payments_controller.rb create mode 100644 engines/billing/app/controllers/subscriptions_controller.rb create mode 100644 engines/billing/app/helpers/billing_helper.rb create mode 100644 engines/billing/app/helpers/braintree_form_helper.rb create mode 100644 engines/billing/app/helpers/braintree_helper.rb create mode 100644 engines/billing/app/models/customer.rb create mode 100644 engines/billing/app/views/billing_admin/show.html.haml create mode 100644 engines/billing/app/views/credit_card_info/confirm.html.haml create mode 100644 engines/billing/app/views/credit_card_info/edit.html.haml create mode 100644 engines/billing/app/views/customer/_customer_data.html.haml create mode 100644 engines/billing/app/views/customer/_transaction.html.haml create mode 100644 engines/billing/app/views/customer/confirm.html.haml create mode 100644 engines/billing/app/views/customer/edit.html.haml create mode 100644 engines/billing/app/views/customer/new.html.haml create mode 100644 engines/billing/app/views/customer/show.html.haml create mode 100644 engines/billing/app/views/payments/_non_customer_fields.html.haml create mode 100644 engines/billing/app/views/payments/_transaction_details.html.haml create mode 100644 engines/billing/app/views/payments/confirm.html.haml create mode 100644 engines/billing/app/views/payments/index.html.haml create mode 100644 engines/billing/app/views/payments/new.html.haml create mode 100644 engines/billing/app/views/subscriptions/_subscription_details.html.haml create mode 100644 engines/billing/app/views/subscriptions/create.html.haml create mode 100644 engines/billing/app/views/subscriptions/destroy.html.haml create mode 100644 engines/billing/app/views/subscriptions/index.html.haml create mode 100644 engines/billing/app/views/subscriptions/new.html.haml create mode 100644 engines/billing/app/views/subscriptions/show.html.haml create mode 100644 engines/billing/config/initializers/braintree.rb create mode 100644 engines/billing/config/locales/en.yml create mode 100644 engines/billing/config/routes.rb create mode 100644 engines/billing/leap_web_billing.gemspec create mode 100644 engines/billing/lib/braintree_test_app.rb create mode 100644 engines/billing/lib/leap_web_billing.rb create mode 100644 engines/billing/lib/leap_web_billing/engine.rb create mode 100755 engines/billing/script/rails create mode 100644 engines/billing/test/broken/admin_customer_test.rb create mode 100644 engines/billing/test/broken/customer_creation_test.rb create mode 100644 engines/billing/test/broken/subscription_test.rb create mode 100644 engines/billing/test/factories.rb create mode 100644 engines/billing/test/functional/customer_controller_test.rb create mode 100644 engines/billing/test/functional/customers_controller_test.rb create mode 100644 engines/billing/test/functional/payments_controller_test.rb create mode 100644 engines/billing/test/functional/subscriptions_controller_test.rb create mode 100644 engines/billing/test/support/braintree_integration_test.rb create mode 100644 engines/billing/test/support/customer_test_helper.rb create mode 100644 engines/billing/test/test_helper.rb create mode 100644 engines/billing/test/unit/customer_test.rb create mode 100644 engines/billing/test/unit/customer_with_payment_info_test.rb create mode 100644 engines/support/Gemfile create mode 100644 engines/support/README.md create mode 100644 engines/support/Rakefile create mode 100644 engines/support/app/assets/javascripts/tickets.js create mode 100644 engines/support/app/controllers/tickets_controller.rb create mode 100644 engines/support/app/designs/ticket/by_includes_post_by.js create mode 100644 engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js create mode 100644 engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js create mode 100644 engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js create mode 100644 engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js create mode 100644 engines/support/app/helpers/auto_tickets_path_helper.rb create mode 100644 engines/support/app/helpers/tickets_helper.rb create mode 100644 engines/support/app/models/account_extension/tickets.rb create mode 100644 engines/support/app/models/ticket.rb create mode 100644 engines/support/app/models/ticket_comment.rb create mode 100644 engines/support/app/models/ticket_selection.rb create mode 100644 engines/support/app/views/tickets/_comment.html.haml create mode 100644 engines/support/app/views/tickets/_edit_form.html.haml create mode 100644 engines/support/app/views/tickets/_new_comment_form.html.haml create mode 100644 engines/support/app/views/tickets/_tabs.html.haml create mode 100644 engines/support/app/views/tickets/_ticket.html.haml create mode 100644 engines/support/app/views/tickets/index.html.haml create mode 100644 engines/support/app/views/tickets/new.html.haml create mode 100644 engines/support/app/views/tickets/show.html.haml create mode 100644 engines/support/config/initializers/account_lifecycle.rb create mode 100644 engines/support/config/locales/en.yml create mode 100644 engines/support/config/routes.rb create mode 100644 engines/support/leap_web_help.gemspec create mode 100644 engines/support/lib/leap_web_help.rb create mode 100644 engines/support/lib/leap_web_help/engine.rb create mode 100644 engines/support/lib/tasks/leap_web_help_tasks.rake create mode 100755 engines/support/script/rails create mode 100644 engines/support/test/factories.rb create mode 100644 engines/support/test/functional/tickets_controller_test.rb create mode 100644 engines/support/test/integration/navigation_test.rb create mode 100644 engines/support/test/leap_web_help_test.rb create mode 100644 engines/support/test/test_helper.rb create mode 100644 engines/support/test/unit/account_extension_test.rb create mode 100644 engines/support/test/unit/ticket_comment_test.rb create mode 100644 engines/support/test/unit/ticket_test.rb delete mode 100644 help/Gemfile delete mode 100644 help/README.md delete mode 100644 help/Rakefile delete mode 100644 help/app/assets/javascripts/tickets.js delete mode 100644 help/app/controllers/tickets_controller.rb delete mode 100644 help/app/designs/ticket/by_includes_post_by.js delete mode 100644 help/app/designs/ticket/by_includes_post_by_and_created_at.js delete mode 100644 help/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js delete mode 100644 help/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js delete mode 100644 help/app/designs/ticket/by_includes_post_by_and_updated_at.js delete mode 100644 help/app/helpers/auto_tickets_path_helper.rb delete mode 100644 help/app/helpers/tickets_helper.rb delete mode 100644 help/app/models/account_extension/tickets.rb delete mode 100644 help/app/models/ticket.rb delete mode 100644 help/app/models/ticket_comment.rb delete mode 100644 help/app/models/ticket_selection.rb delete mode 100644 help/app/views/tickets/_comment.html.haml delete mode 100644 help/app/views/tickets/_edit_form.html.haml delete mode 100644 help/app/views/tickets/_new_comment_form.html.haml delete mode 100644 help/app/views/tickets/_tabs.html.haml delete mode 100644 help/app/views/tickets/_ticket.html.haml delete mode 100644 help/app/views/tickets/index.html.haml delete mode 100644 help/app/views/tickets/new.html.haml delete mode 100644 help/app/views/tickets/show.html.haml delete mode 100644 help/config/initializers/account_lifecycle.rb delete mode 100644 help/config/locales/en.yml delete mode 100644 help/config/routes.rb delete mode 100644 help/leap_web_help.gemspec delete mode 100644 help/lib/leap_web_help.rb delete mode 100644 help/lib/leap_web_help/engine.rb delete mode 100644 help/lib/tasks/leap_web_help_tasks.rake delete mode 100755 help/script/rails delete mode 100644 help/test/factories.rb delete mode 100644 help/test/functional/tickets_controller_test.rb delete mode 100644 help/test/integration/navigation_test.rb delete mode 100644 help/test/leap_web_help_test.rb delete mode 100644 help/test/test_helper.rb delete mode 100644 help/test/unit/account_extension_test.rb delete mode 100644 help/test/unit/ticket_comment_test.rb delete mode 100644 help/test/unit/ticket_test.rb diff --git a/Gemfile b/Gemfile index 50c4b00..b8b4568 100644 --- a/Gemfile +++ b/Gemfile @@ -10,8 +10,8 @@ gem "json" gem "ruby-srp", "~> 0.2.1" gem "rails_warden" -gem 'leap_web_help', :path => 'help' -gem 'leap_web_billing', :path => 'billing' +gem 'leap_web_help', :path => 'engines/support' +gem 'leap_web_billing', :path => 'engines/billing' gem 'http_accept_language' diff --git a/Gemfile.lock b/Gemfile.lock index 537dafd..a2a1785 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,13 +6,13 @@ GIT activemodel (>= 3.0.6) PATH - remote: billing + remote: engines/billing specs: leap_web_billing (0.5.0) braintree PATH - remote: help + remote: engines/support specs: leap_web_help (0.5.0) diff --git a/billing/Gemfile b/billing/Gemfile deleted file mode 100644 index 30e9669..0000000 --- a/billing/Gemfile +++ /dev/null @@ -1,23 +0,0 @@ -source "https://rubygems.org" - -eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) -eval(File.read(File.dirname(__FILE__) + '/../ui_dependencies.rb')) - -# We require leap_web_core from here so we can use the path option. -gem "leap_web_core", :path => '../core' - -# Declare your gem's dependencies in billing.gemspec. -# Bundler will treat runtime dependencies like base dependencies, and -# development dependencies will be added by default to the :development group. -gemspec - -# jquery-rails is used by the dummy application -#gem "jquery-rails" - -# Declare any dependencies that are still in development here instead of in -# your gemspec. These might include edge Rails or gems from your path or -# Git. Remember to move these dependencies to your gemspec before releasing -# your gem to rubygems.org. - -# To use debugger -# gem 'debugger' diff --git a/billing/README.md b/billing/README.md deleted file mode 100644 index 3ef6153..0000000 --- a/billing/README.md +++ /dev/null @@ -1,49 +0,0 @@ -Billing Engine -==================== - -Currently, this engine support billing via Braintree. More backends to come later. - -Configuration ----------------------------------- - -Start with a sandbox account, which you can get here: https://www.braintreepayments.com/get-started - -Once you have registered for the sandbox, logging in will show you three important variables you will need to configure: - -* merchantId -* publicKey -* privatekey - -To configure the billing engine, edit `config/config.yaml` like so: - - production: (or "development", as you prefer) - billing: - braintree: - environment: sandbox - merchant_id: Ohp2aijaaqu6oJ4w - public_key: ahnar0UwLahwe6Ce - private_key: aemie2Geohgah2EaOad9DeeruW4Iegh4 - -If deploying via puppet, the same data in webapp.json would like this: - - "billing": { - "braintree": { - "environment": "sandbox", - "merchant_id": "Ohp2aijaaqu6oJ4w", - "public_key": "ahnar0UwLahwe6Ce", - "private_key": "aemie2Geohgah2EaOad9DeeruW4Iegh4" - } - } - -Now, you should be able to add charges to your own sandbox when you run the webapp. - -The acceptable values for `billing.braintree.environment` are: `development`, `qa`, `sandbox`, or `production`. - -Plans --------------------------------- - -You also will want to add a Plan to your Sandbox. Within the Braintree Sandbox, navigate to 'Recurring Billing' -> 'Plans'. From here, you can add a new Plan. The values of the test plan are not important, but the ID will be displayed, so should pick something descriptive. - -Here are credit cared numbers to try in the Sandbox: - -https://www.braintreepayments.com/docs/ruby/reference/sandbox \ No newline at end of file diff --git a/billing/Rakefile b/billing/Rakefile deleted file mode 100644 index 52929c4..0000000 --- a/billing/Rakefile +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env rake -begin - require 'bundler/setup' -rescue LoadError - puts 'You must `gem install bundler` and `bundle install` to run rake tasks' -end -begin - require 'rdoc/task' -rescue LoadError - require 'rdoc/rdoc' - require 'rake/rdoctask' - RDoc::Task = Rake::RDocTask -end - -RDoc::Task.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'LeapWebBilling' - rdoc.options << '--line-numbers' - rdoc.rdoc_files.include('README.rdoc') - rdoc.rdoc_files.include('lib/**/*.rb') -end - -spec = eval(File.read('leap_web_billing.gemspec')) -Gem::PackageTask.new(spec) do |p| - p.gem_spec = spec -end - -Bundler::GemHelper.install_tasks - -require 'rake/testtask' - -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.libs << 'test' - t.pattern = 'test/**/*_test.rb' - t.verbose = false -end - - -task :default => :test diff --git a/billing/app/controllers/billing_admin_controller.rb b/billing/app/controllers/billing_admin_controller.rb deleted file mode 100644 index e11d4ee..0000000 --- a/billing/app/controllers/billing_admin_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -class BillingAdminController < BillingBaseController - before_filter :require_admin - - def show - - br_atleast_90_days = Braintree::Subscription.search do |search| - search.days_past_due >= 90 - end - @past_due_atleast_90_days = braintree_resource_collection_to_array(br_atleast_90_days) - - br_all_past_due = Braintree::Subscription.search do |search| - search.status.is Braintree::Subscription::Status::PastDue - #cannot search by balance. - end - @all_past_due = braintree_resource_collection_to_array(br_all_past_due) - - end - - private - - def braintree_resource_collection_to_array(braintree_resource_collection) - array = [] - braintree_resource_collection.each do |object| - array << object - end - array - end - -end diff --git a/billing/app/controllers/billing_base_controller.rb b/billing/app/controllers/billing_base_controller.rb deleted file mode 100644 index 0453677..0000000 --- a/billing/app/controllers/billing_base_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -class BillingBaseController < ApplicationController - before_filter :assign_user - - helper 'billing' - - # required for navigation to work. - def assign_user - if params[:user_id] - @user = User.find(params[:user_id]) - elsif params[:action] == "confirm"# confirms will come back with different ID set, so check for this first - # This is only for cases where an admin cannot apply action for customer, but should be all confirms - @user = current_user - elsif params[:id] - @user = User.find(params[:id]) - else - # TODO - # hacky, what are cases where @user hasn't yet been set? certainly some cases with subscriptions and payments - @user = current_user - end - end - -end diff --git a/billing/app/controllers/credit_card_info_controller.rb b/billing/app/controllers/credit_card_info_controller.rb deleted file mode 100644 index fbaa6f1..0000000 --- a/billing/app/controllers/credit_card_info_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -class CreditCardInfoController < ApplicationController - before_filter :require_login, :set_user - - def edit - @credit_card = Braintree::CreditCard.find(params[:id]) - customer = Customer.find_by_user_id(@user.id) - if customer and customer.braintree_customer_id == @credit_card.customer_id - @tr_data = Braintree::TransparentRedirect. - update_credit_card_data(:redirect_url => confirm_credit_card_info_url, - :payment_method_token => @credit_card.token) - else - access_denied - end - - end - - def confirm - @result = Braintree::TransparentRedirect.confirm(request.query_string) - if @result.success? - render :action => "confirm" - else - @credit_card = Braintree::CreditCard.find(@result.params[:payment_method_token]) - render :action => "edit" - end - end - - - private - - def set_user - # this assumes anybody, even an admin, will not access for another user. - @user = current_user - end - -end diff --git a/billing/app/controllers/customer_controller.rb b/billing/app/controllers/customer_controller.rb deleted file mode 100644 index 6cbcb44..0000000 --- a/billing/app/controllers/customer_controller.rb +++ /dev/null @@ -1,64 +0,0 @@ -class CustomerController < BillingBaseController - before_filter :require_login, :fetch_customer - - def show - if @customer - @customer.with_braintree_data! - @default_cc = @customer.default_credit_card - @active_subscription = @customer.subscriptions - @transactions = @customer.braintree_customer.transactions - end - end - - def new - if @customer.has_payment_info? - redirect_to edit_customer_path(@user), :notice => 'Here is your saved customer data' - else - fetch_new_transparent_redirect_data - end - end - - def edit - fetch_edit_transparent_redirect_data - end - - def confirm - @result = Braintree::TransparentRedirect.confirm(request.query_string) - if @result.success? - @customer.braintree_customer = @result.customer - @customer.save - render :action => "confirm" - elsif @customer.has_payment_info? - fetch_edit_transparent_redirect_data - render :action => "edit" - else - fetch_new_transparent_redirect_data - render :action => "new" - end - end - - protected - - def fetch_new_transparent_redirect_data - access_denied unless @user == current_user # admins cannot do this for others - @tr_data = Braintree::TransparentRedirect. - create_customer_data(:redirect_url => confirm_customer_url) - end - - def fetch_edit_transparent_redirect_data - access_denied unless @user == current_user # admins cannot do this for others - @customer.with_braintree_data! - @default_cc = @customer.default_credit_card - @tr_data = Braintree::TransparentRedirect. - update_customer_data(:redirect_url => confirm_customer_url, - :customer_id => @customer.braintree_customer_id) ##?? - end - - def fetch_customer - @customer = Customer.find_by_user_id(@user.id) - if @user == current_user - @customer ||= Customer.new(user: @user) - end - access_denied unless (@customer and (@customer.user == current_user)) or admin? - end -end diff --git a/billing/app/controllers/payments_controller.rb b/billing/app/controllers/payments_controller.rb deleted file mode 100644 index fce6570..0000000 --- a/billing/app/controllers/payments_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -class PaymentsController < BillingBaseController - before_filter :require_login, :only => [:index] - - def new - fetch_transparent_redirect - end - - def confirm - @result = Braintree::TransparentRedirect.confirm(request.query_string) - if @result.success? - render :action => "confirm" - else - fetch_transparent_redirect - render :action => "new" - end - end - - def index - access_denied unless admin? or (@user == current_user) - customer = Customer.find_by_user_id(@user.id) - braintree_data = Braintree::Customer.find(customer.braintree_customer_id) - # these will be ordered by created_at descending, per http://stackoverflow.com/questions/16425475/ - @transactions = braintree_data.transactions - end - - protected - - - def fetch_transparent_redirect - @tr_data = Braintree::TransparentRedirect.transaction_data redirect_url: confirm_payment_url, - transaction: { type: "sale", options: {submit_for_settlement: true } } - end - -end diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb deleted file mode 100644 index f066b3c..0000000 --- a/billing/app/controllers/subscriptions_controller.rb +++ /dev/null @@ -1,63 +0,0 @@ -class SubscriptionsController < BillingBaseController - before_filter :require_login - before_filter :fetch_subscription, :only => [:show, :destroy] - before_filter :confirm_cancel_subscription, :only => [:destroy] - before_filter :confirm_self_or_admin, :only => [:index] - before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] - # for now, admins cannot create or destroy subscriptions for others: - before_filter :confirm_self, :only => [:new, :create] - - def new - # don't show link to subscribe if they are already subscribed? - credit_card = @customer.default_credit_card #safe to assume default? - @payment_method_token = credit_card.token - @plans = Braintree::Plan.all - end - - # show has no content, so not needed at this point. - - def create - @result = Braintree::Subscription.create( :payment_method_token => params[:payment_method_token], :plan_id => params[:plan_id] ) - #if you want to test pastdue, can add :price => '2001', :trial_period => true,:trial_duration => 1,:trial_duration_unit => "day" and then wait a day - end - - def destroy - @result = Braintree::Subscription.cancel params[:id] - end - - def index - customer = Customer.find_by_user_id(@user.id) - @subscriptions = customer.subscriptions(nil, false) - end - - private - - def fetch_subscription - @subscription = Braintree::Subscription.find params[:id] - @credit_card = Braintree::CreditCard.find @subscription.payment_method_token - @subscription_customer_id = @credit_card.customer_id - current_user_customer = Customer.find_by_user_id(current_user.id) - access_denied unless admin? or (current_user_customer and current_user_customer.braintree_customer_id == @subscription_customer_id) - - end - - def confirm_cancel_subscription - access_denied unless view_context.allow_cancel_subscription(@subscription) - end - - def confirm_no_pending_active_pastdue_subscription - @customer = Customer.find_by_user_id(@user.id) - if subscription = @customer.subscriptions # will return pending, active or pastdue subscription, if it exists - redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription' - end - end - - def confirm_self - @user == current_user - end - - def confirm_self_or_admin - access_denied unless confirm_self or admin? - end - -end diff --git a/billing/app/helpers/billing_helper.rb b/billing/app/helpers/billing_helper.rb deleted file mode 100644 index b9e5e2e..0000000 --- a/billing/app/helpers/billing_helper.rb +++ /dev/null @@ -1,51 +0,0 @@ -module BillingHelper - - def braintree_form_for(object, options = {}, &block) - options.reverse_merge! params: @result && @result.params[object], - errors: @result && @result.errors.for(object), - builder: BraintreeFormHelper::BraintreeFormBuilder, - url: Braintree::TransparentRedirect.url - - form_for object, options, &block - end - - def billing_top_link(user) - # for admins, top link will show special admin information, which has link to show their own customer information - if (admin? and user == current_user) - billing_admin_path - else - show_or_new_customer_link(user) - end - end - - def show_or_new_customer_link(user) - # Link to show if user is admin viewing another user, or user is already a customer. - # Otherwise link to create a new customer. - if (admin? and (user != current_user)) or ((customer = Customer.find_by_user_id(user.id)) and customer.has_payment_info?) - show_customer_path(user) - else - new_customer_path - end - end - - # a bit strange to put here, but we don't have a subscription model - def user_for_subscription(subscription) - - if (transaction = subscription.transactions.first) - # much quicker, but will only work if there is already a transaction associated with subscription (should generally be) - braintree_customer_id = transaction.customer_details.id - else - credit_card = Braintree::CreditCard.find(subscription.payment_method_token) - braintree_customer_id = credit_card.customer_id - end - - customer = Customer.find_by_braintree_customer_id(braintree_customer_id) - user = User.find(customer.user_id) - - end - - def allow_cancel_subscription(subscription) - ['Active', 'Pending'].include? subscription.status or (admin? and subscription.status == 'Past Due') - end - -end diff --git a/billing/app/helpers/braintree_form_helper.rb b/billing/app/helpers/braintree_form_helper.rb deleted file mode 100644 index cb322fa..0000000 --- a/billing/app/helpers/braintree_form_helper.rb +++ /dev/null @@ -1,64 +0,0 @@ -module BraintreeFormHelper - class BraintreeFormBuilder < ActionView::Helpers::FormBuilder - include ActionView::Helpers::AssetTagHelper - include ActionView::Helpers::TagHelper - - def initialize(object_name, object, template, options, proc) - super - @braintree_params = @options[:params] - @braintree_errors = @options[:errors] - @braintree_existing = @options[:existing] - end - - def fields_for(record_name, *args, &block) - options = args.extract_options! - options[:builder] = BraintreeFormBuilder - options[:params] = @braintree_params && @braintree_params[record_name] - options[:errors] = @braintree_errors && @braintree_errors.for(record_name) - new_args = args + [options] - super record_name, *new_args, &block - end - - def text_field(method, options = {}) - has_errors = @braintree_errors && @braintree_errors.on(method).any? - field = super(method, options.merge(:value => determine_value(method))) - result = content_tag("div", field, :class => has_errors ? "fieldWithErrors" : "") - result.safe_concat validation_errors(method) - result - end - - protected - - def determine_value(method) - if @braintree_params - @braintree_params[method] - elsif @braintree_existing - - if @braintree_existing.kind_of?(Braintree::CreditCard) - - case method - when :number - method = :masked_number - when :cvv - return nil - end - end - - @braintree_existing.send(method) - else - nil - end - end - - def validation_errors(method) - if @braintree_errors && @braintree_errors.on(method).any? - @braintree_errors.on(method).map do |error| - content_tag("div", ERB::Util.h(error.message), {:style => "color: red;"}) - end.join - else - "" - end - end - end -end - diff --git a/billing/app/helpers/braintree_helper.rb b/billing/app/helpers/braintree_helper.rb deleted file mode 100644 index 2d18b6c..0000000 --- a/billing/app/helpers/braintree_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module BraintreeHelper - - -end - diff --git a/billing/app/models/customer.rb b/billing/app/models/customer.rb deleted file mode 100644 index 1acc7a5..0000000 --- a/billing/app/models/customer.rb +++ /dev/null @@ -1,58 +0,0 @@ -class Customer < CouchRest::Model::Base - - FIELDS = [:first_name, :last_name, :phone, :website, :company, :fax, :addresses, :credit_cards, :custom_fields] - attr_accessor *FIELDS - - use_database "customers" - belongs_to :user - belongs_to :braintree_customer - - # Braintree::Customer - stored on braintrees servers - we only have the id. - def braintree_customer - @braintree_customer ||= Braintree::Customer.find(braintree_customer_id) - end - - validates :user, presence: true - - design do - view :by_user_id - view :by_braintree_customer_id - end - - def has_payment_info? - !!braintree_customer_id - end - - # from braintree_ruby_examples/rails3_tr_devise and should be tweaked - def with_braintree_data! - return self unless has_payment_info? - - FIELDS.each do |field| - send(:"#{field}=", braintree_customer.send(field)) - end - self - end - - def default_credit_card - return unless has_payment_info? - - credit_cards.find { |cc| cc.default? } - end - - # based on 2nd parameter, either returns the single active subscription (or nil if there isn't one), or an array of all subsciptions - def subscriptions(braintree_data=nil, only_pending_active_pastdue=true) - self.with_braintree_data! - return unless has_payment_info? - - subscriptions = [] - self.default_credit_card.subscriptions.each do |sub| - if only_pending_active_pastdue and ['Pending', 'Active','Past Due'].include? sub.status - return sub - else - subscriptions << sub - end - end - only_pending_active_pastdue ? nil : subscriptions - end - -end diff --git a/billing/app/views/billing_admin/show.html.haml b/billing/app/views/billing_admin/show.html.haml deleted file mode 100644 index 0382cf0..0000000 --- a/billing/app/views/billing_admin/show.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%legend= t(:more_than_90_days_past_due) -= render(:partial => "subscriptions/subscription_details", :collection => @past_due_atleast_90_days, :as => 'subscription', :locals => {:show_user => true}) || t(:none) -%legend= t(:all_past_due) -= render(:partial => "subscriptions/subscription_details", :collection => @all_past_due, :as => 'subscription', :locals => {:show_user => true}) || t(:none) - -%legend= t(:your_settings) -= link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file diff --git a/billing/app/views/credit_card_info/confirm.html.haml b/billing/app/views/credit_card_info/confirm.html.haml deleted file mode 100644 index 9dd8176..0000000 --- a/billing/app/views/credit_card_info/confirm.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%h1 Payment Info Confirmation -%p Your payment information was successfully saved. -%dl - %dt Credit Card - %dd= @result.credit_card.masked_number diff --git a/billing/app/views/credit_card_info/edit.html.haml b/billing/app/views/credit_card_info/edit.html.haml deleted file mode 100644 index bd86a4c..0000000 --- a/billing/app/views/credit_card_info/edit.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%h1 Change Credit Card -- if @result - #total-errors{:style => "color:red;"} - = h(@result.errors.size) - error(s) -= braintree_form_for :credit_card, :existing => @credit_card do |f| - = field_set_tag "Credit Card" do - %dl - %dt= f.label :number, 'Number' - %dd= f.text_field :number - %dt= f.label :expiration_date, 'Expiration Date (MM/YY)' - %dd= f.text_field :expiration_date - %dt= f.label :cvv, 'CVV' - %dd= f.text_field :cvv - = hidden_field_tag :tr_data, @tr_data - = f.submit 'Save Payment Info', :class => :btn - = link_to t(:cancel), edit_customer_path(@user.id), :class => :btn diff --git a/billing/app/views/customer/_customer_data.html.haml b/billing/app/views/customer/_customer_data.html.haml deleted file mode 100644 index e9df040..0000000 --- a/billing/app/views/customer/_customer_data.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%legend= t(:customer_information) -%dl - %dt First Name - %dd= @customer.first_name - %dt Last Name - %dd= @customer.last_name - %dt Phone - %dd= @customer.phone -%legend= t(:credit_card_information) -%dl - %dt Number - %dd= @default_cc.masked_number - %dt Expiration Date - %dd= @default_cc.expiration_date - - if current_user == @user - = link_to t(:edit_saved_data), edit_customer_path(@user.id), :class => :btn diff --git a/billing/app/views/customer/_transaction.html.haml b/billing/app/views/customer/_transaction.html.haml deleted file mode 100644 index e69de29..0000000 diff --git a/billing/app/views/customer/confirm.html.haml b/billing/app/views/customer/confirm.html.haml deleted file mode 100644 index 877a8ac..0000000 --- a/billing/app/views/customer/confirm.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -%h1 Payment Info Confirmation -%p Your payment information was successfully saved. -%dl - %dt First Name - %dd= @result.customer.first_name - %dt Last Name - %dd= @result.customer.last_name - %dt Phone - %dd= @result.customer.phone - %dt Credit Card - - @result.customer.credit_cards.each do |cc| - %dd= cc.masked_number -- customer = Customer.find_by_user_id(@user.id) -= link_to 'View Customer Info', show_customer_path(@user.id), :class=> :btn \ No newline at end of file diff --git a/billing/app/views/customer/edit.html.haml b/billing/app/views/customer/edit.html.haml deleted file mode 100644 index e882d53..0000000 --- a/billing/app/views/customer/edit.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- if @result - #total-errors{:style => "color:red;"} - = h(@result.errors.size) - error(s) -= braintree_form_for :customer, existing: @customer do |f| - = field_set_tag "Customer" do - %dl - %dt= f.label :first_name, 'First Name' - %dd= f.text_field :first_name - %dt= f.label :last_name, 'Last Name' - %dd= f.text_field :last_name - %dt= f.label :phone, 'Phone' - %dd= f.text_field :phone - - if @default_cc - = # todo, as they will need a credit card, so not sure about conditional? - %dt= t(:stored_credit_card) - %dd - = @default_cc.masked_number - = link_to t(:change_credit_card), edit_credit_card_info_path(:id => @default_cc.token), :class => :btn - = hidden_field_tag :tr_data, @tr_data - .form-actions - = f.submit t(:save_customer_info), :class => 'btn btn-primary' - = link_to t(:cancel), show_customer_path(@user), :class=> :btn diff --git a/billing/app/views/customer/new.html.haml b/billing/app/views/customer/new.html.haml deleted file mode 100644 index e1f5ba9..0000000 --- a/billing/app/views/customer/new.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- if @result - #total-errors{:style => "color:red;"} - = h(@result.errors.size) - error(s) -= braintree_form_for :customer do |f| - = field_set_tag "Customer" do - %dl - %dt= f.label :first_name, 'First Name' - %dd= f.text_field :first_name - %dt= f.label :last_name, 'Last Name' - %dd= f.text_field :last_name - %dt= f.label :phone, 'Phone' - %dd= f.text_field :phone - = field_set_tag "Credit Card" do - - f.fields_for :credit_card do |cc| - %dl - %dt= cc.label :number, 'Number' - %dd= cc.text_field :number - %dt= cc.label :expiration_date, 'Expiration Date (MM/YY)' - %dd= cc.text_field :expiration_date - %dt= cc.label :cvv, 'CVV' - %dd= cc.text_field :cvv - = hidden_field_tag :tr_data, @tr_data - = f.submit 'Save Payment Info' diff --git a/billing/app/views/customer/show.html.haml b/billing/app/views/customer/show.html.haml deleted file mode 100644 index ec1779c..0000000 --- a/billing/app/views/customer/show.html.haml +++ /dev/null @@ -1,27 +0,0 @@ -- if admin? and !@customer - = t(:no_saved_customer) -- else - = render :partial => 'customer_data' - %legend= t(:last_three_transactions) - - counter = 0 - = # these will be ordered with most recently created first, per http://stackoverflow.com/questions/16425475/ - - @transactions.each do |t| - - break if counter > 2 # not ruby-like, but object is a Braintree::ResourceCollection so limited methods available - = render :partial => "payments/transaction_details", :locals => {:transaction => t} - - counter += 1 - = link_to t(:transaction_history), user_payments_path(@user) - %legend= t(:subscriptions) - - if @active_subscription - = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription} - - else - %p - = t(:no_relevant_subscription) - - if current_user == @user - %p - .form-actions - = link_to t(:subscribe_to_plan), new_subscription_path, :class => :btn - %p - = link_to t(:all_subscriptions), user_subscriptions_path(@user) - -.form-actions - = link_to t(:make_donation), new_payment_path, :class => 'btn btn-primary' diff --git a/billing/app/views/payments/_non_customer_fields.html.haml b/billing/app/views/payments/_non_customer_fields.html.haml deleted file mode 100644 index 77cfe95..0000000 --- a/billing/app/views/payments/_non_customer_fields.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -= field_set_tag "Personal Information" do - = f.fields_for :customer do |c| - %div= c.label :first_name, "First Name" - %div= c.text_field :first_name - %div= c.label :last_name, "Last Name" - %div= c.text_field :last_name - %div= c.label :email, "Email" - %div= c.text_field :email -= field_set_tag "Credit Card" do - = f.fields_for :credit_card do |c| - %div= c.label :number, "Number" - %div= c.text_field :number - %div= c.label :expiration_date, "Expiration Date (MM/YY)" - %div= c.text_field :expiration_date - %div= c.label :cvv, "CVV" - %div= c.text_field :cvv \ No newline at end of file diff --git a/billing/app/views/payments/_transaction_details.html.haml b/billing/app/views/payments/_transaction_details.html.haml deleted file mode 100644 index 85e4f6a..0000000 --- a/billing/app/views/payments/_transaction_details.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -%p - = transaction.id - Type: - = transaction.type - Amount: - = number_to_currency(transaction.amount) - Status: - = transaction.status - Date - = transaction.created_at.strftime("%Y-%m-%d") - - if sub_start = transaction.subscription_details.billing_period_start_date - From subscription which started - = sub_start - - else # should not have any of these - Not paid as part of subscription \ No newline at end of file diff --git a/billing/app/views/payments/confirm.html.haml b/billing/app/views/payments/confirm.html.haml deleted file mode 100644 index 45af3c9..0000000 --- a/billing/app/views/payments/confirm.html.haml +++ /dev/null @@ -1,26 +0,0 @@ -%h1 Payment Result -%div Thank you for your donation. -%h2 Transaction Details -%table - %tr - %td Amount - %td - $#{@result.transaction.amount} - %tr - %td Transaction ID: - %td= @result.transaction.id - %tr - %td First Name: - %td= h @result.transaction.customer_details.first_name - %tr - %td Last Name: - %td= h @result.transaction.customer_details.last_name - %tr - %td Email: - %td= h @result.transaction.customer_details.email - %tr - %td Credit Card: - %td= h @result.transaction.credit_card_details.masked_number - %tr - %td Card Type: - %td= h @result.transaction.credit_card_details.card_type \ No newline at end of file diff --git a/billing/app/views/payments/index.html.haml b/billing/app/views/payments/index.html.haml deleted file mode 100644 index 7a89917..0000000 --- a/billing/app/views/payments/index.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%h2=t :transaction_history -- if (@transactions.count == 0) - = t(:no_transaction_history) -- @transactions.each do |t| - = render :partial => "transaction_details", :locals => {:transaction => t} \ No newline at end of file diff --git a/billing/app/views/payments/new.html.haml b/billing/app/views/payments/new.html.haml deleted file mode 100644 index e9a8273..0000000 --- a/billing/app/views/payments/new.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%h1 - = t(:Donation) -- if logged_in? - = t(:donation_not_payment) -- if @result and @result.errors.size > 0 - %div{:style => "color: red;"} - = h @result.errors.size - error(s) -- if @result and @result.transaction and @result.transaction.status != 'success' - %div{:style => "color: red;"} - = t(:processor_declined) -= braintree_form_for :transaction, :html => {:autocomplete => "off"} do |f| - = f.label :amount, t(:amount) - = f.text_field :amount - = render :partial => 'non_customer_fields', :locals => {:f => f} - = hidden_field_tag :tr_data, @tr_data - = f.submit "Submit Donation", :class => 'btn btn-primary' diff --git a/billing/app/views/subscriptions/_subscription_details.html.haml b/billing/app/views/subscriptions/_subscription_details.html.haml deleted file mode 100644 index 6145c95..0000000 --- a/billing/app/views/subscriptions/_subscription_details.html.haml +++ /dev/null @@ -1,26 +0,0 @@ -%p - - if local_assigns[:show_user] - User: - - user_to_show = user_for_subscription(subscription) - = link_to user_to_show.login, user_overview_path(user_to_show) - ID: - = link_to subscription.id, user_subscription_path(@user, subscription.id) - Balance: - - color = (subscription.balance > 0) ? "red" : "" - %font{:color => color} - = number_to_currency(subscription.balance) - Bill on: - = subscription.billing_day_of_month - Start date: - = subscription.first_billing_date - Paid through: - = subscription.paid_through_date - Plan: - = subscription.plan_id - Price: - = number_to_currency(subscription.price) - - color = (subscription.status == 'Active') ? "green" : "red" - Status: - %font{:color => color} - = subscription.status - - # would be good to get plan name but not sure if that is possible? \ No newline at end of file diff --git a/billing/app/views/subscriptions/create.html.haml b/billing/app/views/subscriptions/create.html.haml deleted file mode 100644 index 2b6c5e9..0000000 --- a/billing/app/views/subscriptions/create.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -- if @result.success? - %h1 - Subscription Status - = @result.subscription.status - = render :partial => "subscription_details", :locals => {:subscription => @result.subscription} -- else - %h1 - Error: - = @result.message \ No newline at end of file diff --git a/billing/app/views/subscriptions/destroy.html.haml b/billing/app/views/subscriptions/destroy.html.haml deleted file mode 100644 index 44b4333..0000000 --- a/billing/app/views/subscriptions/destroy.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- if @result.success? - Subscription destroyed -- else - Error: - = @result.message -%p - = link_to 'Customer Information', show_customer_path(@user), :class=> :btn \ No newline at end of file diff --git a/billing/app/views/subscriptions/index.html.haml b/billing/app/views/subscriptions/index.html.haml deleted file mode 100644 index 3d4e8fd..0000000 --- a/billing/app/views/subscriptions/index.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%h2=t :all_subscriptions -- pending_active_pastdue = false -- @subscriptions.each do |s| - - if ['Pending', 'Active','Past Due'].include? s.status - - pending_active_pastdue = true - = render :partial => "subscription_details", :locals => {:subscription => s} -- if !pending_active_pastdue and @user == current_user - = link_to 'subscribe to plan', new_subscription_path, :class => :btn \ No newline at end of file diff --git a/billing/app/views/subscriptions/new.html.haml b/billing/app/views/subscriptions/new.html.haml deleted file mode 100644 index 4183458..0000000 --- a/billing/app/views/subscriptions/new.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -- if @payment_method_token - %h1 - Subscribe to plan - = #currently just one plan - = @plans[0].name - = number_to_currency(@plans[0].price) - = simple_form_for :subscription, :url => :subscriptions do |f| - = hidden_field_tag :payment_method_token, @payment_method_token - = hidden_field_tag :plan_id, @plans[0].id - .form-actions - = f.submit t(:subscribe), :class => 'btn btn-primary' -- else - = t(:must_create_customer) - %p - = link_to t(:create_new_customer), new_customer_path diff --git a/billing/app/views/subscriptions/show.html.haml b/billing/app/views/subscriptions/show.html.haml deleted file mode 100644 index 2699db9..0000000 --- a/billing/app/views/subscriptions/show.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -%h1 - - if @subscription.status == 'Active' - Current - Subscription -= render :partial => "subscription_details", :locals => {:subscription => @subscription} -= link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if allow_cancel_subscription(@subscription) diff --git a/billing/config/initializers/braintree.rb b/billing/config/initializers/braintree.rb deleted file mode 100644 index 3d87f4c..0000000 --- a/billing/config/initializers/braintree.rb +++ /dev/null @@ -1,23 +0,0 @@ -# -# set logger -# -if APP_CONFIG[:logfile].blank? - require 'syslog/logger' - Braintree::Configuration.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new('webapp')) -else - Braintree::Configuration.logger = Logger.new('log/braintree.log') -end - -# -# You can set these per environment in config/config.yml: -# -# Environment must be one of: :development, :qa, :sandbox, :production -# -if billing = APP_CONFIG[:billing] - if braintree = billing[:braintree] - Braintree::Configuration.environment = braintree[:environment].downcase.to_sym - Braintree::Configuration.merchant_id = braintree[:merchant_id] - Braintree::Configuration.public_key = braintree[:public_key] - Braintree::Configuration.private_key = braintree[:private_key] - end -end diff --git a/billing/config/locales/en.yml b/billing/config/locales/en.yml deleted file mode 100644 index 1300958..0000000 --- a/billing/config/locales/en.yml +++ /dev/null @@ -1,11 +0,0 @@ -en: - create_new_customer: "Create a new Braintree Customer" - must_create_customer: "You must store a customer in braintree before subscribing to a plan" - subscribe: "Subscribe" - save_customer_info: "Save Customer Information" - donation_not_payment: "Note: This is a donation, and will not be applied towards your account." - no_relevant_subscription: "No subscription which is Active, Pending, or Past Due" - plan: "Plan" - description: "Description" - cost: "Cost" - free: "Free" \ No newline at end of file diff --git a/billing/config/routes.rb b/billing/config/routes.rb deleted file mode 100644 index 7263dff..0000000 --- a/billing/config/routes.rb +++ /dev/null @@ -1,25 +0,0 @@ -Rails.application.routes.draw do - - scope "(:locale)", :locale => MATCH_LOCALE do - match 'payments/new' => 'payments#new', :as => :new_payment - match 'payments/confirm' => 'payments#confirm', :as => :confirm_payment - resources :users do - resources :payments, :only => [:index] - resources :subscriptions, :only => [:index, :show, :destroy] - end - - resources :customer, :only => [:new, :edit] - resources :credit_card_info, :only => [:edit] - - match 'customer/confirm/' => 'customer#confirm', :as => :confirm_customer - match 'customer/show/:id' => 'customer#show', :as => :show_customer - match 'credit_card_info/confirm' => 'credit_card_info#confirm', :as => :confirm_credit_card_info - - resources :subscriptions, :only => [:new, :create, :update] # index, show & destroy are within users path - match 'billing_admin' => 'billing_admin#show', :as => :billing_admin - - #match 'transactions/:product_id/new' => 'transactions#new', :as => :new_transaction - #match 'transactions/confirm/:product_id' => 'transactions#confirm', :as => :confirm_transaction - end - -end diff --git a/billing/leap_web_billing.gemspec b/billing/leap_web_billing.gemspec deleted file mode 100644 index cb0335e..0000000 --- a/billing/leap_web_billing.gemspec +++ /dev/null @@ -1,21 +0,0 @@ -$:.push File.expand_path("../lib", __FILE__) - -require File.expand_path('../../lib/leap_web/version.rb', __FILE__) - -# Describe your gem and declare its dependencies: -Gem::Specification.new do |s| - s.name = "leap_web_billing" - s.version = LeapWeb::VERSION - s.authors = ["Jessib"] - s.email = ["jessib@leap.se"] - s.homepage = "http://www.leap.se" - s.summary = "Billing for LeapWeb" - s.description = "Billing System for a Leap provider" - - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] - s.test_files = Dir["test/**/*"] - - # s.add_dependency "braintree-rails", "~> 0.4.5" - s.add_dependency "braintree" - #s.add_dependency "carmen-rails" -end diff --git a/billing/lib/braintree_test_app.rb b/billing/lib/braintree_test_app.rb deleted file mode 100644 index 41c327d..0000000 --- a/billing/lib/braintree_test_app.rb +++ /dev/null @@ -1,36 +0,0 @@ -# RackTest assumes all requests to be local. -# Braintree requests need to go out to a different server though. -# So we use a middleware to catch these and send them out again. - -class BraintreeTestApp - def initialize(app) - @app = app - end - - def call(env) - @env = env - config = Braintree::Configuration.instantiate - if request.path =~ /\/merchants\/#{config.merchant_id}\/transparent_redirect_requests$/ - #proxy post to braintree - uri = URI.parse(config.protocol + "://" + config.server + ":" + - config.port.to_s + request.path) - http = Net::HTTP.new(uri.host, uri.port) - res = http.post(uri.path, request.body.read) - - if res.code == "303" - header_hash = res.header.to_hash - header_hash["location"].first.gsub!("http://localhost:3000/", "http://www.example.com/") - [303, {"location" => header_hash["location"].first}, ""] - else - raise "unexpected response from Braintree: expected a 303" - end - else - @app.call(env) - end - end - - def request - @request = Rack::Request.new(@env) - end -end - diff --git a/billing/lib/leap_web_billing.rb b/billing/lib/leap_web_billing.rb deleted file mode 100644 index 288d846..0000000 --- a/billing/lib/leap_web_billing.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "leap_web_billing/engine" - -module LeapWebBilling -end diff --git a/billing/lib/leap_web_billing/engine.rb b/billing/lib/leap_web_billing/engine.rb deleted file mode 100644 index ab574f2..0000000 --- a/billing/lib/leap_web_billing/engine.rb +++ /dev/null @@ -1,10 +0,0 @@ -# thou shall require all your dependencies in an engine. -#require "braintree-rails" -require "braintree" -#require "carmen-rails" - -module LeapWebBilling - class Engine < ::Rails::Engine - - end -end diff --git a/billing/script/rails b/billing/script/rails deleted file mode 100755 index 8bd9c0a..0000000 --- a/billing/script/rails +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. - -ENGINE_ROOT = File.expand_path('../..', __FILE__) -ENGINE_PATH = File.expand_path('../../lib/leap_web_billing/engine', __FILE__) - -require 'rails/all' -require 'rails/engine/commands' diff --git a/billing/test/broken/admin_customer_test.rb b/billing/test/broken/admin_customer_test.rb deleted file mode 100644 index df92a0d..0000000 --- a/billing/test/broken/admin_customer_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class AdminCustomerTest < BraintreeIntegrationTest - - setup do - @admin = User.find_by_login('admin') || FactoryGirl.create(:user, login: 'admin') - @user = FactoryGirl.create(:user) - end - - teardown do - @user.destroy if @user - @admin.destroy if @admin - end - - test "check non customer as admin" do - login_as @admin - visit '/' - click_link 'Users' - click_link @user.login - click_link 'Billing Settings' - assert page.has_content? @user.email_address - assert page.has_content? 'No Saved Customer' - end - - test "check customer as admin" do - skip "cannot check customer as admin" - # it would be good to have a test where an admin tries to view the 'Billing Settings' for another user. - # However, partially due to limitations of FakeBraintree, this doesn't seem pursuing at this time. - end -end diff --git a/billing/test/broken/customer_creation_test.rb b/billing/test/broken/customer_creation_test.rb deleted file mode 100644 index 90319a9..0000000 --- a/billing/test/broken/customer_creation_test.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class CustomerCreationTest < BraintreeIntegrationTest - - setup do - @user = FactoryGirl.create(:user) - login_as @user - end - - teardown do - @user.destroy - end - - # Let's test both steps together with capybara - # - # This test is nice and clean but also a bit fragile: - # RackTest assumes all requests to be local. So we need - # BraintreeTestApp for the braintree transparent redirect to work. - # - # this mystifies me why this works. when i type the click_button line (and the - # customer.braintree_customer line) in the debugger, it gives a timeout, - # but it works fine embedded in the test. - test "create customer with braintree" do - visit '/' - click_link 'Billing Settings' - # i am a bit unclear why this works, as it seems there will be validation errors - assert_difference("Customer.count") do - click_button 'Save Payment Info' # this gives me a timeout - end - assert customer = Customer.find_by_user_id(@user.id) - assert customer.braintree_customer - end - - # We only test the confirmation here. - # The request to Braintree is triggered outside of rails - # In skippped test below, we see this works even if the attributes are - # for a broken customer - test "successfully confirms customer creation" do - response = post_transparent_redirect :create_customer_data, - customer: FactoryGirl.attributes_for(:braintree_customer), - redirect_url: confirm_customer_url - - assert_difference("Customer.count") do - post response['Location'] - end - - assert_equal 200, status - assert customer = Customer.find_by_user_id(@user.id) - assert customer.braintree_customer - end - - - test "failed customer creation" do - skip "cannot get customer creation to fail" - - FakeBraintree.decline_all_cards! - - response = post_transparent_redirect :create_customer_data, - customer: FactoryGirl.attributes_for(:broken_customer), - redirect_url: confirm_customer_url - - assert FakeBraintree.decline_all_cards? - assert_no_difference("Customer.count") do - post response['Location'] #this gives me a timeout when run alone - end - assert_nil Customer.find_by_user_id(@user.id) - - end - - def post_transparent_redirect(type, data) - params = data.dup - params[:tr_data] = Braintree::TransparentRedirect.send(type, params) - post_transparent_redirect_params(params) - end - - def post_transparent_redirect_params(params) - uri = URI.parse(Braintree::TransparentRedirect.url) - Net::HTTP.start(uri.host, uri.port) do |http| - http.post(uri.path, Rack::Utils.build_nested_query(params)) - end - end - -end diff --git a/billing/test/broken/subscription_test.rb b/billing/test/broken/subscription_test.rb deleted file mode 100644 index cd010bd..0000000 --- a/billing/test/broken/subscription_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class SubscriptionTest < BraintreeIntegrationTest - include CustomerTestHelper - include StubRecordHelper - - setup do - @admin = User.find_by_login('admin') || FactoryGirl.create(:user, login: 'admin') - @customer = stub_customer - @braintree_customer = @customer.braintree_customer - response = Braintree::Subscription.create plan_id: '5', - payment_method_token: @braintree_customer.credit_cards.first.token, - price: '10' - @subscription = response.subscription - end - - teardown do - @admin.destroy - end - - test "admin can see all subscriptions for another" do - login_as @admin - @customer.stubs(:subscriptions).returns([@subscription]) - @subscription.stubs(:balance).returns 0 - visit user_subscriptions_path(@customer.user_id, :locale => nil) - assert page.has_content?("Subscriptions") - assert page.has_content?("Status: Active") - end - - # test "user cannot see all subscriptions for other user" do - #end - - #test "admin cannot add subscription for another" do - #end - - #test "authenticated user can cancel own subscription" do - #end - - #test "user cannot add subscription if they have active one" do - #end - - #test "user can view own subscriptions" - #end - - #test "admin can view another user's subscriptions" do - #end - -end diff --git a/billing/test/factories.rb b/billing/test/factories.rb deleted file mode 100644 index 87543b2..0000000 --- a/billing/test/factories.rb +++ /dev/null @@ -1,25 +0,0 @@ -FactoryGirl.define do - - TEST_CC_NUMBER = %w(4111 1111 1111 1111).join - - factory :customer do - user - - factory :customer_with_payment_info do - braintree_customer - end - end - - factory :braintree_customer, class: Braintree::Customer do - first_name 'Big' - last_name 'Spender' - credit_card number: TEST_CC_NUMBER, expiration_date: '04/2016' - initialize_with { Braintree::Customer.create(attributes).customer } - skip_create - - factory :broken_customer do - credit_card number: '123456', expiration_date: '04/2016' - end - end - -end diff --git a/billing/test/functional/customer_controller_test.rb b/billing/test/functional/customer_controller_test.rb deleted file mode 100644 index d943e23..0000000 --- a/billing/test/functional/customer_controller_test.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class CustomerControllerTest < ActionController::TestCase - include CustomerTestHelper - - test "new assigns redirect url" do - login - get :new - - assert_response :success - assert assigns(:tr_data) - tr_data = Braintree::Util.parse_query_string(assigns(:tr_data)) - assert_equal confirm_customer_url, tr_data[:redirect_url] - end - - test "new requires login" do - get :new - - assert_response :redirect - assert_redirected_to login_path - end - - test "edit uses params[:id]" do - customer = stub_customer - login customer.user - get :edit, id: customer.user.id - - assert_response :success - assert assigns(:tr_data) - tr_data = Braintree::Util.parse_query_string(assigns(:tr_data)) - assert_equal customer.braintree_customer_id, tr_data[:customer_id] - assert_equal confirm_customer_url, tr_data[:redirect_url] - end - - test "confirm customer creation" do - login - Braintree::TransparentRedirect.expects(:confirm).returns(success_response) - # to_confirm = prepare_confirmation :create_customer_data, - # customer: FactoryGirl.attributes_for(:braintree_customer), - # redirect_url: confirm_customer_url - - assert_difference("Customer.count") do - post :confirm, braintree: :query - end - - assert_response :success - assert result = assigns(:result) - assert result.success? - assert result.customer.id - end - - test "customer update" do - customer = stub_customer - customer.expects(:save) - login customer.user - Braintree::TransparentRedirect.expects(:confirm). - returns(success_response(customer)) - - assert_no_difference("Customer.count") do - post :confirm, query: :from_braintree - end - - assert_response :success - assert result = assigns(:result) - assert result.success? - assert_equal customer.braintree_customer, result.customer - end - - test "failed customer creation" do - skip "can't get customer creation to fail" - login - FakeBraintree.decline_all_cards! - # what is prepare_confirmation ?? this method isn't found - to_confirm = prepare_confirmation :create_customer_data, - customer: FactoryGirl.attributes_for(:broken_customer), - redirect_url: confirm_customer_url - post :confirm, to_confirm - - FakeBraintree.clear! - assert_response :success - assert result = assigns(:result) - assert !result.success? - end - - test "failed customer creation with stubbing" do - login - Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) - post :confirm, bla: :blub - - assert_response :success - assert_template :new - end - - test "failed customer update with stubbing" do - customer = stub_customer - login customer.user - Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) - post :confirm, bla: :blub - - assert_response :success - assert_template :edit - end - - def failure_response - stub success?: false, - errors: stub(for: nil, size: 0), - params: {} - end - - def success_response(customer = nil) - stub success?: true, - customer: braintree_customer(customer) - end - - def braintree_customer(customer) - if customer - customer.braintree_customer - else - FactoryGirl.build :braintree_customer - end - end - -end diff --git a/billing/test/functional/customers_controller_test.rb b/billing/test/functional/customers_controller_test.rb deleted file mode 100644 index 46c33c9..0000000 --- a/billing/test/functional/customers_controller_test.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class CustomersControllerTest < ActionController::TestCase - tests CustomerController - - setup do - @user = FactoryGirl.create :user - @other_user = FactoryGirl.create :user - #FakeBraintree.clear! - #FakeBraintree.verify_all_cards! - testid = 'testid' - #this wasn't actually being used - #FakeBraintree::Customer.new({:credit_cards => [{:number=>"5105105105105100", :expiration_date=>"05/2013"}]}, {:id => testid, :merchant_id => Braintree::Configuration.merchant_id}) - # any reason to call the create instance method on the FakeBraintree::Customer ? - @customer = Customer.new(:user_id => @other_user.id) - @customer.braintree_customer_id = testid - @customer.save - - end - - teardown do - @user.destroy - @other_user.destroy - @customer.destroy - end - - test "no access if not logged in" do - get :new - assert_access_denied(true, false) - get :show, :id => @customer.braintree_customer_id - assert_access_denied(true, false) - get :edit, :id => @customer.braintree_customer_id - assert_access_denied(true, false) - end - - - test "should get new if logged in and not customer" do - login @user - get :new - assert_not_nil assigns(:tr_data) - assert_response :success - end - - test "new should direct edit if user is already a customer" do - login @other_user - get :new - assert_response :redirect - assert_equal edit_customer_url(@customer.user), response.header['Location'] - end - - - test "show" do - skip "show customer" - login @other_user - # Below will fail, as when we go to fetch the customer data, Braintree::Customer.find(params[:id]) won't find the customer as it is a FakeBraintree customer. - #get :show, :id => @customer.braintree_customer_id - - end - -end diff --git a/billing/test/functional/payments_controller_test.rb b/billing/test/functional/payments_controller_test.rb deleted file mode 100644 index 90b7582..0000000 --- a/billing/test/functional/payments_controller_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class PaymentsControllerTest < ActionController::TestCase - include CustomerTestHelper - - test "payment when unauthorized" do - get :new - assert_not_nil assigns(:tr_data) - assert_response :success - end - - test "successful confirmation renders confirm" do - Braintree::TransparentRedirect.expects(:confirm).returns(success_response) - get :confirm - - assert_response :success - assert_template :confirm - end - - test "failed confirmation renders new" do - Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) - get :confirm - - assert_response :success - assert_not_nil assigns(:tr_data) - assert_template :new - end - - def failure_response - stub success?: false, - errors: stub(for: nil, size: 0), - params: {}, - transaction: stub(status: nil) - end - - def success_response - stub success?: true, - transaction: stub_transaction - end - - # that's what you get when not following the law of demeter... - def stub_transaction - stub amount: "100.00", - id: "ASDF", - customer_details: FactoryGirl.build(:braintree_customer), - credit_card_details: FactoryGirl.build(:braintree_customer).credit_cards.first - end - -end diff --git a/billing/test/functional/subscriptions_controller_test.rb b/billing/test/functional/subscriptions_controller_test.rb deleted file mode 100644 index a6a1057..0000000 --- a/billing/test/functional/subscriptions_controller_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class SubscriptionsControllerTest < ActionController::TestCase - include CustomerTestHelper - - test "destroy cancels subscription" do - customer = stub_customer - login customer.user - result = Braintree::Subscription.create plan_id: 'my_plan', - payment_method_token: customer.braintree_customer.credit_cards.first.token - subscription = result.subscription - delete :destroy, id: subscription.id, user_id: customer.user.id - assert_equal "Canceled", Braintree::Subscription.find(subscription.id).status - end -end diff --git a/billing/test/support/braintree_integration_test.rb b/billing/test/support/braintree_integration_test.rb deleted file mode 100644 index 976c5a2..0000000 --- a/billing/test/support/braintree_integration_test.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'capybara/rails' -# require 'fake_braintree' - messes up other integration tests -require 'braintree_test_app' - -class BraintreeIntegrationTest < BrowserIntegrationTest - include Warden::Test::Helpers - - setup do - Warden.test_mode! - Rails.application.config.middleware.use BraintreeTestApp - end - - teardown do - Warden.test_reset! - Rails.application.config.middleware.delete "BraintreeTestApp" - end - -end diff --git a/billing/test/support/customer_test_helper.rb b/billing/test/support/customer_test_helper.rb deleted file mode 100644 index adac00a..0000000 --- a/billing/test/support/customer_test_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module CustomerTestHelper - - def stub_customer(user = nil) - user ||= find_record :user - customer = stub_record :customer_with_payment_info, - user: user, - user_id: user.id - Customer.stubs(:find_by_user_id).with(user.id).returns(customer) - return customer - end -end diff --git a/billing/test/test_helper.rb b/billing/test/test_helper.rb deleted file mode 100644 index 1e26a31..0000000 --- a/billing/test/test_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -# Configure Rails Environment -ENV["RAILS_ENV"] = "test" - -require File.expand_path("../dummy/config/environment.rb", __FILE__) -require "rails/test_help" - -Rails.backtrace_cleaner.remove_silencers! - -# Load support files -Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } - -# Load fixtures from the engine -if ActiveSupport::TestCase.method_defined?(:fixture_path=) - ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) -end diff --git a/billing/test/unit/customer_test.rb b/billing/test/unit/customer_test.rb deleted file mode 100644 index 6156f87..0000000 --- a/billing/test/unit/customer_test.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'test_helper' - -class CustomerTest < ActiveSupport::TestCase - include StubRecordHelper - - setup do - @user = find_record :user - @customer = FactoryGirl.build(:customer, user: @user) - end - - test "test set of attributes should be valid" do - @customer.valid? - assert_equal Hash.new, @customer.errors.messages - end - - test "customer belongs to user" do - assert_equal User, @customer.user.class - end - - test "user validation" do - @customer.user = nil - assert !@customer.valid? - end - - test "has no payment info" do - assert !@customer.braintree_customer_id - assert !@customer.has_payment_info? - end - - test "with no braintree data" do - assert_equal @customer, @customer.with_braintree_data! - end - - test "without default credit card" do - assert_nil @customer.default_credit_card - end - -end diff --git a/billing/test/unit/customer_with_payment_info_test.rb b/billing/test/unit/customer_with_payment_info_test.rb deleted file mode 100644 index 0589a59..0000000 --- a/billing/test/unit/customer_with_payment_info_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class CustomerWithPaymentInfoTest < ActiveSupport::TestCase - include StubRecordHelper - - setup do - @user = find_record :user - @customer = FactoryGirl.build(:customer_with_payment_info, user: @user) - end - - test "has payment_info" do - assert @customer.braintree_customer_id - assert @customer.has_payment_info? - end - - test "constructs customer with braintree data" do - @customer.with_braintree_data! - assert_equal 'Big', @customer.first_name - assert_equal 'Spender', @customer.last_name - assert_equal 1, @customer.credit_cards.size - assert_equal Hash.new, @customer.custom_fields - end - - test "can access braintree_customer after reload" do - @customer.save - @customer = Customer.find_by_user_id(@customer.user_id) - @customer.with_braintree_data! - assert_equal 'Big', @customer.first_name - assert_equal 'Spender', @customer.last_name - assert_equal 1, @customer.credit_cards.size - assert_equal Hash.new, @customer.custom_fields - @customer.destroy - end - - test "sets default_credit_card" do - @customer.with_braintree_data! - assert_equal @customer.credit_cards.first, @customer.default_credit_card - end -end diff --git a/engines/billing/Gemfile b/engines/billing/Gemfile new file mode 100644 index 0000000..30e9669 --- /dev/null +++ b/engines/billing/Gemfile @@ -0,0 +1,23 @@ +source "https://rubygems.org" + +eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) +eval(File.read(File.dirname(__FILE__) + '/../ui_dependencies.rb')) + +# We require leap_web_core from here so we can use the path option. +gem "leap_web_core", :path => '../core' + +# Declare your gem's dependencies in billing.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec + +# jquery-rails is used by the dummy application +#gem "jquery-rails" + +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. + +# To use debugger +# gem 'debugger' diff --git a/engines/billing/README.md b/engines/billing/README.md new file mode 100644 index 0000000..3ef6153 --- /dev/null +++ b/engines/billing/README.md @@ -0,0 +1,49 @@ +Billing Engine +==================== + +Currently, this engine support billing via Braintree. More backends to come later. + +Configuration +---------------------------------- + +Start with a sandbox account, which you can get here: https://www.braintreepayments.com/get-started + +Once you have registered for the sandbox, logging in will show you three important variables you will need to configure: + +* merchantId +* publicKey +* privatekey + +To configure the billing engine, edit `config/config.yaml` like so: + + production: (or "development", as you prefer) + billing: + braintree: + environment: sandbox + merchant_id: Ohp2aijaaqu6oJ4w + public_key: ahnar0UwLahwe6Ce + private_key: aemie2Geohgah2EaOad9DeeruW4Iegh4 + +If deploying via puppet, the same data in webapp.json would like this: + + "billing": { + "braintree": { + "environment": "sandbox", + "merchant_id": "Ohp2aijaaqu6oJ4w", + "public_key": "ahnar0UwLahwe6Ce", + "private_key": "aemie2Geohgah2EaOad9DeeruW4Iegh4" + } + } + +Now, you should be able to add charges to your own sandbox when you run the webapp. + +The acceptable values for `billing.braintree.environment` are: `development`, `qa`, `sandbox`, or `production`. + +Plans +-------------------------------- + +You also will want to add a Plan to your Sandbox. Within the Braintree Sandbox, navigate to 'Recurring Billing' -> 'Plans'. From here, you can add a new Plan. The values of the test plan are not important, but the ID will be displayed, so should pick something descriptive. + +Here are credit cared numbers to try in the Sandbox: + +https://www.braintreepayments.com/docs/ruby/reference/sandbox \ No newline at end of file diff --git a/engines/billing/Rakefile b/engines/billing/Rakefile new file mode 100644 index 0000000..52929c4 --- /dev/null +++ b/engines/billing/Rakefile @@ -0,0 +1,40 @@ +#!/usr/bin/env rake +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end +begin + require 'rdoc/task' +rescue LoadError + require 'rdoc/rdoc' + require 'rake/rdoctask' + RDoc::Task = Rake::RDocTask +end + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'LeapWebBilling' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +spec = eval(File.read('leap_web_billing.gemspec')) +Gem::PackageTask.new(spec) do |p| + p.gem_spec = spec +end + +Bundler::GemHelper.install_tasks + +require 'rake/testtask' + +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = false +end + + +task :default => :test diff --git a/engines/billing/app/controllers/billing_admin_controller.rb b/engines/billing/app/controllers/billing_admin_controller.rb new file mode 100644 index 0000000..e11d4ee --- /dev/null +++ b/engines/billing/app/controllers/billing_admin_controller.rb @@ -0,0 +1,29 @@ +class BillingAdminController < BillingBaseController + before_filter :require_admin + + def show + + br_atleast_90_days = Braintree::Subscription.search do |search| + search.days_past_due >= 90 + end + @past_due_atleast_90_days = braintree_resource_collection_to_array(br_atleast_90_days) + + br_all_past_due = Braintree::Subscription.search do |search| + search.status.is Braintree::Subscription::Status::PastDue + #cannot search by balance. + end + @all_past_due = braintree_resource_collection_to_array(br_all_past_due) + + end + + private + + def braintree_resource_collection_to_array(braintree_resource_collection) + array = [] + braintree_resource_collection.each do |object| + array << object + end + array + end + +end diff --git a/engines/billing/app/controllers/billing_base_controller.rb b/engines/billing/app/controllers/billing_base_controller.rb new file mode 100644 index 0000000..0453677 --- /dev/null +++ b/engines/billing/app/controllers/billing_base_controller.rb @@ -0,0 +1,22 @@ +class BillingBaseController < ApplicationController + before_filter :assign_user + + helper 'billing' + + # required for navigation to work. + def assign_user + if params[:user_id] + @user = User.find(params[:user_id]) + elsif params[:action] == "confirm"# confirms will come back with different ID set, so check for this first + # This is only for cases where an admin cannot apply action for customer, but should be all confirms + @user = current_user + elsif params[:id] + @user = User.find(params[:id]) + else + # TODO + # hacky, what are cases where @user hasn't yet been set? certainly some cases with subscriptions and payments + @user = current_user + end + end + +end diff --git a/engines/billing/app/controllers/credit_card_info_controller.rb b/engines/billing/app/controllers/credit_card_info_controller.rb new file mode 100644 index 0000000..fbaa6f1 --- /dev/null +++ b/engines/billing/app/controllers/credit_card_info_controller.rb @@ -0,0 +1,35 @@ +class CreditCardInfoController < ApplicationController + before_filter :require_login, :set_user + + def edit + @credit_card = Braintree::CreditCard.find(params[:id]) + customer = Customer.find_by_user_id(@user.id) + if customer and customer.braintree_customer_id == @credit_card.customer_id + @tr_data = Braintree::TransparentRedirect. + update_credit_card_data(:redirect_url => confirm_credit_card_info_url, + :payment_method_token => @credit_card.token) + else + access_denied + end + + end + + def confirm + @result = Braintree::TransparentRedirect.confirm(request.query_string) + if @result.success? + render :action => "confirm" + else + @credit_card = Braintree::CreditCard.find(@result.params[:payment_method_token]) + render :action => "edit" + end + end + + + private + + def set_user + # this assumes anybody, even an admin, will not access for another user. + @user = current_user + end + +end diff --git a/engines/billing/app/controllers/customer_controller.rb b/engines/billing/app/controllers/customer_controller.rb new file mode 100644 index 0000000..6cbcb44 --- /dev/null +++ b/engines/billing/app/controllers/customer_controller.rb @@ -0,0 +1,64 @@ +class CustomerController < BillingBaseController + before_filter :require_login, :fetch_customer + + def show + if @customer + @customer.with_braintree_data! + @default_cc = @customer.default_credit_card + @active_subscription = @customer.subscriptions + @transactions = @customer.braintree_customer.transactions + end + end + + def new + if @customer.has_payment_info? + redirect_to edit_customer_path(@user), :notice => 'Here is your saved customer data' + else + fetch_new_transparent_redirect_data + end + end + + def edit + fetch_edit_transparent_redirect_data + end + + def confirm + @result = Braintree::TransparentRedirect.confirm(request.query_string) + if @result.success? + @customer.braintree_customer = @result.customer + @customer.save + render :action => "confirm" + elsif @customer.has_payment_info? + fetch_edit_transparent_redirect_data + render :action => "edit" + else + fetch_new_transparent_redirect_data + render :action => "new" + end + end + + protected + + def fetch_new_transparent_redirect_data + access_denied unless @user == current_user # admins cannot do this for others + @tr_data = Braintree::TransparentRedirect. + create_customer_data(:redirect_url => confirm_customer_url) + end + + def fetch_edit_transparent_redirect_data + access_denied unless @user == current_user # admins cannot do this for others + @customer.with_braintree_data! + @default_cc = @customer.default_credit_card + @tr_data = Braintree::TransparentRedirect. + update_customer_data(:redirect_url => confirm_customer_url, + :customer_id => @customer.braintree_customer_id) ##?? + end + + def fetch_customer + @customer = Customer.find_by_user_id(@user.id) + if @user == current_user + @customer ||= Customer.new(user: @user) + end + access_denied unless (@customer and (@customer.user == current_user)) or admin? + end +end diff --git a/engines/billing/app/controllers/payments_controller.rb b/engines/billing/app/controllers/payments_controller.rb new file mode 100644 index 0000000..fce6570 --- /dev/null +++ b/engines/billing/app/controllers/payments_controller.rb @@ -0,0 +1,34 @@ +class PaymentsController < BillingBaseController + before_filter :require_login, :only => [:index] + + def new + fetch_transparent_redirect + end + + def confirm + @result = Braintree::TransparentRedirect.confirm(request.query_string) + if @result.success? + render :action => "confirm" + else + fetch_transparent_redirect + render :action => "new" + end + end + + def index + access_denied unless admin? or (@user == current_user) + customer = Customer.find_by_user_id(@user.id) + braintree_data = Braintree::Customer.find(customer.braintree_customer_id) + # these will be ordered by created_at descending, per http://stackoverflow.com/questions/16425475/ + @transactions = braintree_data.transactions + end + + protected + + + def fetch_transparent_redirect + @tr_data = Braintree::TransparentRedirect.transaction_data redirect_url: confirm_payment_url, + transaction: { type: "sale", options: {submit_for_settlement: true } } + end + +end diff --git a/engines/billing/app/controllers/subscriptions_controller.rb b/engines/billing/app/controllers/subscriptions_controller.rb new file mode 100644 index 0000000..f066b3c --- /dev/null +++ b/engines/billing/app/controllers/subscriptions_controller.rb @@ -0,0 +1,63 @@ +class SubscriptionsController < BillingBaseController + before_filter :require_login + before_filter :fetch_subscription, :only => [:show, :destroy] + before_filter :confirm_cancel_subscription, :only => [:destroy] + before_filter :confirm_self_or_admin, :only => [:index] + before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] + # for now, admins cannot create or destroy subscriptions for others: + before_filter :confirm_self, :only => [:new, :create] + + def new + # don't show link to subscribe if they are already subscribed? + credit_card = @customer.default_credit_card #safe to assume default? + @payment_method_token = credit_card.token + @plans = Braintree::Plan.all + end + + # show has no content, so not needed at this point. + + def create + @result = Braintree::Subscription.create( :payment_method_token => params[:payment_method_token], :plan_id => params[:plan_id] ) + #if you want to test pastdue, can add :price => '2001', :trial_period => true,:trial_duration => 1,:trial_duration_unit => "day" and then wait a day + end + + def destroy + @result = Braintree::Subscription.cancel params[:id] + end + + def index + customer = Customer.find_by_user_id(@user.id) + @subscriptions = customer.subscriptions(nil, false) + end + + private + + def fetch_subscription + @subscription = Braintree::Subscription.find params[:id] + @credit_card = Braintree::CreditCard.find @subscription.payment_method_token + @subscription_customer_id = @credit_card.customer_id + current_user_customer = Customer.find_by_user_id(current_user.id) + access_denied unless admin? or (current_user_customer and current_user_customer.braintree_customer_id == @subscription_customer_id) + + end + + def confirm_cancel_subscription + access_denied unless view_context.allow_cancel_subscription(@subscription) + end + + def confirm_no_pending_active_pastdue_subscription + @customer = Customer.find_by_user_id(@user.id) + if subscription = @customer.subscriptions # will return pending, active or pastdue subscription, if it exists + redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription' + end + end + + def confirm_self + @user == current_user + end + + def confirm_self_or_admin + access_denied unless confirm_self or admin? + end + +end diff --git a/engines/billing/app/helpers/billing_helper.rb b/engines/billing/app/helpers/billing_helper.rb new file mode 100644 index 0000000..b9e5e2e --- /dev/null +++ b/engines/billing/app/helpers/billing_helper.rb @@ -0,0 +1,51 @@ +module BillingHelper + + def braintree_form_for(object, options = {}, &block) + options.reverse_merge! params: @result && @result.params[object], + errors: @result && @result.errors.for(object), + builder: BraintreeFormHelper::BraintreeFormBuilder, + url: Braintree::TransparentRedirect.url + + form_for object, options, &block + end + + def billing_top_link(user) + # for admins, top link will show special admin information, which has link to show their own customer information + if (admin? and user == current_user) + billing_admin_path + else + show_or_new_customer_link(user) + end + end + + def show_or_new_customer_link(user) + # Link to show if user is admin viewing another user, or user is already a customer. + # Otherwise link to create a new customer. + if (admin? and (user != current_user)) or ((customer = Customer.find_by_user_id(user.id)) and customer.has_payment_info?) + show_customer_path(user) + else + new_customer_path + end + end + + # a bit strange to put here, but we don't have a subscription model + def user_for_subscription(subscription) + + if (transaction = subscription.transactions.first) + # much quicker, but will only work if there is already a transaction associated with subscription (should generally be) + braintree_customer_id = transaction.customer_details.id + else + credit_card = Braintree::CreditCard.find(subscription.payment_method_token) + braintree_customer_id = credit_card.customer_id + end + + customer = Customer.find_by_braintree_customer_id(braintree_customer_id) + user = User.find(customer.user_id) + + end + + def allow_cancel_subscription(subscription) + ['Active', 'Pending'].include? subscription.status or (admin? and subscription.status == 'Past Due') + end + +end diff --git a/engines/billing/app/helpers/braintree_form_helper.rb b/engines/billing/app/helpers/braintree_form_helper.rb new file mode 100644 index 0000000..cb322fa --- /dev/null +++ b/engines/billing/app/helpers/braintree_form_helper.rb @@ -0,0 +1,64 @@ +module BraintreeFormHelper + class BraintreeFormBuilder < ActionView::Helpers::FormBuilder + include ActionView::Helpers::AssetTagHelper + include ActionView::Helpers::TagHelper + + def initialize(object_name, object, template, options, proc) + super + @braintree_params = @options[:params] + @braintree_errors = @options[:errors] + @braintree_existing = @options[:existing] + end + + def fields_for(record_name, *args, &block) + options = args.extract_options! + options[:builder] = BraintreeFormBuilder + options[:params] = @braintree_params && @braintree_params[record_name] + options[:errors] = @braintree_errors && @braintree_errors.for(record_name) + new_args = args + [options] + super record_name, *new_args, &block + end + + def text_field(method, options = {}) + has_errors = @braintree_errors && @braintree_errors.on(method).any? + field = super(method, options.merge(:value => determine_value(method))) + result = content_tag("div", field, :class => has_errors ? "fieldWithErrors" : "") + result.safe_concat validation_errors(method) + result + end + + protected + + def determine_value(method) + if @braintree_params + @braintree_params[method] + elsif @braintree_existing + + if @braintree_existing.kind_of?(Braintree::CreditCard) + + case method + when :number + method = :masked_number + when :cvv + return nil + end + end + + @braintree_existing.send(method) + else + nil + end + end + + def validation_errors(method) + if @braintree_errors && @braintree_errors.on(method).any? + @braintree_errors.on(method).map do |error| + content_tag("div", ERB::Util.h(error.message), {:style => "color: red;"}) + end.join + else + "" + end + end + end +end + diff --git a/engines/billing/app/helpers/braintree_helper.rb b/engines/billing/app/helpers/braintree_helper.rb new file mode 100644 index 0000000..2d18b6c --- /dev/null +++ b/engines/billing/app/helpers/braintree_helper.rb @@ -0,0 +1,5 @@ +module BraintreeHelper + + +end + diff --git a/engines/billing/app/models/customer.rb b/engines/billing/app/models/customer.rb new file mode 100644 index 0000000..1acc7a5 --- /dev/null +++ b/engines/billing/app/models/customer.rb @@ -0,0 +1,58 @@ +class Customer < CouchRest::Model::Base + + FIELDS = [:first_name, :last_name, :phone, :website, :company, :fax, :addresses, :credit_cards, :custom_fields] + attr_accessor *FIELDS + + use_database "customers" + belongs_to :user + belongs_to :braintree_customer + + # Braintree::Customer - stored on braintrees servers - we only have the id. + def braintree_customer + @braintree_customer ||= Braintree::Customer.find(braintree_customer_id) + end + + validates :user, presence: true + + design do + view :by_user_id + view :by_braintree_customer_id + end + + def has_payment_info? + !!braintree_customer_id + end + + # from braintree_ruby_examples/rails3_tr_devise and should be tweaked + def with_braintree_data! + return self unless has_payment_info? + + FIELDS.each do |field| + send(:"#{field}=", braintree_customer.send(field)) + end + self + end + + def default_credit_card + return unless has_payment_info? + + credit_cards.find { |cc| cc.default? } + end + + # based on 2nd parameter, either returns the single active subscription (or nil if there isn't one), or an array of all subsciptions + def subscriptions(braintree_data=nil, only_pending_active_pastdue=true) + self.with_braintree_data! + return unless has_payment_info? + + subscriptions = [] + self.default_credit_card.subscriptions.each do |sub| + if only_pending_active_pastdue and ['Pending', 'Active','Past Due'].include? sub.status + return sub + else + subscriptions << sub + end + end + only_pending_active_pastdue ? nil : subscriptions + end + +end diff --git a/engines/billing/app/views/billing_admin/show.html.haml b/engines/billing/app/views/billing_admin/show.html.haml new file mode 100644 index 0000000..0382cf0 --- /dev/null +++ b/engines/billing/app/views/billing_admin/show.html.haml @@ -0,0 +1,7 @@ +%legend= t(:more_than_90_days_past_due) += render(:partial => "subscriptions/subscription_details", :collection => @past_due_atleast_90_days, :as => 'subscription', :locals => {:show_user => true}) || t(:none) +%legend= t(:all_past_due) += render(:partial => "subscriptions/subscription_details", :collection => @all_past_due, :as => 'subscription', :locals => {:show_user => true}) || t(:none) + +%legend= t(:your_settings) += link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file diff --git a/engines/billing/app/views/credit_card_info/confirm.html.haml b/engines/billing/app/views/credit_card_info/confirm.html.haml new file mode 100644 index 0000000..9dd8176 --- /dev/null +++ b/engines/billing/app/views/credit_card_info/confirm.html.haml @@ -0,0 +1,5 @@ +%h1 Payment Info Confirmation +%p Your payment information was successfully saved. +%dl + %dt Credit Card + %dd= @result.credit_card.masked_number diff --git a/engines/billing/app/views/credit_card_info/edit.html.haml b/engines/billing/app/views/credit_card_info/edit.html.haml new file mode 100644 index 0000000..bd86a4c --- /dev/null +++ b/engines/billing/app/views/credit_card_info/edit.html.haml @@ -0,0 +1,17 @@ +%h1 Change Credit Card +- if @result + #total-errors{:style => "color:red;"} + = h(@result.errors.size) + error(s) += braintree_form_for :credit_card, :existing => @credit_card do |f| + = field_set_tag "Credit Card" do + %dl + %dt= f.label :number, 'Number' + %dd= f.text_field :number + %dt= f.label :expiration_date, 'Expiration Date (MM/YY)' + %dd= f.text_field :expiration_date + %dt= f.label :cvv, 'CVV' + %dd= f.text_field :cvv + = hidden_field_tag :tr_data, @tr_data + = f.submit 'Save Payment Info', :class => :btn + = link_to t(:cancel), edit_customer_path(@user.id), :class => :btn diff --git a/engines/billing/app/views/customer/_customer_data.html.haml b/engines/billing/app/views/customer/_customer_data.html.haml new file mode 100644 index 0000000..e9df040 --- /dev/null +++ b/engines/billing/app/views/customer/_customer_data.html.haml @@ -0,0 +1,16 @@ +%legend= t(:customer_information) +%dl + %dt First Name + %dd= @customer.first_name + %dt Last Name + %dd= @customer.last_name + %dt Phone + %dd= @customer.phone +%legend= t(:credit_card_information) +%dl + %dt Number + %dd= @default_cc.masked_number + %dt Expiration Date + %dd= @default_cc.expiration_date + - if current_user == @user + = link_to t(:edit_saved_data), edit_customer_path(@user.id), :class => :btn diff --git a/engines/billing/app/views/customer/_transaction.html.haml b/engines/billing/app/views/customer/_transaction.html.haml new file mode 100644 index 0000000..e69de29 diff --git a/engines/billing/app/views/customer/confirm.html.haml b/engines/billing/app/views/customer/confirm.html.haml new file mode 100644 index 0000000..877a8ac --- /dev/null +++ b/engines/billing/app/views/customer/confirm.html.haml @@ -0,0 +1,14 @@ +%h1 Payment Info Confirmation +%p Your payment information was successfully saved. +%dl + %dt First Name + %dd= @result.customer.first_name + %dt Last Name + %dd= @result.customer.last_name + %dt Phone + %dd= @result.customer.phone + %dt Credit Card + - @result.customer.credit_cards.each do |cc| + %dd= cc.masked_number +- customer = Customer.find_by_user_id(@user.id) += link_to 'View Customer Info', show_customer_path(@user.id), :class=> :btn \ No newline at end of file diff --git a/engines/billing/app/views/customer/edit.html.haml b/engines/billing/app/views/customer/edit.html.haml new file mode 100644 index 0000000..e882d53 --- /dev/null +++ b/engines/billing/app/views/customer/edit.html.haml @@ -0,0 +1,23 @@ +- if @result + #total-errors{:style => "color:red;"} + = h(@result.errors.size) + error(s) += braintree_form_for :customer, existing: @customer do |f| + = field_set_tag "Customer" do + %dl + %dt= f.label :first_name, 'First Name' + %dd= f.text_field :first_name + %dt= f.label :last_name, 'Last Name' + %dd= f.text_field :last_name + %dt= f.label :phone, 'Phone' + %dd= f.text_field :phone + - if @default_cc + = # todo, as they will need a credit card, so not sure about conditional? + %dt= t(:stored_credit_card) + %dd + = @default_cc.masked_number + = link_to t(:change_credit_card), edit_credit_card_info_path(:id => @default_cc.token), :class => :btn + = hidden_field_tag :tr_data, @tr_data + .form-actions + = f.submit t(:save_customer_info), :class => 'btn btn-primary' + = link_to t(:cancel), show_customer_path(@user), :class=> :btn diff --git a/engines/billing/app/views/customer/new.html.haml b/engines/billing/app/views/customer/new.html.haml new file mode 100644 index 0000000..e1f5ba9 --- /dev/null +++ b/engines/billing/app/views/customer/new.html.haml @@ -0,0 +1,24 @@ +- if @result + #total-errors{:style => "color:red;"} + = h(@result.errors.size) + error(s) += braintree_form_for :customer do |f| + = field_set_tag "Customer" do + %dl + %dt= f.label :first_name, 'First Name' + %dd= f.text_field :first_name + %dt= f.label :last_name, 'Last Name' + %dd= f.text_field :last_name + %dt= f.label :phone, 'Phone' + %dd= f.text_field :phone + = field_set_tag "Credit Card" do + - f.fields_for :credit_card do |cc| + %dl + %dt= cc.label :number, 'Number' + %dd= cc.text_field :number + %dt= cc.label :expiration_date, 'Expiration Date (MM/YY)' + %dd= cc.text_field :expiration_date + %dt= cc.label :cvv, 'CVV' + %dd= cc.text_field :cvv + = hidden_field_tag :tr_data, @tr_data + = f.submit 'Save Payment Info' diff --git a/engines/billing/app/views/customer/show.html.haml b/engines/billing/app/views/customer/show.html.haml new file mode 100644 index 0000000..ec1779c --- /dev/null +++ b/engines/billing/app/views/customer/show.html.haml @@ -0,0 +1,27 @@ +- if admin? and !@customer + = t(:no_saved_customer) +- else + = render :partial => 'customer_data' + %legend= t(:last_three_transactions) + - counter = 0 + = # these will be ordered with most recently created first, per http://stackoverflow.com/questions/16425475/ + - @transactions.each do |t| + - break if counter > 2 # not ruby-like, but object is a Braintree::ResourceCollection so limited methods available + = render :partial => "payments/transaction_details", :locals => {:transaction => t} + - counter += 1 + = link_to t(:transaction_history), user_payments_path(@user) + %legend= t(:subscriptions) + - if @active_subscription + = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription} + - else + %p + = t(:no_relevant_subscription) + - if current_user == @user + %p + .form-actions + = link_to t(:subscribe_to_plan), new_subscription_path, :class => :btn + %p + = link_to t(:all_subscriptions), user_subscriptions_path(@user) + +.form-actions + = link_to t(:make_donation), new_payment_path, :class => 'btn btn-primary' diff --git a/engines/billing/app/views/payments/_non_customer_fields.html.haml b/engines/billing/app/views/payments/_non_customer_fields.html.haml new file mode 100644 index 0000000..77cfe95 --- /dev/null +++ b/engines/billing/app/views/payments/_non_customer_fields.html.haml @@ -0,0 +1,16 @@ += field_set_tag "Personal Information" do + = f.fields_for :customer do |c| + %div= c.label :first_name, "First Name" + %div= c.text_field :first_name + %div= c.label :last_name, "Last Name" + %div= c.text_field :last_name + %div= c.label :email, "Email" + %div= c.text_field :email += field_set_tag "Credit Card" do + = f.fields_for :credit_card do |c| + %div= c.label :number, "Number" + %div= c.text_field :number + %div= c.label :expiration_date, "Expiration Date (MM/YY)" + %div= c.text_field :expiration_date + %div= c.label :cvv, "CVV" + %div= c.text_field :cvv \ No newline at end of file diff --git a/engines/billing/app/views/payments/_transaction_details.html.haml b/engines/billing/app/views/payments/_transaction_details.html.haml new file mode 100644 index 0000000..85e4f6a --- /dev/null +++ b/engines/billing/app/views/payments/_transaction_details.html.haml @@ -0,0 +1,15 @@ +%p + = transaction.id + Type: + = transaction.type + Amount: + = number_to_currency(transaction.amount) + Status: + = transaction.status + Date + = transaction.created_at.strftime("%Y-%m-%d") + - if sub_start = transaction.subscription_details.billing_period_start_date + From subscription which started + = sub_start + - else # should not have any of these + Not paid as part of subscription \ No newline at end of file diff --git a/engines/billing/app/views/payments/confirm.html.haml b/engines/billing/app/views/payments/confirm.html.haml new file mode 100644 index 0000000..45af3c9 --- /dev/null +++ b/engines/billing/app/views/payments/confirm.html.haml @@ -0,0 +1,26 @@ +%h1 Payment Result +%div Thank you for your donation. +%h2 Transaction Details +%table + %tr + %td Amount + %td + $#{@result.transaction.amount} + %tr + %td Transaction ID: + %td= @result.transaction.id + %tr + %td First Name: + %td= h @result.transaction.customer_details.first_name + %tr + %td Last Name: + %td= h @result.transaction.customer_details.last_name + %tr + %td Email: + %td= h @result.transaction.customer_details.email + %tr + %td Credit Card: + %td= h @result.transaction.credit_card_details.masked_number + %tr + %td Card Type: + %td= h @result.transaction.credit_card_details.card_type \ No newline at end of file diff --git a/engines/billing/app/views/payments/index.html.haml b/engines/billing/app/views/payments/index.html.haml new file mode 100644 index 0000000..7a89917 --- /dev/null +++ b/engines/billing/app/views/payments/index.html.haml @@ -0,0 +1,5 @@ +%h2=t :transaction_history +- if (@transactions.count == 0) + = t(:no_transaction_history) +- @transactions.each do |t| + = render :partial => "transaction_details", :locals => {:transaction => t} \ No newline at end of file diff --git a/engines/billing/app/views/payments/new.html.haml b/engines/billing/app/views/payments/new.html.haml new file mode 100644 index 0000000..e9a8273 --- /dev/null +++ b/engines/billing/app/views/payments/new.html.haml @@ -0,0 +1,17 @@ +%h1 + = t(:Donation) +- if logged_in? + = t(:donation_not_payment) +- if @result and @result.errors.size > 0 + %div{:style => "color: red;"} + = h @result.errors.size + error(s) +- if @result and @result.transaction and @result.transaction.status != 'success' + %div{:style => "color: red;"} + = t(:processor_declined) += braintree_form_for :transaction, :html => {:autocomplete => "off"} do |f| + = f.label :amount, t(:amount) + = f.text_field :amount + = render :partial => 'non_customer_fields', :locals => {:f => f} + = hidden_field_tag :tr_data, @tr_data + = f.submit "Submit Donation", :class => 'btn btn-primary' diff --git a/engines/billing/app/views/subscriptions/_subscription_details.html.haml b/engines/billing/app/views/subscriptions/_subscription_details.html.haml new file mode 100644 index 0000000..6145c95 --- /dev/null +++ b/engines/billing/app/views/subscriptions/_subscription_details.html.haml @@ -0,0 +1,26 @@ +%p + - if local_assigns[:show_user] + User: + - user_to_show = user_for_subscription(subscription) + = link_to user_to_show.login, user_overview_path(user_to_show) + ID: + = link_to subscription.id, user_subscription_path(@user, subscription.id) + Balance: + - color = (subscription.balance > 0) ? "red" : "" + %font{:color => color} + = number_to_currency(subscription.balance) + Bill on: + = subscription.billing_day_of_month + Start date: + = subscription.first_billing_date + Paid through: + = subscription.paid_through_date + Plan: + = subscription.plan_id + Price: + = number_to_currency(subscription.price) + - color = (subscription.status == 'Active') ? "green" : "red" + Status: + %font{:color => color} + = subscription.status + - # would be good to get plan name but not sure if that is possible? \ No newline at end of file diff --git a/engines/billing/app/views/subscriptions/create.html.haml b/engines/billing/app/views/subscriptions/create.html.haml new file mode 100644 index 0000000..2b6c5e9 --- /dev/null +++ b/engines/billing/app/views/subscriptions/create.html.haml @@ -0,0 +1,9 @@ +- if @result.success? + %h1 + Subscription Status + = @result.subscription.status + = render :partial => "subscription_details", :locals => {:subscription => @result.subscription} +- else + %h1 + Error: + = @result.message \ No newline at end of file diff --git a/engines/billing/app/views/subscriptions/destroy.html.haml b/engines/billing/app/views/subscriptions/destroy.html.haml new file mode 100644 index 0000000..44b4333 --- /dev/null +++ b/engines/billing/app/views/subscriptions/destroy.html.haml @@ -0,0 +1,7 @@ +- if @result.success? + Subscription destroyed +- else + Error: + = @result.message +%p + = link_to 'Customer Information', show_customer_path(@user), :class=> :btn \ No newline at end of file diff --git a/engines/billing/app/views/subscriptions/index.html.haml b/engines/billing/app/views/subscriptions/index.html.haml new file mode 100644 index 0000000..3d4e8fd --- /dev/null +++ b/engines/billing/app/views/subscriptions/index.html.haml @@ -0,0 +1,8 @@ +%h2=t :all_subscriptions +- pending_active_pastdue = false +- @subscriptions.each do |s| + - if ['Pending', 'Active','Past Due'].include? s.status + - pending_active_pastdue = true + = render :partial => "subscription_details", :locals => {:subscription => s} +- if !pending_active_pastdue and @user == current_user + = link_to 'subscribe to plan', new_subscription_path, :class => :btn \ No newline at end of file diff --git a/engines/billing/app/views/subscriptions/new.html.haml b/engines/billing/app/views/subscriptions/new.html.haml new file mode 100644 index 0000000..4183458 --- /dev/null +++ b/engines/billing/app/views/subscriptions/new.html.haml @@ -0,0 +1,15 @@ +- if @payment_method_token + %h1 + Subscribe to plan + = #currently just one plan + = @plans[0].name + = number_to_currency(@plans[0].price) + = simple_form_for :subscription, :url => :subscriptions do |f| + = hidden_field_tag :payment_method_token, @payment_method_token + = hidden_field_tag :plan_id, @plans[0].id + .form-actions + = f.submit t(:subscribe), :class => 'btn btn-primary' +- else + = t(:must_create_customer) + %p + = link_to t(:create_new_customer), new_customer_path diff --git a/engines/billing/app/views/subscriptions/show.html.haml b/engines/billing/app/views/subscriptions/show.html.haml new file mode 100644 index 0000000..2699db9 --- /dev/null +++ b/engines/billing/app/views/subscriptions/show.html.haml @@ -0,0 +1,6 @@ +%h1 + - if @subscription.status == 'Active' + Current + Subscription += render :partial => "subscription_details", :locals => {:subscription => @subscription} += link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if allow_cancel_subscription(@subscription) diff --git a/engines/billing/config/initializers/braintree.rb b/engines/billing/config/initializers/braintree.rb new file mode 100644 index 0000000..3d87f4c --- /dev/null +++ b/engines/billing/config/initializers/braintree.rb @@ -0,0 +1,23 @@ +# +# set logger +# +if APP_CONFIG[:logfile].blank? + require 'syslog/logger' + Braintree::Configuration.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new('webapp')) +else + Braintree::Configuration.logger = Logger.new('log/braintree.log') +end + +# +# You can set these per environment in config/config.yml: +# +# Environment must be one of: :development, :qa, :sandbox, :production +# +if billing = APP_CONFIG[:billing] + if braintree = billing[:braintree] + Braintree::Configuration.environment = braintree[:environment].downcase.to_sym + Braintree::Configuration.merchant_id = braintree[:merchant_id] + Braintree::Configuration.public_key = braintree[:public_key] + Braintree::Configuration.private_key = braintree[:private_key] + end +end diff --git a/engines/billing/config/locales/en.yml b/engines/billing/config/locales/en.yml new file mode 100644 index 0000000..1300958 --- /dev/null +++ b/engines/billing/config/locales/en.yml @@ -0,0 +1,11 @@ +en: + create_new_customer: "Create a new Braintree Customer" + must_create_customer: "You must store a customer in braintree before subscribing to a plan" + subscribe: "Subscribe" + save_customer_info: "Save Customer Information" + donation_not_payment: "Note: This is a donation, and will not be applied towards your account." + no_relevant_subscription: "No subscription which is Active, Pending, or Past Due" + plan: "Plan" + description: "Description" + cost: "Cost" + free: "Free" \ No newline at end of file diff --git a/engines/billing/config/routes.rb b/engines/billing/config/routes.rb new file mode 100644 index 0000000..7263dff --- /dev/null +++ b/engines/billing/config/routes.rb @@ -0,0 +1,25 @@ +Rails.application.routes.draw do + + scope "(:locale)", :locale => MATCH_LOCALE do + match 'payments/new' => 'payments#new', :as => :new_payment + match 'payments/confirm' => 'payments#confirm', :as => :confirm_payment + resources :users do + resources :payments, :only => [:index] + resources :subscriptions, :only => [:index, :show, :destroy] + end + + resources :customer, :only => [:new, :edit] + resources :credit_card_info, :only => [:edit] + + match 'customer/confirm/' => 'customer#confirm', :as => :confirm_customer + match 'customer/show/:id' => 'customer#show', :as => :show_customer + match 'credit_card_info/confirm' => 'credit_card_info#confirm', :as => :confirm_credit_card_info + + resources :subscriptions, :only => [:new, :create, :update] # index, show & destroy are within users path + match 'billing_admin' => 'billing_admin#show', :as => :billing_admin + + #match 'transactions/:product_id/new' => 'transactions#new', :as => :new_transaction + #match 'transactions/confirm/:product_id' => 'transactions#confirm', :as => :confirm_transaction + end + +end diff --git a/engines/billing/leap_web_billing.gemspec b/engines/billing/leap_web_billing.gemspec new file mode 100644 index 0000000..c6ac3f3 --- /dev/null +++ b/engines/billing/leap_web_billing.gemspec @@ -0,0 +1,21 @@ +$:.push File.expand_path("../lib", __FILE__) + +require File.expand_path('../../../lib/leap_web/version.rb', __FILE__) + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "leap_web_billing" + s.version = LeapWeb::VERSION + s.authors = ["Jessib"] + s.email = ["jessib@leap.se"] + s.homepage = "http://www.leap.se" + s.summary = "Billing for LeapWeb" + s.description = "Billing System for a Leap provider" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.test_files = Dir["test/**/*"] + + # s.add_dependency "braintree-rails", "~> 0.4.5" + s.add_dependency "braintree" + #s.add_dependency "carmen-rails" +end diff --git a/engines/billing/lib/braintree_test_app.rb b/engines/billing/lib/braintree_test_app.rb new file mode 100644 index 0000000..41c327d --- /dev/null +++ b/engines/billing/lib/braintree_test_app.rb @@ -0,0 +1,36 @@ +# RackTest assumes all requests to be local. +# Braintree requests need to go out to a different server though. +# So we use a middleware to catch these and send them out again. + +class BraintreeTestApp + def initialize(app) + @app = app + end + + def call(env) + @env = env + config = Braintree::Configuration.instantiate + if request.path =~ /\/merchants\/#{config.merchant_id}\/transparent_redirect_requests$/ + #proxy post to braintree + uri = URI.parse(config.protocol + "://" + config.server + ":" + + config.port.to_s + request.path) + http = Net::HTTP.new(uri.host, uri.port) + res = http.post(uri.path, request.body.read) + + if res.code == "303" + header_hash = res.header.to_hash + header_hash["location"].first.gsub!("http://localhost:3000/", "http://www.example.com/") + [303, {"location" => header_hash["location"].first}, ""] + else + raise "unexpected response from Braintree: expected a 303" + end + else + @app.call(env) + end + end + + def request + @request = Rack::Request.new(@env) + end +end + diff --git a/engines/billing/lib/leap_web_billing.rb b/engines/billing/lib/leap_web_billing.rb new file mode 100644 index 0000000..288d846 --- /dev/null +++ b/engines/billing/lib/leap_web_billing.rb @@ -0,0 +1,4 @@ +require "leap_web_billing/engine" + +module LeapWebBilling +end diff --git a/engines/billing/lib/leap_web_billing/engine.rb b/engines/billing/lib/leap_web_billing/engine.rb new file mode 100644 index 0000000..ab574f2 --- /dev/null +++ b/engines/billing/lib/leap_web_billing/engine.rb @@ -0,0 +1,10 @@ +# thou shall require all your dependencies in an engine. +#require "braintree-rails" +require "braintree" +#require "carmen-rails" + +module LeapWebBilling + class Engine < ::Rails::Engine + + end +end diff --git a/engines/billing/script/rails b/engines/billing/script/rails new file mode 100755 index 0000000..8bd9c0a --- /dev/null +++ b/engines/billing/script/rails @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/leap_web_billing/engine', __FILE__) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/engines/billing/test/broken/admin_customer_test.rb b/engines/billing/test/broken/admin_customer_test.rb new file mode 100644 index 0000000..df92a0d --- /dev/null +++ b/engines/billing/test/broken/admin_customer_test.rb @@ -0,0 +1,31 @@ +require 'test_helper' +require 'fake_braintree' + +class AdminCustomerTest < BraintreeIntegrationTest + + setup do + @admin = User.find_by_login('admin') || FactoryGirl.create(:user, login: 'admin') + @user = FactoryGirl.create(:user) + end + + teardown do + @user.destroy if @user + @admin.destroy if @admin + end + + test "check non customer as admin" do + login_as @admin + visit '/' + click_link 'Users' + click_link @user.login + click_link 'Billing Settings' + assert page.has_content? @user.email_address + assert page.has_content? 'No Saved Customer' + end + + test "check customer as admin" do + skip "cannot check customer as admin" + # it would be good to have a test where an admin tries to view the 'Billing Settings' for another user. + # However, partially due to limitations of FakeBraintree, this doesn't seem pursuing at this time. + end +end diff --git a/engines/billing/test/broken/customer_creation_test.rb b/engines/billing/test/broken/customer_creation_test.rb new file mode 100644 index 0000000..90319a9 --- /dev/null +++ b/engines/billing/test/broken/customer_creation_test.rb @@ -0,0 +1,84 @@ +require 'test_helper' +require 'fake_braintree' + +class CustomerCreationTest < BraintreeIntegrationTest + + setup do + @user = FactoryGirl.create(:user) + login_as @user + end + + teardown do + @user.destroy + end + + # Let's test both steps together with capybara + # + # This test is nice and clean but also a bit fragile: + # RackTest assumes all requests to be local. So we need + # BraintreeTestApp for the braintree transparent redirect to work. + # + # this mystifies me why this works. when i type the click_button line (and the + # customer.braintree_customer line) in the debugger, it gives a timeout, + # but it works fine embedded in the test. + test "create customer with braintree" do + visit '/' + click_link 'Billing Settings' + # i am a bit unclear why this works, as it seems there will be validation errors + assert_difference("Customer.count") do + click_button 'Save Payment Info' # this gives me a timeout + end + assert customer = Customer.find_by_user_id(@user.id) + assert customer.braintree_customer + end + + # We only test the confirmation here. + # The request to Braintree is triggered outside of rails + # In skippped test below, we see this works even if the attributes are + # for a broken customer + test "successfully confirms customer creation" do + response = post_transparent_redirect :create_customer_data, + customer: FactoryGirl.attributes_for(:braintree_customer), + redirect_url: confirm_customer_url + + assert_difference("Customer.count") do + post response['Location'] + end + + assert_equal 200, status + assert customer = Customer.find_by_user_id(@user.id) + assert customer.braintree_customer + end + + + test "failed customer creation" do + skip "cannot get customer creation to fail" + + FakeBraintree.decline_all_cards! + + response = post_transparent_redirect :create_customer_data, + customer: FactoryGirl.attributes_for(:broken_customer), + redirect_url: confirm_customer_url + + assert FakeBraintree.decline_all_cards? + assert_no_difference("Customer.count") do + post response['Location'] #this gives me a timeout when run alone + end + assert_nil Customer.find_by_user_id(@user.id) + + end + + def post_transparent_redirect(type, data) + params = data.dup + params[:tr_data] = Braintree::TransparentRedirect.send(type, params) + post_transparent_redirect_params(params) + end + + def post_transparent_redirect_params(params) + uri = URI.parse(Braintree::TransparentRedirect.url) + Net::HTTP.start(uri.host, uri.port) do |http| + http.post(uri.path, Rack::Utils.build_nested_query(params)) + end + end + +end diff --git a/engines/billing/test/broken/subscription_test.rb b/engines/billing/test/broken/subscription_test.rb new file mode 100644 index 0000000..cd010bd --- /dev/null +++ b/engines/billing/test/broken/subscription_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' +require 'fake_braintree' + +class SubscriptionTest < BraintreeIntegrationTest + include CustomerTestHelper + include StubRecordHelper + + setup do + @admin = User.find_by_login('admin') || FactoryGirl.create(:user, login: 'admin') + @customer = stub_customer + @braintree_customer = @customer.braintree_customer + response = Braintree::Subscription.create plan_id: '5', + payment_method_token: @braintree_customer.credit_cards.first.token, + price: '10' + @subscription = response.subscription + end + + teardown do + @admin.destroy + end + + test "admin can see all subscriptions for another" do + login_as @admin + @customer.stubs(:subscriptions).returns([@subscription]) + @subscription.stubs(:balance).returns 0 + visit user_subscriptions_path(@customer.user_id, :locale => nil) + assert page.has_content?("Subscriptions") + assert page.has_content?("Status: Active") + end + + # test "user cannot see all subscriptions for other user" do + #end + + #test "admin cannot add subscription for another" do + #end + + #test "authenticated user can cancel own subscription" do + #end + + #test "user cannot add subscription if they have active one" do + #end + + #test "user can view own subscriptions" + #end + + #test "admin can view another user's subscriptions" do + #end + +end diff --git a/engines/billing/test/factories.rb b/engines/billing/test/factories.rb new file mode 100644 index 0000000..87543b2 --- /dev/null +++ b/engines/billing/test/factories.rb @@ -0,0 +1,25 @@ +FactoryGirl.define do + + TEST_CC_NUMBER = %w(4111 1111 1111 1111).join + + factory :customer do + user + + factory :customer_with_payment_info do + braintree_customer + end + end + + factory :braintree_customer, class: Braintree::Customer do + first_name 'Big' + last_name 'Spender' + credit_card number: TEST_CC_NUMBER, expiration_date: '04/2016' + initialize_with { Braintree::Customer.create(attributes).customer } + skip_create + + factory :broken_customer do + credit_card number: '123456', expiration_date: '04/2016' + end + end + +end diff --git a/engines/billing/test/functional/customer_controller_test.rb b/engines/billing/test/functional/customer_controller_test.rb new file mode 100644 index 0000000..d943e23 --- /dev/null +++ b/engines/billing/test/functional/customer_controller_test.rb @@ -0,0 +1,124 @@ +require 'test_helper' +require 'fake_braintree' + +class CustomerControllerTest < ActionController::TestCase + include CustomerTestHelper + + test "new assigns redirect url" do + login + get :new + + assert_response :success + assert assigns(:tr_data) + tr_data = Braintree::Util.parse_query_string(assigns(:tr_data)) + assert_equal confirm_customer_url, tr_data[:redirect_url] + end + + test "new requires login" do + get :new + + assert_response :redirect + assert_redirected_to login_path + end + + test "edit uses params[:id]" do + customer = stub_customer + login customer.user + get :edit, id: customer.user.id + + assert_response :success + assert assigns(:tr_data) + tr_data = Braintree::Util.parse_query_string(assigns(:tr_data)) + assert_equal customer.braintree_customer_id, tr_data[:customer_id] + assert_equal confirm_customer_url, tr_data[:redirect_url] + end + + test "confirm customer creation" do + login + Braintree::TransparentRedirect.expects(:confirm).returns(success_response) + # to_confirm = prepare_confirmation :create_customer_data, + # customer: FactoryGirl.attributes_for(:braintree_customer), + # redirect_url: confirm_customer_url + + assert_difference("Customer.count") do + post :confirm, braintree: :query + end + + assert_response :success + assert result = assigns(:result) + assert result.success? + assert result.customer.id + end + + test "customer update" do + customer = stub_customer + customer.expects(:save) + login customer.user + Braintree::TransparentRedirect.expects(:confirm). + returns(success_response(customer)) + + assert_no_difference("Customer.count") do + post :confirm, query: :from_braintree + end + + assert_response :success + assert result = assigns(:result) + assert result.success? + assert_equal customer.braintree_customer, result.customer + end + + test "failed customer creation" do + skip "can't get customer creation to fail" + login + FakeBraintree.decline_all_cards! + # what is prepare_confirmation ?? this method isn't found + to_confirm = prepare_confirmation :create_customer_data, + customer: FactoryGirl.attributes_for(:broken_customer), + redirect_url: confirm_customer_url + post :confirm, to_confirm + + FakeBraintree.clear! + assert_response :success + assert result = assigns(:result) + assert !result.success? + end + + test "failed customer creation with stubbing" do + login + Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) + post :confirm, bla: :blub + + assert_response :success + assert_template :new + end + + test "failed customer update with stubbing" do + customer = stub_customer + login customer.user + Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) + post :confirm, bla: :blub + + assert_response :success + assert_template :edit + end + + def failure_response + stub success?: false, + errors: stub(for: nil, size: 0), + params: {} + end + + def success_response(customer = nil) + stub success?: true, + customer: braintree_customer(customer) + end + + def braintree_customer(customer) + if customer + customer.braintree_customer + else + FactoryGirl.build :braintree_customer + end + end + +end diff --git a/engines/billing/test/functional/customers_controller_test.rb b/engines/billing/test/functional/customers_controller_test.rb new file mode 100644 index 0000000..46c33c9 --- /dev/null +++ b/engines/billing/test/functional/customers_controller_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' +require 'fake_braintree' + +class CustomersControllerTest < ActionController::TestCase + tests CustomerController + + setup do + @user = FactoryGirl.create :user + @other_user = FactoryGirl.create :user + #FakeBraintree.clear! + #FakeBraintree.verify_all_cards! + testid = 'testid' + #this wasn't actually being used + #FakeBraintree::Customer.new({:credit_cards => [{:number=>"5105105105105100", :expiration_date=>"05/2013"}]}, {:id => testid, :merchant_id => Braintree::Configuration.merchant_id}) + # any reason to call the create instance method on the FakeBraintree::Customer ? + @customer = Customer.new(:user_id => @other_user.id) + @customer.braintree_customer_id = testid + @customer.save + + end + + teardown do + @user.destroy + @other_user.destroy + @customer.destroy + end + + test "no access if not logged in" do + get :new + assert_access_denied(true, false) + get :show, :id => @customer.braintree_customer_id + assert_access_denied(true, false) + get :edit, :id => @customer.braintree_customer_id + assert_access_denied(true, false) + end + + + test "should get new if logged in and not customer" do + login @user + get :new + assert_not_nil assigns(:tr_data) + assert_response :success + end + + test "new should direct edit if user is already a customer" do + login @other_user + get :new + assert_response :redirect + assert_equal edit_customer_url(@customer.user), response.header['Location'] + end + + + test "show" do + skip "show customer" + login @other_user + # Below will fail, as when we go to fetch the customer data, Braintree::Customer.find(params[:id]) won't find the customer as it is a FakeBraintree customer. + #get :show, :id => @customer.braintree_customer_id + + end + +end diff --git a/engines/billing/test/functional/payments_controller_test.rb b/engines/billing/test/functional/payments_controller_test.rb new file mode 100644 index 0000000..90b7582 --- /dev/null +++ b/engines/billing/test/functional/payments_controller_test.rb @@ -0,0 +1,50 @@ +require 'test_helper' +require 'fake_braintree' + +class PaymentsControllerTest < ActionController::TestCase + include CustomerTestHelper + + test "payment when unauthorized" do + get :new + assert_not_nil assigns(:tr_data) + assert_response :success + end + + test "successful confirmation renders confirm" do + Braintree::TransparentRedirect.expects(:confirm).returns(success_response) + get :confirm + + assert_response :success + assert_template :confirm + end + + test "failed confirmation renders new" do + Braintree::TransparentRedirect.expects(:confirm).returns(failure_response) + get :confirm + + assert_response :success + assert_not_nil assigns(:tr_data) + assert_template :new + end + + def failure_response + stub success?: false, + errors: stub(for: nil, size: 0), + params: {}, + transaction: stub(status: nil) + end + + def success_response + stub success?: true, + transaction: stub_transaction + end + + # that's what you get when not following the law of demeter... + def stub_transaction + stub amount: "100.00", + id: "ASDF", + customer_details: FactoryGirl.build(:braintree_customer), + credit_card_details: FactoryGirl.build(:braintree_customer).credit_cards.first + end + +end diff --git a/engines/billing/test/functional/subscriptions_controller_test.rb b/engines/billing/test/functional/subscriptions_controller_test.rb new file mode 100644 index 0000000..a6a1057 --- /dev/null +++ b/engines/billing/test/functional/subscriptions_controller_test.rb @@ -0,0 +1,16 @@ +require 'test_helper' +require 'fake_braintree' + +class SubscriptionsControllerTest < ActionController::TestCase + include CustomerTestHelper + + test "destroy cancels subscription" do + customer = stub_customer + login customer.user + result = Braintree::Subscription.create plan_id: 'my_plan', + payment_method_token: customer.braintree_customer.credit_cards.first.token + subscription = result.subscription + delete :destroy, id: subscription.id, user_id: customer.user.id + assert_equal "Canceled", Braintree::Subscription.find(subscription.id).status + end +end diff --git a/engines/billing/test/support/braintree_integration_test.rb b/engines/billing/test/support/braintree_integration_test.rb new file mode 100644 index 0000000..976c5a2 --- /dev/null +++ b/engines/billing/test/support/braintree_integration_test.rb @@ -0,0 +1,18 @@ +require 'capybara/rails' +# require 'fake_braintree' - messes up other integration tests +require 'braintree_test_app' + +class BraintreeIntegrationTest < BrowserIntegrationTest + include Warden::Test::Helpers + + setup do + Warden.test_mode! + Rails.application.config.middleware.use BraintreeTestApp + end + + teardown do + Warden.test_reset! + Rails.application.config.middleware.delete "BraintreeTestApp" + end + +end diff --git a/engines/billing/test/support/customer_test_helper.rb b/engines/billing/test/support/customer_test_helper.rb new file mode 100644 index 0000000..adac00a --- /dev/null +++ b/engines/billing/test/support/customer_test_helper.rb @@ -0,0 +1,11 @@ +module CustomerTestHelper + + def stub_customer(user = nil) + user ||= find_record :user + customer = stub_record :customer_with_payment_info, + user: user, + user_id: user.id + Customer.stubs(:find_by_user_id).with(user.id).returns(customer) + return customer + end +end diff --git a/engines/billing/test/test_helper.rb b/engines/billing/test/test_helper.rb new file mode 100644 index 0000000..7ad3869 --- /dev/null +++ b/engines/billing/test/test_helper.rb @@ -0,0 +1,15 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require File.expand_path("../../../../dummy/config/environment.rb", __FILE__) +require "rails/test_help" + +Rails.backtrace_cleaner.remove_silencers! + +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# Load fixtures from the engine +if ActiveSupport::TestCase.method_defined?(:fixture_path=) + ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) +end diff --git a/engines/billing/test/unit/customer_test.rb b/engines/billing/test/unit/customer_test.rb new file mode 100644 index 0000000..6156f87 --- /dev/null +++ b/engines/billing/test/unit/customer_test.rb @@ -0,0 +1,38 @@ +require 'test_helper' + +class CustomerTest < ActiveSupport::TestCase + include StubRecordHelper + + setup do + @user = find_record :user + @customer = FactoryGirl.build(:customer, user: @user) + end + + test "test set of attributes should be valid" do + @customer.valid? + assert_equal Hash.new, @customer.errors.messages + end + + test "customer belongs to user" do + assert_equal User, @customer.user.class + end + + test "user validation" do + @customer.user = nil + assert !@customer.valid? + end + + test "has no payment info" do + assert !@customer.braintree_customer_id + assert !@customer.has_payment_info? + end + + test "with no braintree data" do + assert_equal @customer, @customer.with_braintree_data! + end + + test "without default credit card" do + assert_nil @customer.default_credit_card + end + +end diff --git a/engines/billing/test/unit/customer_with_payment_info_test.rb b/engines/billing/test/unit/customer_with_payment_info_test.rb new file mode 100644 index 0000000..0589a59 --- /dev/null +++ b/engines/billing/test/unit/customer_with_payment_info_test.rb @@ -0,0 +1,40 @@ +require 'test_helper' +require 'fake_braintree' + +class CustomerWithPaymentInfoTest < ActiveSupport::TestCase + include StubRecordHelper + + setup do + @user = find_record :user + @customer = FactoryGirl.build(:customer_with_payment_info, user: @user) + end + + test "has payment_info" do + assert @customer.braintree_customer_id + assert @customer.has_payment_info? + end + + test "constructs customer with braintree data" do + @customer.with_braintree_data! + assert_equal 'Big', @customer.first_name + assert_equal 'Spender', @customer.last_name + assert_equal 1, @customer.credit_cards.size + assert_equal Hash.new, @customer.custom_fields + end + + test "can access braintree_customer after reload" do + @customer.save + @customer = Customer.find_by_user_id(@customer.user_id) + @customer.with_braintree_data! + assert_equal 'Big', @customer.first_name + assert_equal 'Spender', @customer.last_name + assert_equal 1, @customer.credit_cards.size + assert_equal Hash.new, @customer.custom_fields + @customer.destroy + end + + test "sets default_credit_card" do + @customer.with_braintree_data! + assert_equal @customer.credit_cards.first, @customer.default_credit_card + end +end diff --git a/engines/support/Gemfile b/engines/support/Gemfile new file mode 100644 index 0000000..ad7d29b --- /dev/null +++ b/engines/support/Gemfile @@ -0,0 +1,15 @@ +source "https://rubygems.org" + +eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) +eval(File.read(File.dirname(__FILE__) + '/..//ui_dependencies.rb')) + +# We require leap_web_core from here so we can use the path option. +gem "leap_web_core", :path => '../core' + +# Declare your gem's dependencies in leap_web_users.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec + +# To use debugger +# gem 'ruby-debug' diff --git a/engines/support/README.md b/engines/support/README.md new file mode 100644 index 0000000..c9573e6 --- /dev/null +++ b/engines/support/README.md @@ -0,0 +1 @@ +Implements a simple, clean, and easy to use help ticketing system. \ No newline at end of file diff --git a/engines/support/Rakefile b/engines/support/Rakefile new file mode 100644 index 0000000..0e73163 --- /dev/null +++ b/engines/support/Rakefile @@ -0,0 +1,44 @@ +#!/usr/bin/env rake + +require 'rake/packagetask' +require 'rubygems/package_task' + +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end +begin + require 'rdoc/task' +rescue LoadError + require 'rdoc/rdoc' + require 'rake/rdoctask' + RDoc::Task = Rake::RDocTask +end + +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'LeapWebHelp' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.rdoc') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +spec = eval(File.read('leap_web_help.gemspec')) +Gem::PackageTask.new(spec) do |p| + p.gem_spec = spec +end + +Bundler::GemHelper.install_tasks + +require 'rake/testtask' + +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = false +end + + +task :default => :test diff --git a/engines/support/app/assets/javascripts/tickets.js b/engines/support/app/assets/javascripts/tickets.js new file mode 100644 index 0000000..18537aa --- /dev/null +++ b/engines/support/app/assets/javascripts/tickets.js @@ -0,0 +1,4 @@ +//$(document).ready(function () { +// $.fn.editable.defaults.mode = 'inline'; +// $('#subject').editable(); +//}); \ No newline at end of file diff --git a/engines/support/app/controllers/tickets_controller.rb b/engines/support/app/controllers/tickets_controller.rb new file mode 100644 index 0000000..d65ee43 --- /dev/null +++ b/engines/support/app/controllers/tickets_controller.rb @@ -0,0 +1,153 @@ +class TicketsController < ApplicationController + include AutoTicketsPathHelper + + respond_to :html, :json + #has_scope :open, :type => boolean + + before_filter :require_login, :only => [:index] + before_filter :fetch_ticket, :only => [:show, :update, :destroy] # don't now have an edit method + before_filter :fetch_user + before_filter :set_title + + def new + @ticket = Ticket.new + @ticket.comments.build + end + + def create + @ticket = Ticket.new(params[:ticket]) + + @ticket.comments.last.posted_by = (logged_in? ? current_user.id : nil) #protecting posted_by isn't working, so this should protect it. + @ticket.comments.last.private = false unless admin? + @ticket.created_by = current_user.id if logged_in? + @ticket.email = current_user.email_address if logged_in? and current_user.email_address + + if @ticket.save + flash[:notice] = t(:thing_was_successfully_created, :thing => t(:ticket)) + end + + # cannot set this until ticket has been saved, as @ticket.id will not be set + if !logged_in? and flash[:notice] + flash[:notice] += " " + t(:access_ticket_text, :full_url => ticket_url(@ticket.id)) + end + respond_with(@ticket, :location => auto_ticket_path(@ticket)) + end + + def show + @comment = TicketComment.new + if !@ticket + redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => t(:ticket)) + return + end + end + + def update + if params[:commit] == 'close' + @ticket.is_open = false + @ticket.save + redirect_to_tickets + elsif params[:commit] == 'open' + @ticket.is_open = true + @ticket.save + redirect_to auto_ticket_path(@ticket) + else + @ticket.attributes = cleanup_ticket_params(params[:ticket]) + + if params[:commit] == 'reply_and_close' + @ticket.close + end + + if @ticket.comments_changed? + @ticket.comments.last.posted_by = (current_user ? current_user.id : nil) + @ticket.comments.last.private = false unless admin? + end + + if @ticket.changed? and @ticket.save + flash[:notice] = t(:changes_saved) + redirect_to_tickets + else + flash[:error] = @ticket.errors.full_messages.join(". ") if @ticket.changed? + redirect_to auto_ticket_path(@ticket) + end + end + end + + def index + @all_tickets = Ticket.search(search_options(params)) + @tickets = @all_tickets.page(params[:page]).per(APP_CONFIG[:pagination_size]) + end + + def destroy + # should we allow non-admins to delete their own tickets? i don't think necessary. + @ticket.destroy if admin? + redirect_to auto_tickets_path + end + + protected + + def set_title + @title = t(:tickets) + end + + private + + # + # redirects to ticket index, if appropriate. + # otherwise, just redirects to @ticket + # + def redirect_to_tickets + if logged_in? + if params[:commit] == t(:reply_and_close) + redirect_to auto_tickets_path + else + redirect_to auto_ticket_path(@ticket) + end + else + # if we are not logged in, there is no index to view + redirect_to auto_ticket_path(@ticket) + end + end + + # + # unset comments hash if no new comment was typed + # + def cleanup_ticket_params(ticket) + if ticket && ticket[:comments_attributes] + if ticket[:comments_attributes].values.first[:body].blank? + ticket[:comments_attributes] = nil + end + end + return ticket + end + + def ticket_access? + @ticket and (admin? or !@ticket.created_by or (current_user and current_user.id == @ticket.created_by)) + end + + def fetch_ticket + @ticket = Ticket.find(params[:id]) + if !@ticket and admin? + redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => 'ticket') + return + end + access_denied unless ticket_access? + end + + def fetch_user + if params[:user_id] + @user = User.find(params[:user_id]) + end + end + + # + # clean up params for ticket search + # + def search_options(params) + params.merge( + :admin_status => params[:user_id] ? 'mine' : 'all', + :user_id => @user ? @user.id : current_user.id, + :is_admin => admin? + ) + end + +end diff --git a/engines/support/app/designs/ticket/by_includes_post_by.js b/engines/support/app/designs/ticket/by_includes_post_by.js new file mode 100644 index 0000000..2eeac89 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by.js @@ -0,0 +1,13 @@ +// TODO: This view is only used in tests--should we keep it? +function(doc) { + var arr = {} + if (doc['type'] == 'Ticket' && doc.comments) { + doc.comments.forEach(function(comment){ + if (comment.posted_by && !arr[comment.posted_by]) { + //don't add duplicates + arr[comment.posted_by] = true; + emit(comment.posted_by, 1); + } + }); + } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js new file mode 100644 index 0000000..72169b0 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js @@ -0,0 +1,12 @@ +function(doc) { + var arr = {} + if (doc['type'] == 'Ticket' && doc.comments) { + doc.comments.forEach(function(comment){ + if (comment.posted_by && !arr[comment.posted_by]) { + //don't add duplicates + arr[comment.posted_by] = true; + emit([comment.posted_by, doc.created_at], 1); + } + }); + } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js new file mode 100644 index 0000000..33dfe0b --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js @@ -0,0 +1,12 @@ +function(doc) { + var arr = {} + if (doc['type'] == 'Ticket' && doc.comments) { + doc.comments.forEach(function(comment){ + if (comment.posted_by && !arr[comment.posted_by]) { + //don't add duplicates + arr[comment.posted_by] = true; + emit([comment.posted_by, doc.is_open, doc.created_at], 1); + } + }); + } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js new file mode 100644 index 0000000..3bd2a74 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js @@ -0,0 +1,12 @@ +function(doc) { + var arr = {} + if (doc['type'] == 'Ticket' && doc.comments) { + doc.comments.forEach(function(comment){ + if (comment.posted_by && !arr[comment.posted_by]) { + //don't add duplicates + arr[comment.posted_by] = true; + emit([comment.posted_by, doc.is_open, doc.updated_at], 1); + } + }); + } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js new file mode 100644 index 0000000..2b4304f --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js @@ -0,0 +1,12 @@ +function(doc) { + var arr = {} + if (doc['type'] == 'Ticket' && doc.comments) { + doc.comments.forEach(function(comment){ + if (comment.posted_by && !arr[comment.posted_by]) { + //don't add duplicates + arr[comment.posted_by] = true; + emit([comment.posted_by, doc.updated_at], 1); + } + }); + } +} diff --git a/engines/support/app/helpers/auto_tickets_path_helper.rb b/engines/support/app/helpers/auto_tickets_path_helper.rb new file mode 100644 index 0000000..93f3cb9 --- /dev/null +++ b/engines/support/app/helpers/auto_tickets_path_helper.rb @@ -0,0 +1,53 @@ +# +# These "auto" forms of the normal ticket path route helpers allow us to do two things automatically: +# +# (1) include the user in the path if appropriate. +# (2) retain the sort params, if appropriate. +# +# Tickets views with a user_id are limited to that user. For admins, they don't need a user_id for any ticket action. +# +# This is available both to the views and the tickets_controller. +# +module AutoTicketsPathHelper + + protected + + def auto_tickets_path(options={}) + return unless options.class == Hash + options = ticket_view_options.merge options + if @user + user_tickets_path(@user, options) + else + tickets_path(options) + end + end + + def auto_ticket_path(ticket, options={}) + options = ticket_view_options.merge options + if @user + user_ticket_path(@user, ticket, options) + else + ticket_path(ticket, options) + end + end + + def auto_new_ticket_path(options={}) + return unless options.class == Hash + options = ticket_view_options.merge options + if @user + new_user_ticket_path(@user, options) + else + new_ticket_path(options) + end + end + + private + + def ticket_view_options + hsh = {} + hsh[:open_status] = params[:open_status] if params[:open_status] && !params[:open_status].empty? + hsh[:sort_order] = params[:sort_order] if params[:sort_order] && !params[:sort_order].empty? + hsh + end + +end \ No newline at end of file diff --git a/engines/support/app/helpers/tickets_helper.rb b/engines/support/app/helpers/tickets_helper.rb new file mode 100644 index 0000000..7af50d6 --- /dev/null +++ b/engines/support/app/helpers/tickets_helper.rb @@ -0,0 +1,76 @@ +module TicketsHelper + # + # FORM HELPERS + # + + # + # hidden fields that should be added to ever ticket form. + # these are use for proper redirection after successful actions. + # + def hidden_ticket_fields + haml_concat hidden_field_tag('open_status', params[:open_status]) + haml_concat hidden_field_tag('sort_order', params[:sort_order]) + haml_concat hidden_field_tag('user_id', params[:user_id]) + "" + end + + # + # PARAM HELPERS + # + + def search_status + if action?(:index) + params[:open_status] || 'open' + else + nil + end + end + + def search_order + params[:sort_order] || 'updated_at_desc' + end + + # + # LINK HELPERS + # + + def link_to_status(new_status) + if new_status == "open" + label = t(:open_tickets) + elsif new_status == "closed" + label = t(:closed_tickets) + elsif new_status == "all" + label = t(:all_tickets) + end + link_to label, auto_tickets_path(:open_status => new_status, :sort_order => search_order) + end + + def link_to_order(order_field) + if search_order.start_with?(order_field) + # link for currently-filtered field. Link to other direction of this field. + if search_order.end_with? 'asc' + direction = 'desc' + icon_direction = 'up' + else + direction = 'asc' + icon_direction = 'down' + end + arrow = content_tag(:i, '', class: 'icon-arrow-'+ icon_direction) + else + # for not-currently-filtered field, don't display an arrow, and link to descending direction + arrow = '' + direction = 'desc' + end + + if order_field == 'updated' + label = t(:updated) + elsif order_field == 'created' + label = t(:created) + end + + link_to auto_tickets_path(:sort_order => order_field + '_at_' + direction, :open_status => search_status) do + arrow + label + end + end + +end diff --git a/engines/support/app/models/account_extension/tickets.rb b/engines/support/app/models/account_extension/tickets.rb new file mode 100644 index 0000000..f898b56 --- /dev/null +++ b/engines/support/app/models/account_extension/tickets.rb @@ -0,0 +1,13 @@ +module AccountExtension::Tickets + extend ActiveSupport::Concern + + def destroy_with_tickets + Ticket.destroy_all_from(self.user) + destroy_without_tickets + end + + included do + alias_method_chain :destroy, :tickets + end + +end diff --git a/engines/support/app/models/ticket.rb b/engines/support/app/models/ticket.rb new file mode 100644 index 0000000..cd22758 --- /dev/null +++ b/engines/support/app/models/ticket.rb @@ -0,0 +1,105 @@ +# +# TODO: thought i should reverse keys for descending, but that didn't work. +# look into whether that should be tweaked, and whether it works okay with +# pagination (seems to now...) +# +# TODO: better validation of email +# +# TODO: don't hardcode strings 'unknown user' and 'unauthenticated user' +# +class Ticket < CouchRest::Model::Base + use_database "tickets" + + property :created_by, String, :protected => true # nil for anonymous tickets, should never be changed + property :regarding_user, String # may be nil or valid username + property :subject, String + property :email, String + property :is_open, TrueClass, :default => true + property :comments, [TicketComment] + + timestamps! + + before_validation :set_email, :set_regarding_user, :on => :create + + design do + view :by_updated_at + view :by_created_at + view :by_created_by + + view :by_is_open_and_created_at + view :by_is_open_and_updated_at + + own_path = Pathname.new(File.dirname(__FILE__)) + load_views(own_path.join('..', 'designs', 'ticket')) + end + + validates :subject, :presence => true + validates :email, :allow_blank => true, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ + + def self.search(options = {}) + @selection = TicketSelection.new(options) + @selection.tickets + end + + def self.destroy_all_from(user) + self.by_created_by.key(user.id).each do |ticket| + ticket.destroy + end + end + + def is_creator_validated? + !!created_by + end + + def set_email + self.email = nil if self.email == "" + end + + def set_regarding_user + self.regarding_user = nil if self.regarding_user == "" + end + + def close + self.is_open = false + end + + def reopen + self.is_open = true + end + + def commenters + commenters = [] + self.comments.each do |comment| + if comment.posted_by + if user = User.find(comment.posted_by) + commenters << user.login if user and !commenters.include?(user.login) + else + commenters << 'unknown user' if !commenters.include?('unknown user') + end + else + commenters << 'unauthenticated user' if !commenters.include?('unauthenticated user') + end + end + commenters.join(', ') + end + + # + # update comments. User should be set by controller. + # + def comments_attributes=(attributes) + if attributes + comment = TicketComment.new(attributes.values.first) + comment.posted_at = Time.now + comments << comment + end + end + + def created_by_user + User.find(self.created_by) + end + + def regarding_user_actual_user + User.find_by_login(self.regarding_user) + end + +end diff --git a/engines/support/app/models/ticket_comment.rb b/engines/support/app/models/ticket_comment.rb new file mode 100644 index 0000000..bed5237 --- /dev/null +++ b/engines/support/app/models/ticket_comment.rb @@ -0,0 +1,43 @@ +class TicketComment + include CouchRest::Model::Embeddable + + #belongs_to :ticket #is this best way to do it? will want to access all of a tickets comments, so maybe this isn't the way? + property :posted_by, String#, :protected => true #Integer#this should be current_user if that is set, meaning the user is logged in #cannot have it be protected and set via comments_attributes=. also, if it is protected and we set in the tickets_controller, it gets unset. TODO---is this okay to have it not protected and manually check it? We do not users to be able to set this. + # if the current user is not set, then we could just say the comment comes from an 'unauthenticated user', which would be somebody with the secret URL + property :posted_at, Time#, :protected => true + #property :posted_verified, TrueClass, :protected => true #should be true if current_user is set when the comment is created + property :body, String + property :private, TrueClass # private comments are only viewable by admins #this is checked when set, to make sure it was set by an admin + + # ? timestamps! + validates :body, :presence => true + #before_validation :set_time#, :set_posted_by + + #design do + # view :by_posted_at + # view :by_body + #end + + def is_comment_validated? + !!posted_by + end + + def posted_by_user + User.find(posted_by) if posted_by + end + +=begin + #TODO. + #this is resetting all comments associated with the ticket: + def set_time + self.posted_at = Time.now + end +=end + +=begin + def set_posted_by + self.posted_by = User.current if User.current + end +=end + +end diff --git a/engines/support/app/models/ticket_selection.rb b/engines/support/app/models/ticket_selection.rb new file mode 100644 index 0000000..74d5b78 --- /dev/null +++ b/engines/support/app/models/ticket_selection.rb @@ -0,0 +1,71 @@ +class TicketSelection + + # + # supported options: + # + # user_id: id of the user (uuid string) + # open_status: open | closed | all + # sort_order: updated_at_desc | updated_at_asc | created_at_desc | created_at_asc + # admin_status: mine | all + # is_admin: true | false + # + def initialize(options = {}) + @user_id = options[:user_id].gsub /[^a-z0-9]/, '' + @open_status = allow options[:open_status], 'open', 'closed', 'all' + @sort_order = allow options[:sort_order], 'updated_at_desc', 'updated_at_asc', 'created_at_desc', 'created_at_asc' + @admin_status = allow options[:admin_status], 'mine', 'all' + @is_admin = allow options[:is_admin], false, true + end + + def tickets + Ticket.send(finder_method).startkey(startkey).endkey(endkey).send(order) + end + + protected + + def allow(source, *allowed) + if allowed.include?(source) + source + else + allowed.first + end + end + + def finder_method + method = 'by_' + method += 'includes_post_by_and_' if only_mine? + method += 'is_open_and_' if @open_status != 'all' + method += @sort_order.sub(/_(de|a)sc$/, '') + end + + def startkey + startkeys = [] + startkeys << @user_id if only_mine? + startkeys << (@open_status == 'open') if @open_status != 'all' + startkeys << 0 + startkeys = startkeys.join if startkeys.length == 1 # want string not array if just one thing in array + startkeys + end + + def endkey + endtime = Time.now + 2.days # TODO. this obviously isn't ideal + if self.startkey.is_a?(Array) + endkeys = self.startkey + endkeys.pop + endkeys << endtime + else + endtime + end + end + + def order + # we have defined the ascending method to return the view itself: + (@sort_order.end_with? 'desc') ? 'descending' : 'ascending' + end + + + def only_mine? + !@is_admin || @admin_status == 'mine' + end + +end diff --git a/engines/support/app/views/tickets/_comment.html.haml b/engines/support/app/views/tickets/_comment.html.haml new file mode 100644 index 0000000..778ca13 --- /dev/null +++ b/engines/support/app/views/tickets/_comment.html.haml @@ -0,0 +1,20 @@ +- if admin? or !comment.private # only show comment if user is admin or comment is not private + %tr + %td.user + %div + %strong + - if comment.posted_by_user + = comment.posted_by_user.login + - else + = t(:anonymous) + %div= comment.posted_at.to_s(:short) + - if comment.posted_by_user && comment.posted_by_user.is_admin? + %div + %span.label.label-inverse + = t(:admin) + - if comment.private + %div + %span.label.label-important + = t(:private) + %td.comment + = simple_format(comment.body) \ No newline at end of file diff --git a/engines/support/app/views/tickets/_edit_form.html.haml b/engines/support/app/views/tickets/_edit_form.html.haml new file mode 100644 index 0000000..714f8ff --- /dev/null +++ b/engines/support/app/views/tickets/_edit_form.html.haml @@ -0,0 +1,48 @@ +:ruby + # created by user link + if @ticket.created_by_user + created_by = link_to @ticket.created_by_user.login, @ticket.created_by_user + else + created_by = t(:anonymous) + end + + # regarding user link + if admin? + if @ticket.regarding_user_actual_user + regarding_user_link = link_to @ticket.regarding_user_actual_user.login, @ticket.regarding_user_actual_user + else + regarding_user_link = "(#{t(:unknown)})" + end + else + regarding_user_link = '' + end + += form_for @ticket do |f| + = hidden_ticket_fields + %p.first + - if @ticket.is_open? + %span.label.label-info= t(:open) + - else + %span.label.label-success= t(:closed) + %span.label.label-clear= t(:created_by_on, :user => created_by, :time => @ticket.created_at.to_s(:short)).html_safe + %div= t(:subject) + = f.text_field :subject, :class => 'large full-width' + .row-fluid + .span4 + %div= t(:status) + = f.select :is_open, [[t(:open), "true"], [t(:closed), "false"]] + .span4 + %div= t(:email) + = f.text_field :email + .span4 + %div + = t(:regarding_account) + = regarding_user_link + = f.text_field :regarding_user + = f.button t(:save), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'save' + - if @ticket.is_open? + = f.button t(:close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'close' + - else + = f.button t(:open), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'open' + - if admin? + = link_to t(:destroy), auto_ticket_path(@ticket), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn' diff --git a/engines/support/app/views/tickets/_new_comment_form.html.haml b/engines/support/app/views/tickets/_new_comment_form.html.haml new file mode 100644 index 0000000..8418e01 --- /dev/null +++ b/engines/support/app/views/tickets/_new_comment_form.html.haml @@ -0,0 +1,13 @@ +-# +-# for posting a new comment to an existing ticket. +-# += simple_form_for @ticket, :html => {:class => 'slim'} do |f| + = hidden_ticket_fields + = f.simple_fields_for :comments, @comment, :wrapper => :none, :html => {:class => 'slim'} do |c| + = c.input :body, :label => false, :as => :text, :input_html => {:class => "full-width", :rows=> 5} + - if admin? + = c.input :private, :as => :boolean, :label => false, :inline_label => true + = f.button :button, t(:post_reply), :name => 'commit', :class => 'btn-primary', :type => 'submit', :value => 'post_reply' + - if logged_in? && @ticket.is_open + = f.button :button, t(:reply_and_close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'reply_and_close' + = link_to t(:cancel), auto_tickets_path, :class => :btn diff --git a/engines/support/app/views/tickets/_tabs.html.haml b/engines/support/app/views/tickets/_tabs.html.haml new file mode 100644 index 0000000..b7b5d3a --- /dev/null +++ b/engines/support/app/views/tickets/_tabs.html.haml @@ -0,0 +1,23 @@ +-# +-# SORT ORDER TABS +-# +- unless action?(:new) + %ul.nav.nav-pills.pull-right.slim + %li{:class=> ("active" if search_order.start_with? 'created_at')} + = link_to_order('created') + %li{:class=> ("active" if search_order.start_with? 'updated_at')} + = link_to_order('updated') + +-# +-# STATUS FILTER TABS +-# +%ul.nav.nav-tabs + - if logged_in? + %li{:class => ("active" if search_status == 'open')} + = link_to_status 'open' + %li{:class => ("active" if search_status == 'closed')} + = link_to_status 'closed' + %li{:class => ("active" if search_status == 'all')} + = link_to_status 'all' + %li{:class => ("active" if action?(:new))} + = link_to icon(:plus, :black) + t(:new_ticket), auto_new_ticket_path diff --git a/engines/support/app/views/tickets/_ticket.html.haml b/engines/support/app/views/tickets/_ticket.html.haml new file mode 100644 index 0000000..5bc33c8 --- /dev/null +++ b/engines/support/app/views/tickets/_ticket.html.haml @@ -0,0 +1,6 @@ +- url = auto_ticket_path(ticket) +%tr + %td= link_to ticket.subject, url + %td= link_to ticket.created_at.to_s(:short), url + %td= link_to ticket.updated_at.to_s(:short), url + %td= ticket.commenters diff --git a/engines/support/app/views/tickets/index.html.haml b/engines/support/app/views/tickets/index.html.haml new file mode 100644 index 0000000..c02a326 --- /dev/null +++ b/engines/support/app/views/tickets/index.html.haml @@ -0,0 +1,19 @@ +- @show_navigation = !params[:user_id].nil? + += render 'tickets/tabs' + +%table.table.table-striped.table-bordered + %thead + %tr + %th= t(:subject) + %th= t(:created) + %th= t(:updated) + %th= t(:voices) + %tbody + - if @tickets.any? + = render @tickets.all + - else + %tr + %td{:colspan=>4}= t(:none) + += paginate @tickets diff --git a/engines/support/app/views/tickets/new.html.haml b/engines/support/app/views/tickets/new.html.haml new file mode 100644 index 0000000..8f217a5 --- /dev/null +++ b/engines/support/app/views/tickets/new.html.haml @@ -0,0 +1,30 @@ +- @show_navigation = !params[:user_id].nil? + += render 'tickets/tabs' + +- if admin? && @user + - email = @user.email_address + - regarding = @user.login +- elsif logged_in? + - email = current_user.email_address + - regarding = current_user.login + += simple_form_for @ticket, :validate => true, :html => {:class => 'form-horizontal'} do |f| + = hidden_ticket_fields + = f.input :subject + - if logged_in? + = f.input :email, input_html: {value: email} + = f.input :regarding_user, input_html: {value: regarding} + - else + = f.input :email + = f.input :regarding_user + = f.simple_fields_for :comments, @comment do |c| + = c.input :body, :label => t(:description), :as => :text, :input_html => {:class => "full-width", :rows=> 5} + - if admin? + = c.input :private, :as => :boolean, :label => false, :inline_label => true + .form-actions + = f.button :submit, :class => 'btn-primary', :value => t(:create_thing, :thing => t(:ticket)) + - if logged_in? + = link_to t(:cancel), auto_tickets_path, :class => :btn + - else + = link_to t(:cancel), home_path, :class => 'btn' \ No newline at end of file diff --git a/engines/support/app/views/tickets/show.html.haml b/engines/support/app/views/tickets/show.html.haml new file mode 100644 index 0000000..bfdb773 --- /dev/null +++ b/engines/support/app/views/tickets/show.html.haml @@ -0,0 +1,12 @@ +- @show_navigation = !params[:user_id].nil? + +.ticket + = render 'tickets/edit_form' + %table.table.table-striped.table-bordered + %tbody + = render :partial => 'tickets/comment', :collection => @ticket.comments + %tr + %td.user + = logged_in? ? current_user.login : t(:anonymous) + %td.comment + = render 'tickets/new_comment_form' \ No newline at end of file diff --git a/engines/support/config/initializers/account_lifecycle.rb b/engines/support/config/initializers/account_lifecycle.rb new file mode 100644 index 0000000..d9f04c1 --- /dev/null +++ b/engines/support/config/initializers/account_lifecycle.rb @@ -0,0 +1,3 @@ +ActiveSupport.on_load(:account) do + include AccountExtension::Tickets +end diff --git a/engines/support/config/locales/en.yml b/engines/support/config/locales/en.yml new file mode 100644 index 0000000..342adea --- /dev/null +++ b/engines/support/config/locales/en.yml @@ -0,0 +1,22 @@ +en: + access_ticket_text: > + You can later access this ticket at the URL %{full_url}. You might want to bookmark this page to find it again. + Anybody with this URL will be able to access this ticket, so if you are on a shared computer you might want to + remove it from the browser history. + support_tickets: "Support Tickets" + all_tickets: "All Tickets" + my_tickets: "My Tickets" + open_tickets: "Open Tickets" + closed_tickets: "Closed Tickets" + new_ticket: "New Ticket" + tickets: "Tickets" + subject: "Subject" + destroy: "Destroy" + open: "Open" + closed: "Closed" + close: "Close" + post_reply: "Post Reply" + reply_and_close: "Reply and Close" + description: "Description" + ticket: "Ticket" + regarding_account: "Regarding Account" \ No newline at end of file diff --git a/engines/support/config/routes.rb b/engines/support/config/routes.rb new file mode 100644 index 0000000..23e0c11 --- /dev/null +++ b/engines/support/config/routes.rb @@ -0,0 +1,8 @@ +Rails.application.routes.draw do + scope "(:locale)", :locale => MATCH_LOCALE do + resources :tickets, :except => :edit + resources :users do + resources :tickets, :except => :edit + end + end +end diff --git a/engines/support/leap_web_help.gemspec b/engines/support/leap_web_help.gemspec new file mode 100644 index 0000000..41959d6 --- /dev/null +++ b/engines/support/leap_web_help.gemspec @@ -0,0 +1,18 @@ +$:.push File.expand_path("../../lib", __FILE__) + +require File.expand_path('../../../lib/leap_web/version.rb', __FILE__) + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "leap_web_help" + s.version = LeapWeb::VERSION + s.authors = ["Jessib"] + s.email = ["jessib@leap.se"] + s.homepage = "http://www.leap.se" + s.summary = "Help Desk for LeapWeb" + s.description = "Managing Tickets for a Leap provider" + + s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] + s.test_files = Dir["test/**/*"] + +end diff --git a/engines/support/lib/leap_web_help.rb b/engines/support/lib/leap_web_help.rb new file mode 100644 index 0000000..f5b04aa --- /dev/null +++ b/engines/support/lib/leap_web_help.rb @@ -0,0 +1,4 @@ +require "leap_web_help/engine" + +module LeapWebHelp +end diff --git a/engines/support/lib/leap_web_help/engine.rb b/engines/support/lib/leap_web_help/engine.rb new file mode 100644 index 0000000..dfa763f --- /dev/null +++ b/engines/support/lib/leap_web_help/engine.rb @@ -0,0 +1,4 @@ +module LeapWebHelp + class Engine < ::Rails::Engine + end +end diff --git a/engines/support/lib/tasks/leap_web_help_tasks.rake b/engines/support/lib/tasks/leap_web_help_tasks.rake new file mode 100644 index 0000000..1f38982 --- /dev/null +++ b/engines/support/lib/tasks/leap_web_help_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :leap_web_help do +# # Task goes here +# end diff --git a/engines/support/script/rails b/engines/support/script/rails new file mode 100755 index 0000000..5676ab9 --- /dev/null +++ b/engines/support/script/rails @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/leap_web_help/engine', __FILE__) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/engines/support/test/factories.rb b/engines/support/test/factories.rb new file mode 100644 index 0000000..be04f15 --- /dev/null +++ b/engines/support/test/factories.rb @@ -0,0 +1,18 @@ +FactoryGirl.define do + + factory :ticket do + subject { Faker::Lorem.sentence } + email { Faker::Internet.email } + + factory :ticket_with_comment do + comments_attributes do + { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } + end + end + + factory :ticket_with_creator do + created_by { FactoryGirl.create(:user).id } + end + end + +end diff --git a/engines/support/test/functional/tickets_controller_test.rb b/engines/support/test/functional/tickets_controller_test.rb new file mode 100644 index 0000000..416fb73 --- /dev/null +++ b/engines/support/test/functional/tickets_controller_test.rb @@ -0,0 +1,273 @@ +require 'test_helper' + +class TicketsControllerTest < ActionController::TestCase + + teardown do + # destroy all tickets that were created during the test + Ticket.all.each{|t| t.destroy} + end + + test "should get index if logged in" do + login + get :index + assert_response :success + assert_not_nil assigns(:tickets) + end + + test "no index if not logged in" do + get :index + assert_response :redirect + assert_nil assigns(:tickets) + end + + test "should get new" do + get :new + assert_equal Ticket, assigns(:ticket).class + assert_response :success + end + + test "unauthenticated tickets are visible" do + ticket = find_record :ticket, :created_by => nil + get :show, :id => ticket.id + assert_response :success + end + + test "user tickets are not visible without login" do + user = find_record :user + ticket = find_record :ticket, :created_by => user.id + get :show, :id => ticket.id + assert_response :redirect + assert_redirected_to login_url + end + + test "user tickets are visible to creator" do + user = find_record :user + ticket = find_record :ticket, :created_by => user.id + login user + get :show, :id => ticket.id + assert_response :success + end + + test "other users tickets are not visible" do + other_user = find_record :user + ticket = find_record :ticket, :created_by => other_user.id + login + get :show, :id => ticket.id + assert_response :redirect + assert_redirected_to home_url + end + + test "should create unauthenticated ticket" do + params = {:subject => "unauth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + + assert_difference('Ticket.count') do + post :create, :ticket => params + end + + assert_response :redirect + assert_nil assigns(:ticket).created_by + + assert_equal 1, assigns(:ticket).comments.count + assert_nil assigns(:ticket).comments.first.posted_by + + end + + test "should create authenticated ticket" do + + params = {:subject => "auth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + + login + + assert_difference('Ticket.count') do + post :create, :ticket => params + end + + assert_response :redirect + + assert_not_nil assigns(:ticket).created_by + assert_equal assigns(:ticket).created_by, @current_user.id + assert_equal assigns(:ticket).email, @current_user.email_address + + assert_equal 1, assigns(:ticket).comments.count + assert_not_nil assigns(:ticket).comments.first.posted_by + assert_equal assigns(:ticket).comments.first.posted_by, @current_user.id + end + + test "add comment to unauthenticated ticket" do + ticket = FactoryGirl.create :ticket, :created_by => nil + + assert_difference('Ticket.find(ticket.id).comments.count') do + put :update, :id => ticket.id, + :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } + end + + assert_equal ticket, assigns(:ticket) # still same ticket, with different comments + assert_not_equal ticket.comments, assigns(:ticket).comments # ticket == assigns(:ticket), but they have different comments (which we want) + + end + + + test "add comment to own authenticated ticket" do + + login + ticket = FactoryGirl.create :ticket, :created_by => @current_user.id + + #they should be able to comment if it is their ticket: + assert_difference('Ticket.find(ticket.id).comments.count') do + put :update, :id => ticket.id, + :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } + end + assert_not_equal ticket.comments, assigns(:ticket).comments + assert_not_nil assigns(:ticket).comments.last.posted_by + assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + + end + + + test "cannot comment if it is not your ticket" do + + other_user = find_record :user + login :is_admin? => false, :email => nil + ticket = FactoryGirl.create :ticket, :created_by => other_user.id + # they should *not* be able to comment if it is not their ticket + put :update, :id => ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"not allowed comment"}} } + assert_response :redirect + assert_access_denied + + assert_equal ticket.comments.map(&:body), assigns(:ticket).comments.map(&:body) + + end + + + test "admin add comment to authenticated ticket" do + + other_user = find_record :user + login :is_admin? => true + + ticket = FactoryGirl.create :ticket, :created_by => other_user.id + + #admin should be able to comment: + assert_difference('Ticket.find(ticket.id).comments.count') do + put :update, :id => ticket.id, + :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } + end + assert_not_equal ticket.comments, assigns(:ticket).comments + assert_not_nil assigns(:ticket).comments.last.posted_by + assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + end + + test "tickets by admin" do + other_user = find_record :user + ticket = FactoryGirl.create :ticket, :created_by => other_user.id + + login :is_admin? => true + + get :index, {:admin_status => "all", :open_status => "open"} + assert assigns(:all_tickets).count > 0 + + # if we close one ticket, the admin should have 1 less open ticket + assert_difference('assigns[:all_tickets].count', -1) do + assigns(:tickets).first.close + assigns(:tickets).first.save + get :index, {:admin_status => "all", :open_status => "open"} + end + end + + + test "admin_status mine vs all" do + testticket = FactoryGirl.create :ticket + user = find_record :user + login :is_admin? => true, :email => nil + + get :index, {:open_status => "open"} + assert assigns(:all_tickets).include?(testticket) + get :index, {:user_id => user.id, :open_status => "open"} + assert !assigns(:all_tickets).include?(testticket) + end + + test "commenting on a ticket adds to tickets that are mine" do + testticket = FactoryGirl.create :ticket + user = find_record :admin_user + login user + get :index, {:user_id => user.id, :open_status => "open"} + assert_difference('assigns[:all_tickets].count') do + put :update, :id => testticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} + get :index, {:user_id => user.id, :open_status => "open"} + end + + assert assigns(:all_tickets).include?(assigns(:ticket)) + assert_not_nil assigns(:ticket).comments.last.posted_by + assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + end + + test "admin ticket ordering" do + tickets = FactoryGirl.create_list :ticket, 2 + + login :is_admin? => true, :email => nil + get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_desc'} + + # this will consider all tickets, not just those on first page + first_tick = assigns(:all_tickets).all.first + last_tick = assigns(:all_tickets).all.last + assert first_tick.created_at > last_tick.created_at + + # and now reverse order: + get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_asc'} + + assert_equal first_tick, assigns(:all_tickets).last + assert_equal last_tick, assigns(:all_tickets).first + + assert_not_equal first_tick, assigns(:all_tickets).first + assert_not_equal last_tick, assigns(:all_tickets).last + + end + + test "tickets for regular user" do + login + ticket = FactoryGirl.create :ticket + other_ticket = FactoryGirl.create :ticket + + put :update, :id => ticket.id, + :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } + assert_not_nil assigns(:ticket).comments.last.posted_by + assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + + get :index, {:open_status => "open"} + assert assigns(:all_tickets).count > 0 + assert assigns(:all_tickets).include?(ticket) + assert !assigns(:all_tickets).include?(other_ticket) + + # user should have one more ticket if a new tick gets a comment by this user + assert_difference('assigns[:all_tickets].count') do + put :update, :id => other_ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} + get :index, {:open_status => "open"} + end + assert assigns(:all_tickets).include?(other_ticket) + + # if we close one ticket, the user should have 1 less open ticket + assert_difference('assigns[:all_tickets].count', -1) do + other_ticket.reload + other_ticket.close + other_ticket.save + get :index, {:open_status => "open"} + end + + number_open_tickets = assigns(:all_tickets).count + + # look at closed tickets: + get :index, {:open_status => "closed"} + assert !assigns(:all_tickets).include?(ticket) + assert assigns(:all_tickets).include?(other_ticket) + number_closed_tickets = assigns(:all_tickets).count + + # all tickets should equal closed + open + get :index, {:open_status => "all"} + assert assigns(:all_tickets).include?(ticket) + assert assigns(:all_tickets).include?(other_ticket) + assert_equal assigns(:all_tickets).count, number_closed_tickets + number_open_tickets + + + end + +end + diff --git a/engines/support/test/integration/navigation_test.rb b/engines/support/test/integration/navigation_test.rb new file mode 100644 index 0000000..eec8c0e --- /dev/null +++ b/engines/support/test/integration/navigation_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class NavigationTest < ActionDispatch::IntegrationTest + + # test "the truth" do + # assert true + # end +end + diff --git a/engines/support/test/leap_web_help_test.rb b/engines/support/test/leap_web_help_test.rb new file mode 100644 index 0000000..d74c087 --- /dev/null +++ b/engines/support/test/leap_web_help_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LeapWebHelpTest < ActiveSupport::TestCase + test "truth" do + assert_kind_of Module, LeapWebHelp + end +end diff --git a/engines/support/test/test_helper.rb b/engines/support/test/test_helper.rb new file mode 100644 index 0000000..fff9173 --- /dev/null +++ b/engines/support/test/test_helper.rb @@ -0,0 +1,15 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require File.expand_path('../../../../test/dummy/config/environment', __FILE__) +require "rails/test_help" + +Rails.backtrace_cleaner.remove_silencers! + +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# Load fixtures from the engine +if ActiveSupport::TestCase.method_defined?(:fixture_path=) + ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) +end diff --git a/engines/support/test/unit/account_extension_test.rb b/engines/support/test/unit/account_extension_test.rb new file mode 100644 index 0000000..aba162c --- /dev/null +++ b/engines/support/test/unit/account_extension_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class AccountExtensionTest < ActiveSupport::TestCase + + test "destroying an account triggers ticket destruction" do + t = FactoryGirl.create :ticket_with_creator + u = t.created_by_user + Account.new(u).destroy + assert_equal nil, Ticket.find(t.id) + end + +end diff --git a/engines/support/test/unit/ticket_comment_test.rb b/engines/support/test/unit/ticket_comment_test.rb new file mode 100644 index 0000000..fe8cc95 --- /dev/null +++ b/engines/support/test/unit/ticket_comment_test.rb @@ -0,0 +1,59 @@ +require 'test_helper' + +class TicketCommentTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end + +=begin + setup do + @sample_ticket = Ticket.create :title => 'test ticket' + @sample_ticket.save + end +=end + + test "create" do + + comment2 = TicketComment.new :body => "help my email is broken!" + assert comment2.valid? + #assert_not_nil comment2.posted_at #? + #assert_nil comment2.posted_by #if not logged in #TODO + + #comment.ticket = testticket #Ticket.find_by_title("testing") + #assert_equal testticket.title, comment.ticket.title + + #tc.ticket = Ticket.find_by_title("test title") + #tc.ticket.title + end + +=begin + test "create authenticated comment" do + User.current = 4 + comment2 = TicketComment.new :body => "help my email is broken!" + comment2.valid? #save # should not save comment + assert_not_nil comment2.posted_by + end +=end + + test "add comments" do + testticket = Ticket.create :subject => "testing" + assert_equal testticket.comments.count, 0 + comment = TicketComment.new :body => "my email broke" + #assert comment.valid? #validating or saving necessary for setting posted_at + #assert_not_nil comment.posted_at + + testticket.comments << comment + assert_equal testticket.comments.count, 1 + sleep(1) # so first comment has earlier posted_at time + comment2 = TicketComment.new :body => "my email broke" + testticket.comments << comment2 #this should validate comment2 + testticket.valid? + assert_equal testticket.comments.count, 2 + testticket.reload.destroy + # where should posted_at be set? + #assert_not_nil comment.posted_at + #assert_not_nil testticket.comments.last.posted_at + #assert testticket.comments.first.posted_at < testticket.comments.last.posted_at + end + +end diff --git a/engines/support/test/unit/ticket_test.rb b/engines/support/test/unit/ticket_test.rb new file mode 100644 index 0000000..f5e6ea7 --- /dev/null +++ b/engines/support/test/unit/ticket_test.rb @@ -0,0 +1,88 @@ +require 'test_helper' + +class TicketTest < ActiveSupport::TestCase + + test "ticket with default attribs is valid" do + t = FactoryGirl.build :ticket + assert t.valid? + end + + test "ticket without email is valid" do + t = FactoryGirl.build :ticket, email: "" + assert t.valid? + end + + test "ticket validates email format" do + t = FactoryGirl.build :ticket, email: "aswerssfd" + assert !t.valid? + end + + test "ticket open states" do + t = FactoryGirl.build :ticket + assert t.is_open + t.close + assert !t.is_open + t.reopen + assert t.is_open + end + + test "creation validated" do + @sample = Ticket.new + assert !@sample.is_creator_validated? + #p current_user + @sample.created_by = 22 #current_user + assert @sample.is_creator_validated? + end + + test "destroy all tickets from a user" do + t = FactoryGirl.create :ticket_with_creator + u = t.created_by_user + Ticket.destroy_all_from(u) + assert_equal nil, Ticket.find(t.id) + end +=begin +# TODO: do once have current_user stuff in order + test "code if & only if not creator-validated" do + User.current_test = nil + t1 = Ticket.create :subject => 'test title' + assert_not_nil t1.code + assert_nil t1.created_by + + User.current_test = 4 + t2 = Ticket.create :subject => 'test title' + assert_nil t2.code + assert_not_nil t2.created_by + end +=end + + + test "find tickets user commented on" do + + # clear old tickets just in case + # this will cause RestClient::ResourceNotFound errors if there are multiple copies of the same ticket returned + Ticket.by_includes_post_by.key('123').each {|t| t.destroy} + # TODO: the by_includes_post_by view is only used for tests. Maybe we should get rid of it and change the test to including ordering? + + + testticket = Ticket.create :subject => "test retrieving commented tickets" + comment = TicketComment.new :body => "my email broke", :posted_by => "123" + assert_equal 0, testticket.comments.count + assert_equal [], Ticket.by_includes_post_by.key('123').all + + testticket.comments << comment + testticket.save + assert_equal 1, testticket.reload.comments.count + assert_equal [testticket], Ticket.by_includes_post_by.key('123').all + + comment = TicketComment.new :body => "another comment", :posted_by => "123" + testticket.comments << comment + testticket.save + + # this will ensure that the ticket is only included once, even though the user has commented on the ticket twice: + assert_equal [testticket], Ticket.by_includes_post_by.key('123').all + + testticket.destroy + assert_equal [], Ticket.by_includes_post_by.key('123').all; + end + +end diff --git a/help/Gemfile b/help/Gemfile deleted file mode 100644 index ad7d29b..0000000 --- a/help/Gemfile +++ /dev/null @@ -1,15 +0,0 @@ -source "https://rubygems.org" - -eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) -eval(File.read(File.dirname(__FILE__) + '/..//ui_dependencies.rb')) - -# We require leap_web_core from here so we can use the path option. -gem "leap_web_core", :path => '../core' - -# Declare your gem's dependencies in leap_web_users.gemspec. -# Bundler will treat runtime dependencies like base dependencies, and -# development dependencies will be added by default to the :development group. -gemspec - -# To use debugger -# gem 'ruby-debug' diff --git a/help/README.md b/help/README.md deleted file mode 100644 index c9573e6..0000000 --- a/help/README.md +++ /dev/null @@ -1 +0,0 @@ -Implements a simple, clean, and easy to use help ticketing system. \ No newline at end of file diff --git a/help/Rakefile b/help/Rakefile deleted file mode 100644 index 0e73163..0000000 --- a/help/Rakefile +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env rake - -require 'rake/packagetask' -require 'rubygems/package_task' - -begin - require 'bundler/setup' -rescue LoadError - puts 'You must `gem install bundler` and `bundle install` to run rake tasks' -end -begin - require 'rdoc/task' -rescue LoadError - require 'rdoc/rdoc' - require 'rake/rdoctask' - RDoc::Task = Rake::RDocTask -end - -RDoc::Task.new(:rdoc) do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'LeapWebHelp' - rdoc.options << '--line-numbers' - rdoc.rdoc_files.include('README.rdoc') - rdoc.rdoc_files.include('lib/**/*.rb') -end - -spec = eval(File.read('leap_web_help.gemspec')) -Gem::PackageTask.new(spec) do |p| - p.gem_spec = spec -end - -Bundler::GemHelper.install_tasks - -require 'rake/testtask' - -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.libs << 'test' - t.pattern = 'test/**/*_test.rb' - t.verbose = false -end - - -task :default => :test diff --git a/help/app/assets/javascripts/tickets.js b/help/app/assets/javascripts/tickets.js deleted file mode 100644 index 18537aa..0000000 --- a/help/app/assets/javascripts/tickets.js +++ /dev/null @@ -1,4 +0,0 @@ -//$(document).ready(function () { -// $.fn.editable.defaults.mode = 'inline'; -// $('#subject').editable(); -//}); \ No newline at end of file diff --git a/help/app/controllers/tickets_controller.rb b/help/app/controllers/tickets_controller.rb deleted file mode 100644 index d65ee43..0000000 --- a/help/app/controllers/tickets_controller.rb +++ /dev/null @@ -1,153 +0,0 @@ -class TicketsController < ApplicationController - include AutoTicketsPathHelper - - respond_to :html, :json - #has_scope :open, :type => boolean - - before_filter :require_login, :only => [:index] - before_filter :fetch_ticket, :only => [:show, :update, :destroy] # don't now have an edit method - before_filter :fetch_user - before_filter :set_title - - def new - @ticket = Ticket.new - @ticket.comments.build - end - - def create - @ticket = Ticket.new(params[:ticket]) - - @ticket.comments.last.posted_by = (logged_in? ? current_user.id : nil) #protecting posted_by isn't working, so this should protect it. - @ticket.comments.last.private = false unless admin? - @ticket.created_by = current_user.id if logged_in? - @ticket.email = current_user.email_address if logged_in? and current_user.email_address - - if @ticket.save - flash[:notice] = t(:thing_was_successfully_created, :thing => t(:ticket)) - end - - # cannot set this until ticket has been saved, as @ticket.id will not be set - if !logged_in? and flash[:notice] - flash[:notice] += " " + t(:access_ticket_text, :full_url => ticket_url(@ticket.id)) - end - respond_with(@ticket, :location => auto_ticket_path(@ticket)) - end - - def show - @comment = TicketComment.new - if !@ticket - redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => t(:ticket)) - return - end - end - - def update - if params[:commit] == 'close' - @ticket.is_open = false - @ticket.save - redirect_to_tickets - elsif params[:commit] == 'open' - @ticket.is_open = true - @ticket.save - redirect_to auto_ticket_path(@ticket) - else - @ticket.attributes = cleanup_ticket_params(params[:ticket]) - - if params[:commit] == 'reply_and_close' - @ticket.close - end - - if @ticket.comments_changed? - @ticket.comments.last.posted_by = (current_user ? current_user.id : nil) - @ticket.comments.last.private = false unless admin? - end - - if @ticket.changed? and @ticket.save - flash[:notice] = t(:changes_saved) - redirect_to_tickets - else - flash[:error] = @ticket.errors.full_messages.join(". ") if @ticket.changed? - redirect_to auto_ticket_path(@ticket) - end - end - end - - def index - @all_tickets = Ticket.search(search_options(params)) - @tickets = @all_tickets.page(params[:page]).per(APP_CONFIG[:pagination_size]) - end - - def destroy - # should we allow non-admins to delete their own tickets? i don't think necessary. - @ticket.destroy if admin? - redirect_to auto_tickets_path - end - - protected - - def set_title - @title = t(:tickets) - end - - private - - # - # redirects to ticket index, if appropriate. - # otherwise, just redirects to @ticket - # - def redirect_to_tickets - if logged_in? - if params[:commit] == t(:reply_and_close) - redirect_to auto_tickets_path - else - redirect_to auto_ticket_path(@ticket) - end - else - # if we are not logged in, there is no index to view - redirect_to auto_ticket_path(@ticket) - end - end - - # - # unset comments hash if no new comment was typed - # - def cleanup_ticket_params(ticket) - if ticket && ticket[:comments_attributes] - if ticket[:comments_attributes].values.first[:body].blank? - ticket[:comments_attributes] = nil - end - end - return ticket - end - - def ticket_access? - @ticket and (admin? or !@ticket.created_by or (current_user and current_user.id == @ticket.created_by)) - end - - def fetch_ticket - @ticket = Ticket.find(params[:id]) - if !@ticket and admin? - redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => 'ticket') - return - end - access_denied unless ticket_access? - end - - def fetch_user - if params[:user_id] - @user = User.find(params[:user_id]) - end - end - - # - # clean up params for ticket search - # - def search_options(params) - params.merge( - :admin_status => params[:user_id] ? 'mine' : 'all', - :user_id => @user ? @user.id : current_user.id, - :is_admin => admin? - ) - end - -end diff --git a/help/app/designs/ticket/by_includes_post_by.js b/help/app/designs/ticket/by_includes_post_by.js deleted file mode 100644 index 2eeac89..0000000 --- a/help/app/designs/ticket/by_includes_post_by.js +++ /dev/null @@ -1,13 +0,0 @@ -// TODO: This view is only used in tests--should we keep it? -function(doc) { - var arr = {} - if (doc['type'] == 'Ticket' && doc.comments) { - doc.comments.forEach(function(comment){ - if (comment.posted_by && !arr[comment.posted_by]) { - //don't add duplicates - arr[comment.posted_by] = true; - emit(comment.posted_by, 1); - } - }); - } -} diff --git a/help/app/designs/ticket/by_includes_post_by_and_created_at.js b/help/app/designs/ticket/by_includes_post_by_and_created_at.js deleted file mode 100644 index 72169b0..0000000 --- a/help/app/designs/ticket/by_includes_post_by_and_created_at.js +++ /dev/null @@ -1,12 +0,0 @@ -function(doc) { - var arr = {} - if (doc['type'] == 'Ticket' && doc.comments) { - doc.comments.forEach(function(comment){ - if (comment.posted_by && !arr[comment.posted_by]) { - //don't add duplicates - arr[comment.posted_by] = true; - emit([comment.posted_by, doc.created_at], 1); - } - }); - } -} diff --git a/help/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js b/help/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js deleted file mode 100644 index 33dfe0b..0000000 --- a/help/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js +++ /dev/null @@ -1,12 +0,0 @@ -function(doc) { - var arr = {} - if (doc['type'] == 'Ticket' && doc.comments) { - doc.comments.forEach(function(comment){ - if (comment.posted_by && !arr[comment.posted_by]) { - //don't add duplicates - arr[comment.posted_by] = true; - emit([comment.posted_by, doc.is_open, doc.created_at], 1); - } - }); - } -} diff --git a/help/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js b/help/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js deleted file mode 100644 index 3bd2a74..0000000 --- a/help/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js +++ /dev/null @@ -1,12 +0,0 @@ -function(doc) { - var arr = {} - if (doc['type'] == 'Ticket' && doc.comments) { - doc.comments.forEach(function(comment){ - if (comment.posted_by && !arr[comment.posted_by]) { - //don't add duplicates - arr[comment.posted_by] = true; - emit([comment.posted_by, doc.is_open, doc.updated_at], 1); - } - }); - } -} diff --git a/help/app/designs/ticket/by_includes_post_by_and_updated_at.js b/help/app/designs/ticket/by_includes_post_by_and_updated_at.js deleted file mode 100644 index 2b4304f..0000000 --- a/help/app/designs/ticket/by_includes_post_by_and_updated_at.js +++ /dev/null @@ -1,12 +0,0 @@ -function(doc) { - var arr = {} - if (doc['type'] == 'Ticket' && doc.comments) { - doc.comments.forEach(function(comment){ - if (comment.posted_by && !arr[comment.posted_by]) { - //don't add duplicates - arr[comment.posted_by] = true; - emit([comment.posted_by, doc.updated_at], 1); - } - }); - } -} diff --git a/help/app/helpers/auto_tickets_path_helper.rb b/help/app/helpers/auto_tickets_path_helper.rb deleted file mode 100644 index 93f3cb9..0000000 --- a/help/app/helpers/auto_tickets_path_helper.rb +++ /dev/null @@ -1,53 +0,0 @@ -# -# These "auto" forms of the normal ticket path route helpers allow us to do two things automatically: -# -# (1) include the user in the path if appropriate. -# (2) retain the sort params, if appropriate. -# -# Tickets views with a user_id are limited to that user. For admins, they don't need a user_id for any ticket action. -# -# This is available both to the views and the tickets_controller. -# -module AutoTicketsPathHelper - - protected - - def auto_tickets_path(options={}) - return unless options.class == Hash - options = ticket_view_options.merge options - if @user - user_tickets_path(@user, options) - else - tickets_path(options) - end - end - - def auto_ticket_path(ticket, options={}) - options = ticket_view_options.merge options - if @user - user_ticket_path(@user, ticket, options) - else - ticket_path(ticket, options) - end - end - - def auto_new_ticket_path(options={}) - return unless options.class == Hash - options = ticket_view_options.merge options - if @user - new_user_ticket_path(@user, options) - else - new_ticket_path(options) - end - end - - private - - def ticket_view_options - hsh = {} - hsh[:open_status] = params[:open_status] if params[:open_status] && !params[:open_status].empty? - hsh[:sort_order] = params[:sort_order] if params[:sort_order] && !params[:sort_order].empty? - hsh - end - -end \ No newline at end of file diff --git a/help/app/helpers/tickets_helper.rb b/help/app/helpers/tickets_helper.rb deleted file mode 100644 index 7af50d6..0000000 --- a/help/app/helpers/tickets_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module TicketsHelper - # - # FORM HELPERS - # - - # - # hidden fields that should be added to ever ticket form. - # these are use for proper redirection after successful actions. - # - def hidden_ticket_fields - haml_concat hidden_field_tag('open_status', params[:open_status]) - haml_concat hidden_field_tag('sort_order', params[:sort_order]) - haml_concat hidden_field_tag('user_id', params[:user_id]) - "" - end - - # - # PARAM HELPERS - # - - def search_status - if action?(:index) - params[:open_status] || 'open' - else - nil - end - end - - def search_order - params[:sort_order] || 'updated_at_desc' - end - - # - # LINK HELPERS - # - - def link_to_status(new_status) - if new_status == "open" - label = t(:open_tickets) - elsif new_status == "closed" - label = t(:closed_tickets) - elsif new_status == "all" - label = t(:all_tickets) - end - link_to label, auto_tickets_path(:open_status => new_status, :sort_order => search_order) - end - - def link_to_order(order_field) - if search_order.start_with?(order_field) - # link for currently-filtered field. Link to other direction of this field. - if search_order.end_with? 'asc' - direction = 'desc' - icon_direction = 'up' - else - direction = 'asc' - icon_direction = 'down' - end - arrow = content_tag(:i, '', class: 'icon-arrow-'+ icon_direction) - else - # for not-currently-filtered field, don't display an arrow, and link to descending direction - arrow = '' - direction = 'desc' - end - - if order_field == 'updated' - label = t(:updated) - elsif order_field == 'created' - label = t(:created) - end - - link_to auto_tickets_path(:sort_order => order_field + '_at_' + direction, :open_status => search_status) do - arrow + label - end - end - -end diff --git a/help/app/models/account_extension/tickets.rb b/help/app/models/account_extension/tickets.rb deleted file mode 100644 index f898b56..0000000 --- a/help/app/models/account_extension/tickets.rb +++ /dev/null @@ -1,13 +0,0 @@ -module AccountExtension::Tickets - extend ActiveSupport::Concern - - def destroy_with_tickets - Ticket.destroy_all_from(self.user) - destroy_without_tickets - end - - included do - alias_method_chain :destroy, :tickets - end - -end diff --git a/help/app/models/ticket.rb b/help/app/models/ticket.rb deleted file mode 100644 index cd22758..0000000 --- a/help/app/models/ticket.rb +++ /dev/null @@ -1,105 +0,0 @@ -# -# TODO: thought i should reverse keys for descending, but that didn't work. -# look into whether that should be tweaked, and whether it works okay with -# pagination (seems to now...) -# -# TODO: better validation of email -# -# TODO: don't hardcode strings 'unknown user' and 'unauthenticated user' -# -class Ticket < CouchRest::Model::Base - use_database "tickets" - - property :created_by, String, :protected => true # nil for anonymous tickets, should never be changed - property :regarding_user, String # may be nil or valid username - property :subject, String - property :email, String - property :is_open, TrueClass, :default => true - property :comments, [TicketComment] - - timestamps! - - before_validation :set_email, :set_regarding_user, :on => :create - - design do - view :by_updated_at - view :by_created_at - view :by_created_by - - view :by_is_open_and_created_at - view :by_is_open_and_updated_at - - own_path = Pathname.new(File.dirname(__FILE__)) - load_views(own_path.join('..', 'designs', 'ticket')) - end - - validates :subject, :presence => true - validates :email, :allow_blank => true, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ - - def self.search(options = {}) - @selection = TicketSelection.new(options) - @selection.tickets - end - - def self.destroy_all_from(user) - self.by_created_by.key(user.id).each do |ticket| - ticket.destroy - end - end - - def is_creator_validated? - !!created_by - end - - def set_email - self.email = nil if self.email == "" - end - - def set_regarding_user - self.regarding_user = nil if self.regarding_user == "" - end - - def close - self.is_open = false - end - - def reopen - self.is_open = true - end - - def commenters - commenters = [] - self.comments.each do |comment| - if comment.posted_by - if user = User.find(comment.posted_by) - commenters << user.login if user and !commenters.include?(user.login) - else - commenters << 'unknown user' if !commenters.include?('unknown user') - end - else - commenters << 'unauthenticated user' if !commenters.include?('unauthenticated user') - end - end - commenters.join(', ') - end - - # - # update comments. User should be set by controller. - # - def comments_attributes=(attributes) - if attributes - comment = TicketComment.new(attributes.values.first) - comment.posted_at = Time.now - comments << comment - end - end - - def created_by_user - User.find(self.created_by) - end - - def regarding_user_actual_user - User.find_by_login(self.regarding_user) - end - -end diff --git a/help/app/models/ticket_comment.rb b/help/app/models/ticket_comment.rb deleted file mode 100644 index bed5237..0000000 --- a/help/app/models/ticket_comment.rb +++ /dev/null @@ -1,43 +0,0 @@ -class TicketComment - include CouchRest::Model::Embeddable - - #belongs_to :ticket #is this best way to do it? will want to access all of a tickets comments, so maybe this isn't the way? - property :posted_by, String#, :protected => true #Integer#this should be current_user if that is set, meaning the user is logged in #cannot have it be protected and set via comments_attributes=. also, if it is protected and we set in the tickets_controller, it gets unset. TODO---is this okay to have it not protected and manually check it? We do not users to be able to set this. - # if the current user is not set, then we could just say the comment comes from an 'unauthenticated user', which would be somebody with the secret URL - property :posted_at, Time#, :protected => true - #property :posted_verified, TrueClass, :protected => true #should be true if current_user is set when the comment is created - property :body, String - property :private, TrueClass # private comments are only viewable by admins #this is checked when set, to make sure it was set by an admin - - # ? timestamps! - validates :body, :presence => true - #before_validation :set_time#, :set_posted_by - - #design do - # view :by_posted_at - # view :by_body - #end - - def is_comment_validated? - !!posted_by - end - - def posted_by_user - User.find(posted_by) if posted_by - end - -=begin - #TODO. - #this is resetting all comments associated with the ticket: - def set_time - self.posted_at = Time.now - end -=end - -=begin - def set_posted_by - self.posted_by = User.current if User.current - end -=end - -end diff --git a/help/app/models/ticket_selection.rb b/help/app/models/ticket_selection.rb deleted file mode 100644 index 74d5b78..0000000 --- a/help/app/models/ticket_selection.rb +++ /dev/null @@ -1,71 +0,0 @@ -class TicketSelection - - # - # supported options: - # - # user_id: id of the user (uuid string) - # open_status: open | closed | all - # sort_order: updated_at_desc | updated_at_asc | created_at_desc | created_at_asc - # admin_status: mine | all - # is_admin: true | false - # - def initialize(options = {}) - @user_id = options[:user_id].gsub /[^a-z0-9]/, '' - @open_status = allow options[:open_status], 'open', 'closed', 'all' - @sort_order = allow options[:sort_order], 'updated_at_desc', 'updated_at_asc', 'created_at_desc', 'created_at_asc' - @admin_status = allow options[:admin_status], 'mine', 'all' - @is_admin = allow options[:is_admin], false, true - end - - def tickets - Ticket.send(finder_method).startkey(startkey).endkey(endkey).send(order) - end - - protected - - def allow(source, *allowed) - if allowed.include?(source) - source - else - allowed.first - end - end - - def finder_method - method = 'by_' - method += 'includes_post_by_and_' if only_mine? - method += 'is_open_and_' if @open_status != 'all' - method += @sort_order.sub(/_(de|a)sc$/, '') - end - - def startkey - startkeys = [] - startkeys << @user_id if only_mine? - startkeys << (@open_status == 'open') if @open_status != 'all' - startkeys << 0 - startkeys = startkeys.join if startkeys.length == 1 # want string not array if just one thing in array - startkeys - end - - def endkey - endtime = Time.now + 2.days # TODO. this obviously isn't ideal - if self.startkey.is_a?(Array) - endkeys = self.startkey - endkeys.pop - endkeys << endtime - else - endtime - end - end - - def order - # we have defined the ascending method to return the view itself: - (@sort_order.end_with? 'desc') ? 'descending' : 'ascending' - end - - - def only_mine? - !@is_admin || @admin_status == 'mine' - end - -end diff --git a/help/app/views/tickets/_comment.html.haml b/help/app/views/tickets/_comment.html.haml deleted file mode 100644 index 778ca13..0000000 --- a/help/app/views/tickets/_comment.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -- if admin? or !comment.private # only show comment if user is admin or comment is not private - %tr - %td.user - %div - %strong - - if comment.posted_by_user - = comment.posted_by_user.login - - else - = t(:anonymous) - %div= comment.posted_at.to_s(:short) - - if comment.posted_by_user && comment.posted_by_user.is_admin? - %div - %span.label.label-inverse - = t(:admin) - - if comment.private - %div - %span.label.label-important - = t(:private) - %td.comment - = simple_format(comment.body) \ No newline at end of file diff --git a/help/app/views/tickets/_edit_form.html.haml b/help/app/views/tickets/_edit_form.html.haml deleted file mode 100644 index 714f8ff..0000000 --- a/help/app/views/tickets/_edit_form.html.haml +++ /dev/null @@ -1,48 +0,0 @@ -:ruby - # created by user link - if @ticket.created_by_user - created_by = link_to @ticket.created_by_user.login, @ticket.created_by_user - else - created_by = t(:anonymous) - end - - # regarding user link - if admin? - if @ticket.regarding_user_actual_user - regarding_user_link = link_to @ticket.regarding_user_actual_user.login, @ticket.regarding_user_actual_user - else - regarding_user_link = "(#{t(:unknown)})" - end - else - regarding_user_link = '' - end - -= form_for @ticket do |f| - = hidden_ticket_fields - %p.first - - if @ticket.is_open? - %span.label.label-info= t(:open) - - else - %span.label.label-success= t(:closed) - %span.label.label-clear= t(:created_by_on, :user => created_by, :time => @ticket.created_at.to_s(:short)).html_safe - %div= t(:subject) - = f.text_field :subject, :class => 'large full-width' - .row-fluid - .span4 - %div= t(:status) - = f.select :is_open, [[t(:open), "true"], [t(:closed), "false"]] - .span4 - %div= t(:email) - = f.text_field :email - .span4 - %div - = t(:regarding_account) - = regarding_user_link - = f.text_field :regarding_user - = f.button t(:save), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'save' - - if @ticket.is_open? - = f.button t(:close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'close' - - else - = f.button t(:open), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'open' - - if admin? - = link_to t(:destroy), auto_ticket_path(@ticket), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn' diff --git a/help/app/views/tickets/_new_comment_form.html.haml b/help/app/views/tickets/_new_comment_form.html.haml deleted file mode 100644 index 8418e01..0000000 --- a/help/app/views/tickets/_new_comment_form.html.haml +++ /dev/null @@ -1,13 +0,0 @@ --# --# for posting a new comment to an existing ticket. --# -= simple_form_for @ticket, :html => {:class => 'slim'} do |f| - = hidden_ticket_fields - = f.simple_fields_for :comments, @comment, :wrapper => :none, :html => {:class => 'slim'} do |c| - = c.input :body, :label => false, :as => :text, :input_html => {:class => "full-width", :rows=> 5} - - if admin? - = c.input :private, :as => :boolean, :label => false, :inline_label => true - = f.button :button, t(:post_reply), :name => 'commit', :class => 'btn-primary', :type => 'submit', :value => 'post_reply' - - if logged_in? && @ticket.is_open - = f.button :button, t(:reply_and_close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'reply_and_close' - = link_to t(:cancel), auto_tickets_path, :class => :btn diff --git a/help/app/views/tickets/_tabs.html.haml b/help/app/views/tickets/_tabs.html.haml deleted file mode 100644 index b7b5d3a..0000000 --- a/help/app/views/tickets/_tabs.html.haml +++ /dev/null @@ -1,23 +0,0 @@ --# --# SORT ORDER TABS --# -- unless action?(:new) - %ul.nav.nav-pills.pull-right.slim - %li{:class=> ("active" if search_order.start_with? 'created_at')} - = link_to_order('created') - %li{:class=> ("active" if search_order.start_with? 'updated_at')} - = link_to_order('updated') - --# --# STATUS FILTER TABS --# -%ul.nav.nav-tabs - - if logged_in? - %li{:class => ("active" if search_status == 'open')} - = link_to_status 'open' - %li{:class => ("active" if search_status == 'closed')} - = link_to_status 'closed' - %li{:class => ("active" if search_status == 'all')} - = link_to_status 'all' - %li{:class => ("active" if action?(:new))} - = link_to icon(:plus, :black) + t(:new_ticket), auto_new_ticket_path diff --git a/help/app/views/tickets/_ticket.html.haml b/help/app/views/tickets/_ticket.html.haml deleted file mode 100644 index 5bc33c8..0000000 --- a/help/app/views/tickets/_ticket.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -- url = auto_ticket_path(ticket) -%tr - %td= link_to ticket.subject, url - %td= link_to ticket.created_at.to_s(:short), url - %td= link_to ticket.updated_at.to_s(:short), url - %td= ticket.commenters diff --git a/help/app/views/tickets/index.html.haml b/help/app/views/tickets/index.html.haml deleted file mode 100644 index c02a326..0000000 --- a/help/app/views/tickets/index.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -- @show_navigation = !params[:user_id].nil? - -= render 'tickets/tabs' - -%table.table.table-striped.table-bordered - %thead - %tr - %th= t(:subject) - %th= t(:created) - %th= t(:updated) - %th= t(:voices) - %tbody - - if @tickets.any? - = render @tickets.all - - else - %tr - %td{:colspan=>4}= t(:none) - -= paginate @tickets diff --git a/help/app/views/tickets/new.html.haml b/help/app/views/tickets/new.html.haml deleted file mode 100644 index 8f217a5..0000000 --- a/help/app/views/tickets/new.html.haml +++ /dev/null @@ -1,30 +0,0 @@ -- @show_navigation = !params[:user_id].nil? - -= render 'tickets/tabs' - -- if admin? && @user - - email = @user.email_address - - regarding = @user.login -- elsif logged_in? - - email = current_user.email_address - - regarding = current_user.login - -= simple_form_for @ticket, :validate => true, :html => {:class => 'form-horizontal'} do |f| - = hidden_ticket_fields - = f.input :subject - - if logged_in? - = f.input :email, input_html: {value: email} - = f.input :regarding_user, input_html: {value: regarding} - - else - = f.input :email - = f.input :regarding_user - = f.simple_fields_for :comments, @comment do |c| - = c.input :body, :label => t(:description), :as => :text, :input_html => {:class => "full-width", :rows=> 5} - - if admin? - = c.input :private, :as => :boolean, :label => false, :inline_label => true - .form-actions - = f.button :submit, :class => 'btn-primary', :value => t(:create_thing, :thing => t(:ticket)) - - if logged_in? - = link_to t(:cancel), auto_tickets_path, :class => :btn - - else - = link_to t(:cancel), home_path, :class => 'btn' \ No newline at end of file diff --git a/help/app/views/tickets/show.html.haml b/help/app/views/tickets/show.html.haml deleted file mode 100644 index bfdb773..0000000 --- a/help/app/views/tickets/show.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- @show_navigation = !params[:user_id].nil? - -.ticket - = render 'tickets/edit_form' - %table.table.table-striped.table-bordered - %tbody - = render :partial => 'tickets/comment', :collection => @ticket.comments - %tr - %td.user - = logged_in? ? current_user.login : t(:anonymous) - %td.comment - = render 'tickets/new_comment_form' \ No newline at end of file diff --git a/help/config/initializers/account_lifecycle.rb b/help/config/initializers/account_lifecycle.rb deleted file mode 100644 index d9f04c1..0000000 --- a/help/config/initializers/account_lifecycle.rb +++ /dev/null @@ -1,3 +0,0 @@ -ActiveSupport.on_load(:account) do - include AccountExtension::Tickets -end diff --git a/help/config/locales/en.yml b/help/config/locales/en.yml deleted file mode 100644 index 342adea..0000000 --- a/help/config/locales/en.yml +++ /dev/null @@ -1,22 +0,0 @@ -en: - access_ticket_text: > - You can later access this ticket at the URL %{full_url}. You might want to bookmark this page to find it again. - Anybody with this URL will be able to access this ticket, so if you are on a shared computer you might want to - remove it from the browser history. - support_tickets: "Support Tickets" - all_tickets: "All Tickets" - my_tickets: "My Tickets" - open_tickets: "Open Tickets" - closed_tickets: "Closed Tickets" - new_ticket: "New Ticket" - tickets: "Tickets" - subject: "Subject" - destroy: "Destroy" - open: "Open" - closed: "Closed" - close: "Close" - post_reply: "Post Reply" - reply_and_close: "Reply and Close" - description: "Description" - ticket: "Ticket" - regarding_account: "Regarding Account" \ No newline at end of file diff --git a/help/config/routes.rb b/help/config/routes.rb deleted file mode 100644 index 23e0c11..0000000 --- a/help/config/routes.rb +++ /dev/null @@ -1,8 +0,0 @@ -Rails.application.routes.draw do - scope "(:locale)", :locale => MATCH_LOCALE do - resources :tickets, :except => :edit - resources :users do - resources :tickets, :except => :edit - end - end -end diff --git a/help/leap_web_help.gemspec b/help/leap_web_help.gemspec deleted file mode 100644 index ac6d78d..0000000 --- a/help/leap_web_help.gemspec +++ /dev/null @@ -1,18 +0,0 @@ -$:.push File.expand_path("../lib", __FILE__) - -require File.expand_path('../../lib/leap_web/version.rb', __FILE__) - -# Describe your gem and declare its dependencies: -Gem::Specification.new do |s| - s.name = "leap_web_help" - s.version = LeapWeb::VERSION - s.authors = ["Jessib"] - s.email = ["jessib@leap.se"] - s.homepage = "http://www.leap.se" - s.summary = "Help Desk for LeapWeb" - s.description = "Managing Tickets for a Leap provider" - - s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"] - s.test_files = Dir["test/**/*"] - -end diff --git a/help/lib/leap_web_help.rb b/help/lib/leap_web_help.rb deleted file mode 100644 index f5b04aa..0000000 --- a/help/lib/leap_web_help.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "leap_web_help/engine" - -module LeapWebHelp -end diff --git a/help/lib/leap_web_help/engine.rb b/help/lib/leap_web_help/engine.rb deleted file mode 100644 index dfa763f..0000000 --- a/help/lib/leap_web_help/engine.rb +++ /dev/null @@ -1,4 +0,0 @@ -module LeapWebHelp - class Engine < ::Rails::Engine - end -end diff --git a/help/lib/tasks/leap_web_help_tasks.rake b/help/lib/tasks/leap_web_help_tasks.rake deleted file mode 100644 index 1f38982..0000000 --- a/help/lib/tasks/leap_web_help_tasks.rake +++ /dev/null @@ -1,4 +0,0 @@ -# desc "Explaining what the task does" -# task :leap_web_help do -# # Task goes here -# end diff --git a/help/script/rails b/help/script/rails deleted file mode 100755 index 5676ab9..0000000 --- a/help/script/rails +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. - -ENGINE_ROOT = File.expand_path('../..', __FILE__) -ENGINE_PATH = File.expand_path('../../lib/leap_web_help/engine', __FILE__) - -require 'rails/all' -require 'rails/engine/commands' diff --git a/help/test/factories.rb b/help/test/factories.rb deleted file mode 100644 index be04f15..0000000 --- a/help/test/factories.rb +++ /dev/null @@ -1,18 +0,0 @@ -FactoryGirl.define do - - factory :ticket do - subject { Faker::Lorem.sentence } - email { Faker::Internet.email } - - factory :ticket_with_comment do - comments_attributes do - { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } - end - end - - factory :ticket_with_creator do - created_by { FactoryGirl.create(:user).id } - end - end - -end diff --git a/help/test/functional/tickets_controller_test.rb b/help/test/functional/tickets_controller_test.rb deleted file mode 100644 index 416fb73..0000000 --- a/help/test/functional/tickets_controller_test.rb +++ /dev/null @@ -1,273 +0,0 @@ -require 'test_helper' - -class TicketsControllerTest < ActionController::TestCase - - teardown do - # destroy all tickets that were created during the test - Ticket.all.each{|t| t.destroy} - end - - test "should get index if logged in" do - login - get :index - assert_response :success - assert_not_nil assigns(:tickets) - end - - test "no index if not logged in" do - get :index - assert_response :redirect - assert_nil assigns(:tickets) - end - - test "should get new" do - get :new - assert_equal Ticket, assigns(:ticket).class - assert_response :success - end - - test "unauthenticated tickets are visible" do - ticket = find_record :ticket, :created_by => nil - get :show, :id => ticket.id - assert_response :success - end - - test "user tickets are not visible without login" do - user = find_record :user - ticket = find_record :ticket, :created_by => user.id - get :show, :id => ticket.id - assert_response :redirect - assert_redirected_to login_url - end - - test "user tickets are visible to creator" do - user = find_record :user - ticket = find_record :ticket, :created_by => user.id - login user - get :show, :id => ticket.id - assert_response :success - end - - test "other users tickets are not visible" do - other_user = find_record :user - ticket = find_record :ticket, :created_by => other_user.id - login - get :show, :id => ticket.id - assert_response :redirect - assert_redirected_to home_url - end - - test "should create unauthenticated ticket" do - params = {:subject => "unauth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} - - assert_difference('Ticket.count') do - post :create, :ticket => params - end - - assert_response :redirect - assert_nil assigns(:ticket).created_by - - assert_equal 1, assigns(:ticket).comments.count - assert_nil assigns(:ticket).comments.first.posted_by - - end - - test "should create authenticated ticket" do - - params = {:subject => "auth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} - - login - - assert_difference('Ticket.count') do - post :create, :ticket => params - end - - assert_response :redirect - - assert_not_nil assigns(:ticket).created_by - assert_equal assigns(:ticket).created_by, @current_user.id - assert_equal assigns(:ticket).email, @current_user.email_address - - assert_equal 1, assigns(:ticket).comments.count - assert_not_nil assigns(:ticket).comments.first.posted_by - assert_equal assigns(:ticket).comments.first.posted_by, @current_user.id - end - - test "add comment to unauthenticated ticket" do - ticket = FactoryGirl.create :ticket, :created_by => nil - - assert_difference('Ticket.find(ticket.id).comments.count') do - put :update, :id => ticket.id, - :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } - end - - assert_equal ticket, assigns(:ticket) # still same ticket, with different comments - assert_not_equal ticket.comments, assigns(:ticket).comments # ticket == assigns(:ticket), but they have different comments (which we want) - - end - - - test "add comment to own authenticated ticket" do - - login - ticket = FactoryGirl.create :ticket, :created_by => @current_user.id - - #they should be able to comment if it is their ticket: - assert_difference('Ticket.find(ticket.id).comments.count') do - put :update, :id => ticket.id, - :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } - end - assert_not_equal ticket.comments, assigns(:ticket).comments - assert_not_nil assigns(:ticket).comments.last.posted_by - assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - - end - - - test "cannot comment if it is not your ticket" do - - other_user = find_record :user - login :is_admin? => false, :email => nil - ticket = FactoryGirl.create :ticket, :created_by => other_user.id - # they should *not* be able to comment if it is not their ticket - put :update, :id => ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"not allowed comment"}} } - assert_response :redirect - assert_access_denied - - assert_equal ticket.comments.map(&:body), assigns(:ticket).comments.map(&:body) - - end - - - test "admin add comment to authenticated ticket" do - - other_user = find_record :user - login :is_admin? => true - - ticket = FactoryGirl.create :ticket, :created_by => other_user.id - - #admin should be able to comment: - assert_difference('Ticket.find(ticket.id).comments.count') do - put :update, :id => ticket.id, - :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } - end - assert_not_equal ticket.comments, assigns(:ticket).comments - assert_not_nil assigns(:ticket).comments.last.posted_by - assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - end - - test "tickets by admin" do - other_user = find_record :user - ticket = FactoryGirl.create :ticket, :created_by => other_user.id - - login :is_admin? => true - - get :index, {:admin_status => "all", :open_status => "open"} - assert assigns(:all_tickets).count > 0 - - # if we close one ticket, the admin should have 1 less open ticket - assert_difference('assigns[:all_tickets].count', -1) do - assigns(:tickets).first.close - assigns(:tickets).first.save - get :index, {:admin_status => "all", :open_status => "open"} - end - end - - - test "admin_status mine vs all" do - testticket = FactoryGirl.create :ticket - user = find_record :user - login :is_admin? => true, :email => nil - - get :index, {:open_status => "open"} - assert assigns(:all_tickets).include?(testticket) - get :index, {:user_id => user.id, :open_status => "open"} - assert !assigns(:all_tickets).include?(testticket) - end - - test "commenting on a ticket adds to tickets that are mine" do - testticket = FactoryGirl.create :ticket - user = find_record :admin_user - login user - get :index, {:user_id => user.id, :open_status => "open"} - assert_difference('assigns[:all_tickets].count') do - put :update, :id => testticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} - get :index, {:user_id => user.id, :open_status => "open"} - end - - assert assigns(:all_tickets).include?(assigns(:ticket)) - assert_not_nil assigns(:ticket).comments.last.posted_by - assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - end - - test "admin ticket ordering" do - tickets = FactoryGirl.create_list :ticket, 2 - - login :is_admin? => true, :email => nil - get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_desc'} - - # this will consider all tickets, not just those on first page - first_tick = assigns(:all_tickets).all.first - last_tick = assigns(:all_tickets).all.last - assert first_tick.created_at > last_tick.created_at - - # and now reverse order: - get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_asc'} - - assert_equal first_tick, assigns(:all_tickets).last - assert_equal last_tick, assigns(:all_tickets).first - - assert_not_equal first_tick, assigns(:all_tickets).first - assert_not_equal last_tick, assigns(:all_tickets).last - - end - - test "tickets for regular user" do - login - ticket = FactoryGirl.create :ticket - other_ticket = FactoryGirl.create :ticket - - put :update, :id => ticket.id, - :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } - assert_not_nil assigns(:ticket).comments.last.posted_by - assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id - - get :index, {:open_status => "open"} - assert assigns(:all_tickets).count > 0 - assert assigns(:all_tickets).include?(ticket) - assert !assigns(:all_tickets).include?(other_ticket) - - # user should have one more ticket if a new tick gets a comment by this user - assert_difference('assigns[:all_tickets].count') do - put :update, :id => other_ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} - get :index, {:open_status => "open"} - end - assert assigns(:all_tickets).include?(other_ticket) - - # if we close one ticket, the user should have 1 less open ticket - assert_difference('assigns[:all_tickets].count', -1) do - other_ticket.reload - other_ticket.close - other_ticket.save - get :index, {:open_status => "open"} - end - - number_open_tickets = assigns(:all_tickets).count - - # look at closed tickets: - get :index, {:open_status => "closed"} - assert !assigns(:all_tickets).include?(ticket) - assert assigns(:all_tickets).include?(other_ticket) - number_closed_tickets = assigns(:all_tickets).count - - # all tickets should equal closed + open - get :index, {:open_status => "all"} - assert assigns(:all_tickets).include?(ticket) - assert assigns(:all_tickets).include?(other_ticket) - assert_equal assigns(:all_tickets).count, number_closed_tickets + number_open_tickets - - - end - -end - diff --git a/help/test/integration/navigation_test.rb b/help/test/integration/navigation_test.rb deleted file mode 100644 index eec8c0e..0000000 --- a/help/test/integration/navigation_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -class NavigationTest < ActionDispatch::IntegrationTest - - # test "the truth" do - # assert true - # end -end - diff --git a/help/test/leap_web_help_test.rb b/help/test/leap_web_help_test.rb deleted file mode 100644 index d74c087..0000000 --- a/help/test/leap_web_help_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class LeapWebHelpTest < ActiveSupport::TestCase - test "truth" do - assert_kind_of Module, LeapWebHelp - end -end diff --git a/help/test/test_helper.rb b/help/test/test_helper.rb deleted file mode 100644 index 3381f44..0000000 --- a/help/test/test_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -# Configure Rails Environment -ENV["RAILS_ENV"] = "test" - -require File.expand_path('../../../test/dummy/config/environment', __FILE__) -require "rails/test_help" - -Rails.backtrace_cleaner.remove_silencers! - -# Load support files -Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } - -# Load fixtures from the engine -if ActiveSupport::TestCase.method_defined?(:fixture_path=) - ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) -end diff --git a/help/test/unit/account_extension_test.rb b/help/test/unit/account_extension_test.rb deleted file mode 100644 index aba162c..0000000 --- a/help/test/unit/account_extension_test.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'test_helper' - -class AccountExtensionTest < ActiveSupport::TestCase - - test "destroying an account triggers ticket destruction" do - t = FactoryGirl.create :ticket_with_creator - u = t.created_by_user - Account.new(u).destroy - assert_equal nil, Ticket.find(t.id) - end - -end diff --git a/help/test/unit/ticket_comment_test.rb b/help/test/unit/ticket_comment_test.rb deleted file mode 100644 index fe8cc95..0000000 --- a/help/test/unit/ticket_comment_test.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'test_helper' - -class TicketCommentTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end - -=begin - setup do - @sample_ticket = Ticket.create :title => 'test ticket' - @sample_ticket.save - end -=end - - test "create" do - - comment2 = TicketComment.new :body => "help my email is broken!" - assert comment2.valid? - #assert_not_nil comment2.posted_at #? - #assert_nil comment2.posted_by #if not logged in #TODO - - #comment.ticket = testticket #Ticket.find_by_title("testing") - #assert_equal testticket.title, comment.ticket.title - - #tc.ticket = Ticket.find_by_title("test title") - #tc.ticket.title - end - -=begin - test "create authenticated comment" do - User.current = 4 - comment2 = TicketComment.new :body => "help my email is broken!" - comment2.valid? #save # should not save comment - assert_not_nil comment2.posted_by - end -=end - - test "add comments" do - testticket = Ticket.create :subject => "testing" - assert_equal testticket.comments.count, 0 - comment = TicketComment.new :body => "my email broke" - #assert comment.valid? #validating or saving necessary for setting posted_at - #assert_not_nil comment.posted_at - - testticket.comments << comment - assert_equal testticket.comments.count, 1 - sleep(1) # so first comment has earlier posted_at time - comment2 = TicketComment.new :body => "my email broke" - testticket.comments << comment2 #this should validate comment2 - testticket.valid? - assert_equal testticket.comments.count, 2 - testticket.reload.destroy - # where should posted_at be set? - #assert_not_nil comment.posted_at - #assert_not_nil testticket.comments.last.posted_at - #assert testticket.comments.first.posted_at < testticket.comments.last.posted_at - end - -end diff --git a/help/test/unit/ticket_test.rb b/help/test/unit/ticket_test.rb deleted file mode 100644 index f5e6ea7..0000000 --- a/help/test/unit/ticket_test.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'test_helper' - -class TicketTest < ActiveSupport::TestCase - - test "ticket with default attribs is valid" do - t = FactoryGirl.build :ticket - assert t.valid? - end - - test "ticket without email is valid" do - t = FactoryGirl.build :ticket, email: "" - assert t.valid? - end - - test "ticket validates email format" do - t = FactoryGirl.build :ticket, email: "aswerssfd" - assert !t.valid? - end - - test "ticket open states" do - t = FactoryGirl.build :ticket - assert t.is_open - t.close - assert !t.is_open - t.reopen - assert t.is_open - end - - test "creation validated" do - @sample = Ticket.new - assert !@sample.is_creator_validated? - #p current_user - @sample.created_by = 22 #current_user - assert @sample.is_creator_validated? - end - - test "destroy all tickets from a user" do - t = FactoryGirl.create :ticket_with_creator - u = t.created_by_user - Ticket.destroy_all_from(u) - assert_equal nil, Ticket.find(t.id) - end -=begin -# TODO: do once have current_user stuff in order - test "code if & only if not creator-validated" do - User.current_test = nil - t1 = Ticket.create :subject => 'test title' - assert_not_nil t1.code - assert_nil t1.created_by - - User.current_test = 4 - t2 = Ticket.create :subject => 'test title' - assert_nil t2.code - assert_not_nil t2.created_by - end -=end - - - test "find tickets user commented on" do - - # clear old tickets just in case - # this will cause RestClient::ResourceNotFound errors if there are multiple copies of the same ticket returned - Ticket.by_includes_post_by.key('123').each {|t| t.destroy} - # TODO: the by_includes_post_by view is only used for tests. Maybe we should get rid of it and change the test to including ordering? - - - testticket = Ticket.create :subject => "test retrieving commented tickets" - comment = TicketComment.new :body => "my email broke", :posted_by => "123" - assert_equal 0, testticket.comments.count - assert_equal [], Ticket.by_includes_post_by.key('123').all - - testticket.comments << comment - testticket.save - assert_equal 1, testticket.reload.comments.count - assert_equal [testticket], Ticket.by_includes_post_by.key('123').all - - comment = TicketComment.new :body => "another comment", :posted_by => "123" - testticket.comments << comment - testticket.save - - # this will ensure that the ticket is only included once, even though the user has commented on the ticket twice: - assert_equal [testticket], Ticket.by_includes_post_by.key('123').all - - testticket.destroy - assert_equal [], Ticket.by_includes_post_by.key('123').all; - end - -end diff --git a/lib/extensions/couchrest.rb b/lib/extensions/couchrest.rb index a9a195e..95f5d92 100644 --- a/lib/extensions/couchrest.rb +++ b/lib/extensions/couchrest.rb @@ -45,7 +45,7 @@ module CouchRest def self.load_all_models_with_engines self.load_all_models_without_engines return unless defined?(Rails) - Dir[Rails.root + '*/app/models/**/*.rb'].each do |path| + Dir[Rails.root + 'engines/*/app/models/**/*.rb'].each do |path| require path end end diff --git a/lib/tasks/test.rake b/lib/tasks/test.rake index 3c87b45..d96b625 100644 --- a/lib/tasks/test.rake +++ b/lib/tasks/test.rake @@ -1,18 +1,11 @@ namespace :test do - Rails::SubTestTask.new(:units => "test:prepare") do |t| - t.libs << "test" - t.pattern = '*/test/unit/**/*_test.rb' - end - - Rails::SubTestTask.new(:functionals => "test:prepare") do |t| - t.libs << "test" - t.pattern = '*/test/functional/**/*_test.rb' - end - - Rails::SubTestTask.new(:integration => "test:prepare") do |t| - t.libs << "test" - t.pattern = '*/test/integration/**/*_test.rb' + [:units, :functionals, :integration].each do |type| + Rails::SubTestTask.new(type => "test:prepare") do |t| + t.libs << "test" + subdir = type.to_s.singularize + t.pattern = "engines/*/test/#{subdir}/**/*_test.rb" + end end end diff --git a/test/factories.rb b/test/factories.rb index 980e2aa..ac9333c 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -1,6 +1,8 @@ -Dir.glob(Rails.root.join('*','test','factories.rb')) do |factory_file| +ENGINE_FACTORY_FILES = Rails.root.join('engines','*','test','factories.rb') +Dir.glob(ENGINE_FACTORY_FILES) do |factory_file| require factory_file end + FactoryGirl.define do factory :user do diff --git a/test/test_helper.rb b/test/test_helper.rb index b844b90..d001ac7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,7 +8,7 @@ require 'mocha/setup' Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } # Load support files from all engines -Dir["#{File.dirname(__FILE__)}/../*/test/support/**/*.rb"].each { |f| require f } +Dir["#{File.dirname(__FILE__)}/../engines/*/test/support/**/*.rb"].each { |f| require f } class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... -- cgit v1.2.3