diff --git a/Gemfile b/Gemfile index 816cc4a799c828ed0ae8fcd466f7ee82e1b664f0..ae11e0eb00b561c5d6473fd91df53943bc10fb84 100644 --- a/Gemfile +++ b/Gemfile @@ -60,6 +60,7 @@ end group :test, :development do gem 'thin' + gem 'i18n-missing_translations' end group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index 53e0d0bc0cd61d16d1b9ecec460887976249cd1e..2c229f90a0d7d2bb9afbe58728bae5cf23dbe6b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,6 +122,8 @@ GEM hike (1.2.3) http_accept_language (2.0.0) i18n (0.6.9) + i18n-missing_translations (0.0.2) + i18n (~> 0.6.0) journey (1.0.4) jquery-rails (3.0.4) railties (>= 3.0, < 5.0) @@ -254,6 +256,7 @@ DEPENDENCIES haml (~> 3.1.7) haml-rails (~> 0.3.4) http_accept_language + i18n-missing_translations jquery-rails json kaminari (= 0.13.0) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 35d6cb44cb5541158cbb014b8e7912b93e1e1340..a4560e2358b1c840ff2a5b249439f07ca4758424 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -23,16 +23,6 @@ class ApplicationController < ActionController::Base json: {error: "The server failed to process your request. We'll look into it."} end - # - # Allows us to pass through bold text to flash messages. See format_flash() for where this is reversed. - # - # TODO: move to core - # - def bold(str) - "[b]#{str}[/b]" - end - helper_method :bold - ## ## LOCALE ## diff --git a/app/controllers/controller_extension/flash.rb b/app/controllers/controller_extension/flash.rb new file mode 100644 index 0000000000000000000000000000000000000000..1642141ad3b66839cdb077d1fef8cc2e91e3b835 --- /dev/null +++ b/app/controllers/controller_extension/flash.rb @@ -0,0 +1,43 @@ +module ControllerExtension::Flash + extend ActiveSupport::Concern + + protected + + def flash_for(resource, options = {}) + return unless resource.changed? + add_flash_message_for resource + add_flash_errors_for resource if options[:with_errors] + end + + def add_flash_message_for(resource) + message = flash_message_for(resource) + type = flash_type_for(resource) + if message.present? + flash[type] = message + end + end + + def flash_message_for(resource) + I18n.t flash_i18n_key(resource), + scope: :flash, + cascade: true, + resource: resource.class.model_name.human + end + + def flash_i18n_key(resource) + namespace = [action_name] + namespace += controller_path.split('/') + namespace << flash_type_for(resource) + namespace.join(".") + end + + def flash_type_for(resource) + resource.valid? ? :success : :error + end + + def add_flash_errors_for(resource) + return if resource.valid? + flash[:error] += "<br>" + flash[:error] += resource.errors.full_messages.join(". <br>") + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 90e649a957275cce45259dfccc412d1def624e3f..6de5e1b44aa8a2582a1a90dfe5b1f0ff2c91d3db 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,8 +40,9 @@ module ApplicationHelper end end + # fairly strict sanitation for flash messages def format_flash(msg) - html_escape(msg).gsub('[b]', '<b>').gsub('[/b]', '</b>').html_safe + sanitize(msg, tags: %w(em strong b br), attributes: []) end end diff --git a/app/helpers/link_helper.rb b/app/helpers/link_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..55e392b13f3c4c7fb2c52467efdc19a65e1fc513 --- /dev/null +++ b/app/helpers/link_helper.rb @@ -0,0 +1,49 @@ +module LinkHelper + + # + # markup for bootstrap button + # + # takes same arguments as link_to and adds a 'btn' class. + # In addition: + # * the name will be translated if it is a symbol + # * html_options[:type] will be converted into a btn-type class + # + # example: + # btn :home, home_path, type: [:large, :primary] + # + def btn(*args, &block) + html_options = extract_html_options!(args, &block) + type = Array(html_options.delete(:type)) + type.map! {|t| "btn-#{t}"} + html_options[:class] = concat_classes(html_options[:class], 'btn', type) + args[0] = t(args[0]) if args[0].is_a?(Symbol) + link_to *args, html_options, &block + end + + def destroy_btn(*args, &block) + html_options = extract_html_options!(args, &block) + confirmation = t "#{controller_symbol}.confirm.destroy.are_you_sure", + cascade: true + html_options.merge! method: :delete, confirm: confirmation + btn *args, html_options, &block + end + + # + # concat_classes will combine classes in a fairly flexible way. + # it can handle nil, arrays, space separated strings + # it returns a space separated string of classes. + def concat_classes(*classes) + classes.compact! + classes.map {|c| c.respond_to?(:split) ? c.split(' ') : c } + classes.flatten! + classes.join ' ' + end + + def extract_html_options!(args) + if args.count > 2 or args.count > 1 && block_given? + args.extract_options! + else + {} + end + end +end diff --git a/app/views/common/_action_buttons.html.haml b/app/views/common/_action_buttons.html.haml index c74fcd10f485e57a20775a3a8f6361b2517f0bcc..266abe18ef177ef94dc38f2410f8e3699016c070 100644 --- a/app/views/common/_action_buttons.html.haml +++ b/app/views/common/_action_buttons.html.haml @@ -1,11 +1,11 @@ .home-buttons .row-fluid.second .login.span4 - %span.link= link_to(icon('ok-sign', icon_color) + t(:login), login_path, :class => 'btn') - %span.info= t(:login_info) + %span.link= btn icon('ok-sign') + t(:login), login_path + %span.info= t(:login_info, default: "") .signup.span4 - %span.link= link_to(icon('user', icon_color) + t(:signup), signup_path, :class => 'btn') - %span.info= t(:signup_info) + %span.link= btn icon('user') + t(:signup), signup_path + %span.info= t(:signup_info, default: "") .help.span4 - %span.link= link_to(icon('question-sign', icon_color) + t(:get_help), new_ticket_path, :class => 'btn') - %span.info= t(:help_info) + %span.link= btn icon('question-sign') + t(:get_help), new_ticket_path + %span.info= t(:support_info, default: "") diff --git a/app/views/common/_download_button.html.haml b/app/views/common/_download_button.html.haml index e57c56b279f9a139e43fa0165832e2a4d58fd727..9c268604cb74fb95896878e43b6761e98be98b3f 100644 --- a/app/views/common/_download_button.html.haml +++ b/app/views/common/_download_button.html.haml @@ -2,7 +2,7 @@ .row-fluid.first .span2 .download.span8 - = link_to client_download_url, class: "btn btn-large btn-primary" do + = btn client_download_url, type: [:large, :primary] do = big_icon('download') - = t(:download_client) + = t(:download_bitmask) .span2 diff --git a/app/views/common/_home_page_buttons.html.haml b/app/views/common/_home_page_buttons.html.haml index 8c479837a2202c5223789f2374e9c38fe9e04439..fc6348e9e872f57190a47b323e56d5fa65fd1cc8 100644 --- a/app/views/common/_home_page_buttons.html.haml +++ b/app/views/common/_home_page_buttons.html.haml @@ -1,8 +1,6 @@ -- icon_color = :black - = render 'common/download_button' - if local_assigns[:divider] .row-fluid .span12 = render local_assigns[:divider] -= render 'common/action_buttons', icon_color: icon_color += render 'common/action_buttons' diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml index 75cb8891bc94884121d1582d6517e87935644b26..c7fec22acaf3d72189dfb02f2cc376a97ec36975 100644 --- a/app/views/errors/not_found.html.haml +++ b/app/views/errors/not_found.html.haml @@ -1,7 +1,7 @@ .hero-unit - %h1=t :not_found_title - %h2=t :not_found_subtitle - %p.lead=t :not_found_lead + %h1=t 'errors.title.page_not_found' + %h2=t 'errors.subtitle.page_not_found', default: '' + %p.lead=t 'errors.text.page_not_found', default: '' %a.btn.btn-primary.btn-large{href:'/'} %i.icon-home.icon-white =t :home diff --git a/app/views/errors/server_error.html.haml b/app/views/errors/server_error.html.haml index 68baf201b1e144a1b824c69cae7692eed3e4547b..a4133dafefc02bd5bd6b73e1f9da487cf8b4bc9d 100644 --- a/app/views/errors/server_error.html.haml +++ b/app/views/errors/server_error.html.haml @@ -1,7 +1,7 @@ .hero-unit - %h1=t :server_error_title - %h2=t :server_error_subtitle - %p.lead=t :server_error_lead + %h1=t 'errors.title.server_error' + %h2=t 'errors.subtitle.server_error', default: '' + %p.lead=t 'errors.text.server_error', default: '' %a.btn.btn-primary.btn-large{href:'/'} %i.icon-home.icon-white =t :home diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index a1dd47ae2f2fa180a05ab20d389d0d6b47032514..e827f60c70a65147bff8790d9b17c416758b8b0c 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -2,9 +2,9 @@ %ul.nav.nav-tabs = # this navigation isn't quite right. also, we will want to active for an identity controller once it is added. %li{:class => ("active" if controller?('users', 'overviews') || params[:user_id])} - = link_to t(:users), users_path + = link_to t(".users"), users_path %li{:class => ("active" if controller?('tickets') && !params[:user_id])} - = link_to t(:tickets), tickets_path + = link_to t(".tickets", cascade: true), tickets_path %li = link_to t(:logout), logout_path, :method => :delete - if @user && @show_navigation diff --git a/app/views/layouts/_messages.html.haml b/app/views/layouts/_messages.html.haml index 7ff985f69e28a039c91cfb63ff34081f54eb50f2..18be54fad9b211901ee87112ada8c96b7978619a 100644 --- a/app/views/layouts/_messages.html.haml +++ b/app/views/layouts/_messages.html.haml @@ -1,6 +1,6 @@ #messages - flash.each do |name, msg| - if msg.is_a?(String) - %div{:class => "alert alert-#{name == :notice ? "success" : "error"}"} + %div{:class => "alert alert-#{name == :notice ? "success" : name}"} %a.close{"data-dismiss" => "alert"} × = content_tag :div, format_flash(msg), :id => "flash_#{name}" diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index b81c43de3a9b4c192dc104eb63acedf6187ab50c..94f71f771a77c1dac381a95ed8ff595cd24da128 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -2,6 +2,6 @@ = link_to_navigation t(:overview), @user, :active => (controller?(:users) && action?(:show)) = link_to_navigation t(:account_settings), edit_user_path(@user), :active => (controller?(:users) && !action?(:show)) - # will want link for identity settings - = link_to_navigation t(:support_tickets), auto_tickets_path, :active => controller?(:tickets) + = link_to_navigation t(".tickets"), auto_tickets_path, :active => controller?(:tickets) = link_to_navigation t(:billing_settings), billing_top_link(@user), :active => controller?(:customer, :payments, :subscriptions, :credit_card_info) if APP_CONFIG[:billing] = link_to_navigation t(:logout), logout_path, :method => :delete diff --git a/app/views/pages/pricing.html.haml b/app/views/pages/pricing.html.haml index e339d2756cd1f637350735ddfc9b5675124377c2..983501e4e4c62df58a6a82b4933ab1c33907c309 100644 --- a/app/views/pages/pricing.html.haml +++ b/app/views/pages/pricing.html.haml @@ -1,5 +1,5 @@ %h1= t(:pricing) -%p= link_to(icon('user') + t(:signup), signup_path, :class => 'btn') +%p= btn icon('user') + t(:signup), signup_path - levels = APP_CONFIG[:service_levels] - if levels diff --git a/app/views/pages/terms-of-service.en.md b/app/views/pages/terms-of-service.en.md index 7b57027c337c50c83f5cee1886143b407219b9eb..93490b7542aa92494d4c8eb3649bfc2b7bde0a79 100644 --- a/app/views/pages/terms-of-service.en.md +++ b/app/views/pages/terms-of-service.en.md @@ -3,8 +3,8 @@ This document is our Terms of Service, which describes what activities are allowed, under what conditions we may terminate your account, and asserts our limited liability. It applies to all interactions with **<%=APP_CONFIG[:domain]%>**. Your use of **<%=APP_CONFIG[:domain]%>** services will constitute your agreement to these Terms of Service. <p class="alert alert-info"> - <b>Summary:</b><br/> - (1) If you do anything truly evil, we will terminate your account.<br/> + <b>Summary:</b><br> + (1) If you do anything truly evil, we will terminate your account.<br> (2) We are not liable for any damages related to the use of this service. </p> diff --git a/app/views/users/_destroy_account.html.haml b/app/views/users/_destroy_account.html.haml index 445f3c4a27174849de5498feebfe1a6dc426c2d5..a2c4dddf9b09518e0ffd7a91893fe6461bc78c89 100644 --- a/app/views/users/_destroy_account.html.haml +++ b/app/views/users/_destroy_account.html.haml @@ -8,20 +8,20 @@ - else = t(:admin_destroy_account, :username => @user.login) %p= t(:destroy_account_info) -= link_to user_path(@user), :method => :delete, :confirm => t(:are_you_sure), :class => "btn btn-danger" do += destroy_btn user_path(@user), :type => "danger" do %i.icon-remove.icon-white = t(:destroy_my_account) - if @user != current_user and @user.enabled? %legend = t(:deactivate_account, :username => @user.login) %p= t(:deactivate_description) - = link_to deactivate_user_path(@user), :method => :post, :class => "btn btn-warning" do + = btn deactivate_user_path(@user), :method => :post, :type => "warning" do %i.icon-pause.icon-white = t(:deactivate) - elsif @user != current_user and !@user.enabled? %legend = t(:enable_account, :username => @user.login) %p= t(:enable_description) - = link_to enable_user_path(@user), :method => :post, :class => "btn btn-warning" do + = btn enable_user_path(@user), :method => :post, :type => "warning" do %i.icon-ok.icon-white = t(:enable) diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..e38fdc8ae4dde157715b0d97a8ea64387e688b94 --- /dev/null +++ b/app/views/users/_overview.html.haml @@ -0,0 +1,24 @@ +.overview + + %h2.first= t(".welcome", username: @user.login, cascade: true) + + - if admin? + %p + = t(:created) + = @user.created_at + %br + = t(:updated) + = @user.updated_at + %br + = t(:enabled) + = @user.enabled? + + %p= t(:overview_intro, default: "") + + %ul.unstyled + %li= icon('user') + link_to(t(".account"), edit_user_path(@user)) + - # %li= icon('envelope') + link_to(t(:overview_email), {insert path for user identities, presuambly} + %li= icon('question-sign') + link_to(t(".tickets"), user_tickets_path(@user)) + %li= icon('shopping-cart') + link_to(t(".billing"), billing_top_link(@user)) if APP_CONFIG[:billing] + + diff --git a/app/views/users/new.html.haml b/app/views/users/new.html.haml index bc36068fce91bcafe87cff0984c9929728902b27..41a9d5527b2f27caf317415db2742a71e42e6225 100644 --- a/app/views/users/new.html.haml +++ b/app/views/users/new.html.haml @@ -17,5 +17,5 @@ = f.input :login, :label => t(:username), :required => false, :input_html => { :id => :srp_username } = f.input :password, :required => false, :validate => true, :input_html => { :id => :srp_password } = f.input :password_confirmation, :required => false, :validate => true, :input_html => { :id => :srp_password_confirmation } - = f.button :wrapped, value: t(:signup), cancel: home_path + = f.button :wrapped, cancel: home_path diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index ddc33ab3fafc0b7a70e66a5e22806090a6b88730..da8e4671553dd04df9bdf161aee3aa4030000240 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,30 +1,7 @@ -.overview - - %h2.first= t(:overview_welcome, :username => @user.login) - - - if admin? - %p - = t(:created) - = @user.created_at - %br - = t(:updated) - = @user.updated_at - %br - = t(:enabled) - = @user.enabled? - - %p= t(:overview_intro) - - %ul.unstyled - %li= icon('user') + link_to(t(:overview_account), edit_user_path(@user)) - - # %li= icon('envelope') + link_to(t(:overview_email), {insert path for user identities, presuambly} - %li= icon('question-sign') + link_to(t(:overview_tickets), user_tickets_path(@user)) - %li= icon('shopping-cart') + link_to(t(:overview_billing), billing_top_link(@user)) if APP_CONFIG[:billing] - - - .container-fluid - .row-fluid - %h4 To use bitmask services: - = link_to client_download_url, class: "btn btn-primary" do - %i.icon-arrow-down.icon-white - = t(:download_client) += render 'overview' +.container-fluid + .row-fluid + %h4 To use bitmask services: + = btn client_download_url, type: "primary" do + %i.icon-arrow-down.icon-white + = t(:download_bitmask) diff --git a/config/initializers/add_controller_methods.rb b/config/initializers/add_controller_methods.rb index f572ecbf3d9e9cc50445471fccaefe7e1aa3b4f3..03e8393afbe1fc01807d7efe3a80c250c8a33849 100644 --- a/config/initializers/add_controller_methods.rb +++ b/config/initializers/add_controller_methods.rb @@ -1,4 +1,5 @@ ActiveSupport.on_load(:application_controller) do include ControllerExtension::Authentication include ControllerExtension::TokenAuthentication + include ControllerExtension::Flash end diff --git a/config/initializers/i18n.rb b/config/initializers/i18n.rb index c277a2280e7ce1b77dca8857df07f5deb7bd7401..b209d0058bb8e962bef2c1ef4d0533ebeb36ad61 100644 --- a/config/initializers/i18n.rb +++ b/config/initializers/i18n.rb @@ -8,3 +8,7 @@ MATCH_LOCALE = /(#{I18n.available_locales.join('|')})/ # I18n.available_locales is always an array of symbols, but for comparison with # params we need it to be an array of strings. LOCALES_STRING = I18n.available_locales.map(&:to_s) + +# enable using the cascade option +# see svenfuchs.com/2011/2/11/organizing-translations-with-i18n-cascade-and-i18n-missingtranslations +I18n::Backend::Simple.send(:include, I18n::Backend::Cascade) diff --git a/config/locales/en.yml b/config/locales/en.yml deleted file mode 100644 index 899d659677dc5f2685719a3f84089c11c8d1bc4c..0000000000000000000000000000000000000000 --- a/config/locales/en.yml +++ /dev/null @@ -1,55 +0,0 @@ -en: - home: Home - privacy_policy: Privacy Policy - terms_of_service: Terms of Service - pricing: Pricing - about: About Us - contact: Contact - - not_found_title: Page not found. - not_found_subtitle: "The page you were looking for doesn't exist." - not_found_lead: "You may have mistyped the address or the page may have moved." - server_error_title: Ouch! - server_error_subtitle: We ran into a server error. - server_error_lead: The problem has been logged and we will look into it. - no_such_thing: "No such %{thing}." - - thing_was_successfully_created: "%{thing} was successfully created." - create_thing: "Create %{thing}" - - overview: "Overview" - user_control_panel: "user control panel" - - created: "Created" - created_by_on: "Created by %{user} on %{time}" - updated: "Updated" - - none: "None" - unknown: "Unknown" - admin: "Admin" - anonymous: "Anonymous" - save: "Save" - add: "Add" - remove: "Remove" - changes_saved: "Changes saved successfully." - are_you_sure: "Are you sure? This change cannot be undone." - - download_client: "Download Bitmask" - client_info: "The Bitmask application allows you to use %{provider} services." - all_downloads_info: "It is available for %{clients}." - other_downloads_info: "Bitmask is also available for %{clients}." - login_info: "Log in to change your account settings, create support tickets, and manage payments." - signup_info: "Get a user account via this website. We recommend registering via the Bitmask application instead unless you are only using Bitmask for Android." - welcome: "Welcome to %{provider}." - get_help: "Get Help" - help_info: "Can't login? Create a new support ticket anonymously." - example_email: 'user@domain.org' - os: - linux32: "Linux (32 bit)" - linux64: "Linux (64 bit)" - linux: "GNU/Linux" - windows: "Windows" - android: "Android" - osx: "Mac OS" - other: "(not available for your OS)" - diff --git a/config/locales/errors.en.yml b/config/locales/errors.en.yml new file mode 100644 index 0000000000000000000000000000000000000000..93feab168a28c4f14e857cac3c870ec8843bf296 --- /dev/null +++ b/config/locales/errors.en.yml @@ -0,0 +1,11 @@ +en: + errors: + title: + not_found: Page not found. + server_error: Ouch! + subtitle: + not_found: "The page you were looking for doesn't exist." + server_error: We ran into a server error. + text: + not_found: "You may have mistyped the address or the page may have moved." + server_error: The problem has been logged and we will look into it. diff --git a/config/locales/flash.en.yml b/config/locales/flash.en.yml new file mode 100644 index 0000000000000000000000000000000000000000..7ad28f81ef1aa661585aa62aa6f44fc4fc8fa512 --- /dev/null +++ b/config/locales/flash.en.yml @@ -0,0 +1,10 @@ +en: + flash: + success: "%{resource} was successfully saved." + error: "%{resource} could not be saved." + create: + success: "%{resource} was successfully created." + error: "%{resource} could not be created." + update: + success: "%{resource} was successfully updated." + error: "%{resource} could not be updated." diff --git a/config/locales/footer.en.yml b/config/locales/footer.en.yml new file mode 100644 index 0000000000000000000000000000000000000000..65f8ab2b844745423aaa6b7d5af4abf41f758c09 --- /dev/null +++ b/config/locales/footer.en.yml @@ -0,0 +1,7 @@ +en: + # layout/footer + privacy_policy: Privacy Policy + terms_of_service: Terms of Service + pricing: Pricing + about: About Us + contact: Contact diff --git a/config/locales/generic.en.yml b/config/locales/generic.en.yml new file mode 100644 index 0000000000000000000000000000000000000000..14dafacb2fc17ce10a5825a705bc9fbe04adc4d2 --- /dev/null +++ b/config/locales/generic.en.yml @@ -0,0 +1,25 @@ +en: + signup: "Sign Up" + login: "Log In" + logout: "Log Out" + + cancel: "Cancel" + + created: "Created" + created_by_on: "Created by %{user} on %{time}" + updated: "Updated" + + none: "None" + unknown: "Unknown" + admin: "Admin" + anonymous: "Anonymous" + save: "Save" + add: "Add" + remove: "Remove" + changes_saved: "Changes saved successfully." + are_you_sure: "Are you sure? This change cannot be undone." + + example_email: 'user@domain.org' + + no_such_thing: "No such %{thing}." + create_thing: "Create %{thing}" diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3cdfb1280d77897fc8720ff1c1ec91fa7ba91e7 --- /dev/null +++ b/config/locales/home.en.yml @@ -0,0 +1,8 @@ +en: + home: Home + welcome: "Welcome to %{provider}." + download_bitmask: "Download Bitmask" + + login_info: "Log in to change your account settings, create support tickets, and manage payments." + signup_info: "Get a user account via this website. We recommend registering via the Bitmask application instead unless you are only using Bitmask for Android." + support_info: "Can't login? Create a new support ticket anonymously." diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 5d0c675abf70d2e56773d946904f29e55d6bcc21..4b3588303e31cc04c5570d2bf88534dd1ebb60a2 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -29,8 +29,8 @@ en: helpers: submit: user: - create: "Sign up" + create: "Sign Up" update: "Save" session: - create: "Log in" + create: "Log In" diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml index 0ca5a730fceda765092b00907c593693db447b9a..f0fcb3d574bc1caecc62a29b3215c06ce3126cf4 100644 --- a/config/locales/users.en.yml +++ b/config/locales/users.en.yml @@ -1,10 +1,8 @@ en: + layouts: + users: "Users" + user_control_panel: "user control panel" account_settings: "Account Settings" - logout: "Logout" - none: "None" - signup: "Sign Up" - cancel: "Cancel" - login: "Log In" username: "Username" password: "Password" change_password: "Change Password" @@ -42,11 +40,13 @@ en: # # overview # - overview_welcome: "Welcome %{username}." - overview_intro: "From this user control panel, you can:" - overview_tickets: "Create and check support tickets." - overview_email: "Modify email settings." - overview_account: "Destroy your account." + users: + overview: + welcome: "Welcome %{username}." + intro: "From this user control panel, you can:" + tickets: "Create and check support tickets." + email: "Modify email settings." + account: "Destroy your account." # # rails diff --git a/engines/billing/app/views/credit_card_info/edit.html.haml b/engines/billing/app/views/credit_card_info/edit.html.haml index bd86a4c0f4d4af783c4fa6fcdf3fac22103a0d76..9e44344ab8b9beb42b1ec6e370e434a857ba8a55 100644 --- a/engines/billing/app/views/credit_card_info/edit.html.haml +++ b/engines/billing/app/views/credit_card_info/edit.html.haml @@ -13,5 +13,4 @@ %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 + = f.button :wrapped, 'Save Payment Info', cancel: edit_customer_path(@user.id) diff --git a/engines/billing/app/views/customer/_customer_data.html.haml b/engines/billing/app/views/customer/_customer_data.html.haml index e9df0402900ffe47b47803a320181eb7b4777c13..439ae5cdab8e02f1d3dbe86b5645381a788290e3 100644 --- a/engines/billing/app/views/customer/_customer_data.html.haml +++ b/engines/billing/app/views/customer/_customer_data.html.haml @@ -13,4 +13,4 @@ %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 + = btn t(:edit_saved_data), edit_customer_path(@user.id) diff --git a/engines/billing/app/views/customer/confirm.html.haml b/engines/billing/app/views/customer/confirm.html.haml index 877a8acc54bd56e3dabfb2c34e99a9d18e3f132c..eab9616ae059e3dd7177f93127457a9085fabdaf 100644 --- a/engines/billing/app/views/customer/confirm.html.haml +++ b/engines/billing/app/views/customer/confirm.html.haml @@ -11,4 +11,4 @@ - @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 += btn 'View Customer Info', show_customer_path(@user.id) diff --git a/engines/billing/app/views/customer/edit.html.haml b/engines/billing/app/views/customer/edit.html.haml index e882d53e8a85eba24272951bfd62e23f43d221ba..f461fcc4031e33c73273dbffd2d5e974bf036fcc 100644 --- a/engines/billing/app/views/customer/edit.html.haml +++ b/engines/billing/app/views/customer/edit.html.haml @@ -16,8 +16,8 @@ %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 + = btn t(:change_credit_card), edit_credit_card_info_path(:id => @default_cc.token) = 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 + = btn t(:cancel), show_customer_path(@user) diff --git a/engines/billing/app/views/customer/show.html.haml b/engines/billing/app/views/customer/show.html.haml index ec1779c37e4c81d3a54ad29b71d5f0e27a8718fd..ce4d01ae62c74e36d7a8c5ef7a47315a8cc68a62 100644 --- a/engines/billing/app/views/customer/show.html.haml +++ b/engines/billing/app/views/customer/show.html.haml @@ -9,7 +9,7 @@ - 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) + = btn :transaction_history, user_payments_path(@user) %legend= t(:subscriptions) - if @active_subscription = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription} @@ -19,9 +19,9 @@ - if current_user == @user %p .form-actions - = link_to t(:subscribe_to_plan), new_subscription_path, :class => :btn + = btn :subscribe_to_plan, new_subscription_path %p = link_to t(:all_subscriptions), user_subscriptions_path(@user) .form-actions - = link_to t(:make_donation), new_payment_path, :class => 'btn btn-primary' + = btn :make_donation, new_payment_path, :type => 'primary' diff --git a/engines/billing/app/views/subscriptions/destroy.html.haml b/engines/billing/app/views/subscriptions/destroy.html.haml index 44b43330126a5138fac8d17d5df520097d2e5185..e6e8578e5860cc6d993d04c79d2a9efdcf368471 100644 --- a/engines/billing/app/views/subscriptions/destroy.html.haml +++ b/engines/billing/app/views/subscriptions/destroy.html.haml @@ -4,4 +4,4 @@ Error: = @result.message %p - = link_to 'Customer Information', show_customer_path(@user), :class=> :btn \ No newline at end of file + = btn 'Customer Information', show_customer_path(@user) diff --git a/engines/billing/app/views/subscriptions/index.html.haml b/engines/billing/app/views/subscriptions/index.html.haml index 3d4e8fdc46acda930ad181c9bd84fdbd9f7a54ed..1e3fb258e1c493af8f52b47d8ceea7399591c3a3 100644 --- a/engines/billing/app/views/subscriptions/index.html.haml +++ b/engines/billing/app/views/subscriptions/index.html.haml @@ -5,4 +5,4 @@ - 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 + = btn 'subscribe to plan', new_subscription_path diff --git a/engines/billing/app/views/subscriptions/show.html.haml b/engines/billing/app/views/subscriptions/show.html.haml index 2699db91f534832598ae0499a238f1088bffd967..246ebf0bc82fdda1729ec4965c0d05ac92000c51 100644 --- a/engines/billing/app/views/subscriptions/show.html.haml +++ b/engines/billing/app/views/subscriptions/show.html.haml @@ -3,4 +3,5 @@ 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) +- if allow_cancel_subscription(@subscription) + = destroy_btn :cancel_subscription, user_subscription_path(@user, @subscription.id), type: 'danger' diff --git a/engines/support/app/controllers/tickets_controller.rb b/engines/support/app/controllers/tickets_controller.rb index 99357ab8f93a5371395c21b86ea0cf12637a0712..fab26f3f54190008e0098af7d8f522801345dd57 100644 --- a/engines/support/app/controllers/tickets_controller.rb +++ b/engines/support/app/controllers/tickets_controller.rb @@ -5,8 +5,8 @@ class TicketsController < ApplicationController #has_scope :open, :type => boolean before_filter :require_login, :only => [:index] - before_filter :fetch_ticket, :only => [:show, :update, :destroy] - before_filter :require_ticket_access, :only => [:show, :update, :destroy] + before_filter :fetch_ticket, except: [:new, :create, :index] + before_filter :require_ticket_access, except: [:new, :create, :index] before_filter :fetch_user before_filter :set_title @@ -23,13 +23,13 @@ class TicketsController < ApplicationController @ticket.comments.last.posted_by = current_user.id @ticket.comments.last.private = false unless admin? @ticket.created_by = current_user.id - if @ticket.save - flash[:notice] = t(:thing_was_successfully_created, :thing => t(:ticket)) - if !logged_in? - flash[:notice] += " " + t(:access_ticket_text, :full_url => ticket_url(@ticket.id)) - end + flash_for @ticket + if @ticket.save && !logged_in? + flash[:success] += t 'tickets.access_ticket_text', + full_url: ticket_url(@ticket.id), + default: "" end - respond_with(@ticket, :location => auto_ticket_path(@ticket)) + respond_with @ticket, :location => auto_ticket_path(@ticket) end def show @@ -40,35 +40,33 @@ class TicketsController < ApplicationController end end - def update - if params[:button] == 'close' - @ticket.is_open = false - @ticket.save - redirect_to_tickets - elsif params[:button] == 'open' - @ticket.is_open = true - @ticket.save - redirect_to auto_ticket_path(@ticket) - else - @ticket.attributes = cleanup_ticket_params(params[:ticket]) + def close + @ticket.close + @ticket.save + redirect_to redirection_path + end - if params[:button] == 'reply_and_close' - @ticket.close - end + def open + @ticket.reopen + @ticket.save + redirect_to redirection_path + end - if @ticket.comments_changed? - @ticket.comments.last.posted_by = current_user.id - @ticket.comments.last.private = false unless admin? - end + def update + @ticket.attributes = cleanup_ticket_params(params[:ticket]) - 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 + if params[:button] == 'reply_and_close' + @ticket.close + end + + if @ticket.comments_changed? + @ticket.comments.last.posted_by = current_user.id + @ticket.comments.last.private = false unless admin? end + + flash_for @ticket, with_errors: true + @ticket.save + respond_with @ticket, location: redirection_path end def index @@ -85,25 +83,20 @@ class TicketsController < ApplicationController protected def set_title - @title = t(:tickets) + @title = t("layouts.title.tickets") end private # - # redirects to ticket index, if appropriate. - # otherwise, just redirects to @ticket + # ticket index, if appropriate. + # otherwise, just @ticket # - def redirect_to_tickets - if logged_in? - if params[:button] == t(:reply_and_close) - redirect_to auto_tickets_path - else - redirect_to auto_ticket_path(@ticket) - end + def redirection_path + if logged_in? && params[:button] == t(:reply_and_close) + auto_tickets_path else - # if we are not logged in, there is no index to view - redirect_to auto_ticket_path(@ticket) + auto_ticket_path(@ticket) end end diff --git a/engines/support/app/helpers/tickets_helper.rb b/engines/support/app/helpers/tickets_helper.rb index 7af50d6817d54580854cf92cdc0ef5211d4e8db6..11b02e403f1a2c35cdb29d0da74fc6f0fd8a8186 100644 --- a/engines/support/app/helpers/tickets_helper.rb +++ b/engines/support/app/helpers/tickets_helper.rb @@ -35,13 +35,7 @@ module TicketsHelper # 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 + label = t(:".#{new_status}", cascade: true) link_to label, auto_tickets_path(:open_status => new_status, :sort_order => search_order) end @@ -62,11 +56,7 @@ module TicketsHelper direction = 'desc' end - if order_field == 'updated' - label = t(:updated) - elsif order_field == 'created' - label = t(:created) - end + label = t(:".#{order_field}", cascade: true) link_to auto_tickets_path(:sort_order => order_field + '_at_' + direction, :open_status => search_status) do arrow + label diff --git a/engines/support/app/models/ticket.rb b/engines/support/app/models/ticket.rb index bf5df530286d79431bbf98838a63a4e0701a8783..161507c79096a483be745cdb27ac9f0c5b8806c3 100644 --- a/engines/support/app/models/ticket.rb +++ b/engines/support/app/models/ticket.rb @@ -39,6 +39,8 @@ class Ticket < CouchRest::Model::Base # * valid email address validates :email, :allow_blank => true, :format => /\A(([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,}))?\Z/ + # validates :comments, presence: true + def self.search(options = {}) @selection = TicketSelection.new(options) @selection.tickets diff --git a/engines/support/app/models/ticket_comment.rb b/engines/support/app/models/ticket_comment.rb index bed5237d87efe22db76755fc0c78d4da4d9e43ae..2c5df41eb255f587dbcfe562d8dd312f3d5406c7 100644 --- a/engines/support/app/models/ticket_comment.rb +++ b/engines/support/app/models/ticket_comment.rb @@ -18,6 +18,11 @@ class TicketComment # view :by_body #end + # translations are in the same scope as those of a "proper" couchrest model + def self.i18n_scope + "couchrest" + end + def is_comment_validated? !!posted_by end diff --git a/engines/support/app/views/tickets/_comment.html.haml b/engines/support/app/views/tickets/_comment.html.haml index 778ca13778efa09e53445d6ea18bb346ecd3fb3d..65ec394116fbd7c3570328420f9dc3eaf0a3033a 100644 --- a/engines/support/app/views/tickets/_comment.html.haml +++ b/engines/support/app/views/tickets/_comment.html.haml @@ -1,4 +1,5 @@ -- if admin? or !comment.private # only show comment if user is admin or comment is not private +- # only show comment if user is admin or comment is not private +- if admin? or !comment.private %tr %td.user %div @@ -17,4 +18,4 @@ %span.label.label-important = t(:private) %td.comment - = simple_format(comment.body) \ No newline at end of file + = simple_format(comment.body) diff --git a/engines/support/app/views/tickets/_comments.html.haml b/engines/support/app/views/tickets/_comments.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..0a3b345ba650523eb5bb1d05019bc72e9f307c40 --- /dev/null +++ b/engines/support/app/views/tickets/_comments.html.haml @@ -0,0 +1,8 @@ +%table.table.table-striped.table-bordered + %tbody + = render :partial => 'tickets/comment', :collection => @ticket.comments + %tr + %td.user + = current_user.login || t(:anonymous) + %td.comment + = render 'tickets/new_comment_form' diff --git a/engines/support/app/views/tickets/_edit_form.html.haml b/engines/support/app/views/tickets/_edit_form.html.haml index b8da77964472aa7796ee2a2c053b6af597d70a10..889dac281683da890afa7a4700031c94e15d87dc 100644 --- a/engines/support/app/views/tickets/_edit_form.html.haml +++ b/engines/support/app/views/tickets/_edit_form.html.haml @@ -17,34 +17,30 @@ regarding_user_link = '' end -= simple_form_for @ticket do |f| +- url = url_for([@ticket.is_open? ? :close : :open, @ticket]) += simple_form_for @ticket, url: url do |f| = hidden_ticket_fields %p.first - if @ticket.is_open? %span.label.label-info - %b{style: 'padding:10px'}= t(:open) - = f.button :loading, t(:close), value: 'close', class: 'btn-mini' + %b{style: 'padding:10px'}= t("tickets.status.open") + = f.button :loading, t("tickets.action.close"), class: 'btn-mini' - else %span.label.label-success - %b{style: 'padding:10px'}= t(:closed) - = f.button :loading, t(:open), value: 'open', class: 'btn-mini' - %span.label.label-clear= t(:created_by_on, :user => created_by, :time => @ticket.created_at.to_s(:short)).html_safe + %b{style: 'padding:10px'}= t("tickets.status.closed") + = f.button :loading, t("tickets.action.open"), class: 'btn-mini' + %span.label.label-clear + = t("tickets.created_by_on", user: created_by, time: @ticket.created_at.to_s(:short), cascade: true).html_safe = simple_form_for @ticket do |f| = hidden_ticket_fields - %div= t(:subject) - = f.text_field :subject, :class => 'large full-width' + = f.input :subject, input_html: {:class => 'large full-width'} .row-fluid .span4 - %div= t(:status) - = f.select :is_open, [[t(:open), "true"], [t(:closed), "false"]] + = f.input :is_open, as: :select, collection: [:true, :false], include_blank: false .span4 - %div= t(:email) - = f.text_field :email + = f.input :email .span4 - %div - = t(:regarding_account) - = regarding_user_link - = f.text_field :regarding_user - = f.button :loading, t(:save), :value => 'save' + = f.input :regarding_user, label: Ticket.human_attribute_name(:regarding_user) + regarding_user_link + = f.button :loading - if admin? - = link_to t(:destroy), auto_ticket_path(@ticket), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn' + = destroy_btn t(".destroy", cascade: true), auto_ticket_path(@ticket) diff --git a/engines/support/app/views/tickets/_new_comment_form.html.haml b/engines/support/app/views/tickets/_new_comment_form.html.haml index 40c737fdf62eeee25dd51d72e8cb5c79c604ff88..711421d0fdb4d33f53166d945986ca53dc9d30ed 100644 --- a/engines/support/app/views/tickets/_new_comment_form.html.haml +++ b/engines/support/app/views/tickets/_new_comment_form.html.haml @@ -7,7 +7,7 @@ = 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 :loading, t(:post_reply), class: 'btn-primary', value: 'post_reply' + = f.button :loading, t(".post_reply"), class: 'btn-primary', value: 'post_reply' - if logged_in? && @ticket.is_open - = f.button :loading, t(:reply_and_close), value: 'reply_and_close' - = link_to t(:cancel), auto_tickets_path, :class => :btn + = f.button :loading, t(".reply_and_close"), value: 'reply_and_close' + = btn t(".cancel"), auto_tickets_path diff --git a/engines/support/app/views/tickets/_tabs.html.haml b/engines/support/app/views/tickets/_tabs.html.haml index 445a9093896e02a609c2ea08061442b351689329..7872bb5a158967d5703eb4f7c98f524062e8c045 100644 --- a/engines/support/app/views/tickets/_tabs.html.haml +++ b/engines/support/app/views/tickets/_tabs.html.haml @@ -3,21 +3,17 @@ -# - unless action?(:new) or action?(:create) %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') + - %w(created updated).each do |order| + %li{:class=> ("active" if search_order.start_with? order)} + = link_to_order(order) -# -# 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' + - %w(open closed all).each do |status| + %li{:class => ("active" if search_status == status)} + = link_to_status status %li{:class => ("active" if action?(:new) || action?(:create))} - = link_to icon(:plus, :black) + t(:new_ticket), auto_new_ticket_path + = link_to icon(:plus, :black) + t(".new", cascade: true), auto_new_ticket_path diff --git a/engines/support/app/views/tickets/edit.html.haml b/engines/support/app/views/tickets/edit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..03bda7dfd55ee1b7fbe7428b70354462bc3097dc --- /dev/null +++ b/engines/support/app/views/tickets/edit.html.haml @@ -0,0 +1,6 @@ +- @show_navigation = params[:user_id].present? +- @comment = TicketComment.new + +.ticket + = render 'tickets/edit_form' + = render 'tickets/comments' diff --git a/engines/support/app/views/tickets/index.html.haml b/engines/support/app/views/tickets/index.html.haml index a4df6e37b14f637155f67e8c553464357f094f99..526cd6df155adc493520a977d65fe3171af32b36 100644 --- a/engines/support/app/views/tickets/index.html.haml +++ b/engines/support/app/views/tickets/index.html.haml @@ -5,15 +5,15 @@ %table.table.table-striped.table-bordered %thead %tr - %th= t(:subject) - %th= t(:created) - %th= t(:updated) - %th= t(:voices) + %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) + %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 index 3de5fe90531fd24c387bd08fdaf445e3beb8d3aa..d3580f9faaee97496f0f8b16b803353c76a07b0b 100644 --- a/engines/support/app/views/tickets/new.html.haml +++ b/engines/support/app/views/tickets/new.html.haml @@ -11,7 +11,7 @@ = 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} + = c.input :body, :as => :text, :input_html => {:class => "full-width", :rows=> 5} - if admin? = c.input :private, :as => :boolean, :label => false, :inline_label => true = f.button :wrapped, cancel: (logged_in? ? auto_tickets_path : home_path) diff --git a/engines/support/app/views/tickets/show.html.haml b/engines/support/app/views/tickets/show.html.haml index 4f3c127da6c0d939a516fc8ef2e79164260a161c..99afa2a43d9ce487a92f75bf2dfba93d8fed5b73 100644 --- a/engines/support/app/views/tickets/show.html.haml +++ b/engines/support/app/views/tickets/show.html.haml @@ -2,11 +2,4 @@ .ticket = render 'tickets/edit_form' - %table.table.table-striped.table-bordered - %tbody - = render :partial => 'tickets/comment', :collection => @ticket.comments - %tr - %td.user - = current_user.login || t(:anonymous) - %td.comment - = render 'tickets/new_comment_form' + = render 'tickets/comments' diff --git a/engines/support/config/locales/en.yml b/engines/support/config/locales/en.yml index 342adeada63ca2cf0b974e5b537ca77364d12be4..8d2af67ef1bb32a116afdce5d0ad0dcceefb65a2 100644 --- a/engines/support/config/locales/en.yml +++ b/engines/support/config/locales/en.yml @@ -1,22 +1,105 @@ 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 + support_tickets: "Support" + # translations used in the layout views or @title + layouts: + # fallback for all translations of "tickets" nested below: + tickets: "Tickets" + title: + tickets: "Tickets" + header: + tickets: "Tickets" + navigation: + tickets: "Support Tickets" + # Translations used in the views inside the tickets directory + tickets: + # If these do not exist they will be looked up in the global scope. + all: "All Tickets" + open: "Open Tickets" + closed: "Closed Tickets" + new: "New Ticket" + created: "Created at" + updated: "Updated at" + subject: "couchrest.models.tickets.attributes.subject" + status: + open: "Open" + closed: "Closed" + action: + open: "Open" + close: "Close" + confirm: + destroy: + are_you_sure: "Are you sure you want to destroy this ticket?" + # If you want to be more specific you can use the partial as a scope: + tabs: + all: "All Tickets" + open: "Open Tickets" + closed: "Closed Tickets" + index: + none: "No tickets have been found." + voices: "Voices" + destroy: "Destroy" + post_reply: "Post Reply" + reply_and_close: "Reply and Close" + 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. + # rails i18n + helpers: + # translations used for submit buttons. simple form picks these up + submit: + ticket: + create: "Submit Ticket" + update: "Update Ticket" + # translations for the model and its attributes + # serve as fallback for simpleform labels + couchrest: + models: + ticket: "Ticket" + ticket_comment: "Comment" + attributes: + ticket: + # these will fallback to translations in the "attributes" scope + subject: "Subject" + email: "Email" + regarding_user: "Regarding User" + regarding_account: "Regarding Account" + is_open: "Status" + ticket_comment: + body: "Description" + private: "private" + simple_form: + # labels next to the field + labels: + # these will fallback to the human_name translations of the model + ticket: + # these will fallback to translations in "simple_form.labels.defaults" + regarding_: + # you can be specific about translations for a given action: + #edit: + # regaring_user: + email: "Email" + comments: + # these will fall back to "simple_form.labels.comments" + body: "Description" + # comments: ~ + options: + ticket: + is_open: + "true": "Open" + "false": "Closed" + # mouse over hints for the given fields + hints: + ticket: + email: "Please provide an email address so we can get back to you." + # these will fallback to translations in "simple_form.hints.defaults" + # placeholders inside the fields before anything was typed + #placeholders: + # ticket: ~ + # these will fallback to translations in "simple_form.placeholders.defaults" + + # these are generic defaults. They should be moved into the toplevel + # attributes: + #simple_form: + #labels: + # defaults: diff --git a/engines/support/config/routes.rb b/engines/support/config/routes.rb index 23e0c112abe527727e4e567c9556103021561a19..ca471b3e57c6d87bde5b6b81b1f3cc4e98115430 100644 --- a/engines/support/config/routes.rb +++ b/engines/support/config/routes.rb @@ -1,8 +1,16 @@ Rails.application.routes.draw do - scope "(:locale)", :locale => MATCH_LOCALE do - resources :tickets, :except => :edit + scope "(:locale)", locale: MATCH_LOCALE do + + resources :tickets, except: :edit do + member do + put 'open' + put 'close' + end + end + resources :users do - resources :tickets, :except => :edit + resources :tickets, except: :edit end + end end diff --git a/engines/support/test/factories.rb b/engines/support/test/factories.rb index be04f15a96e5abc1b471fd90498172f067e255b2..bcf41e84fc35bcdcc7baae2c77fe8065a9335659 100644 --- a/engines/support/test/factories.rb +++ b/engines/support/test/factories.rb @@ -6,13 +6,23 @@ FactoryGirl.define do factory :ticket_with_comment do comments_attributes do - { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } + { "0" => { + "body" => Faker::Lorem.sentences.join(" "), + "posted_by" => created_by + } } end end factory :ticket_with_creator do created_by { FactoryGirl.create(:user).id } end + + end + + # TicketComments can not be saved. so only use this with build + # and add to a ticket afterwards + factory :ticket_comment do + body { Faker::Lorem.sentences.join(" ") } end end diff --git a/engines/support/test/functional/ticket_comments_test.rb b/engines/support/test/functional/ticket_comments_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..5cbe2337fe59024da8b8f5e66db0795ea199d6e3 --- /dev/null +++ b/engines/support/test/functional/ticket_comments_test.rb @@ -0,0 +1,101 @@ +require 'test_helper' + +class TicketsCommentsTest < ActionController::TestCase + tests TicketsController + + teardown do + # destroy all tickets that were created during the test + Ticket.all.each{|t| t.destroy} + 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 another users 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 "authenticated comment on an anonymous ticket adds to my tickets" 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 + visible_tickets = Ticket.search admin_status: 'mine', + user_id: @current_user.id, is_admin: false + assert_equal [ticket], visible_tickets.all + 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 "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 + +end diff --git a/engines/support/test/functional/tickets_controller_test.rb b/engines/support/test/functional/tickets_controller_test.rb index fc4a6f86f3ddd141177405ae8e92784823bc126d..1d074cc7c5148dde662eeb026dd767977f432e85 100644 --- a/engines/support/test/functional/tickets_controller_test.rb +++ b/engines/support/test/functional/tickets_controller_test.rb @@ -1,5 +1,14 @@ require 'test_helper' +# +# Tests for the basic actions in the TicketsController +# +# Also see +# TicketCommentsTest +# TicketsListTest +# +# for detailed functional tests for comments and index action. +# class TicketsControllerTest < ActionController::TestCase teardown do @@ -104,180 +113,20 @@ class TicketsControllerTest < ActionController::TestCase 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 - + test "close 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 - + open_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + post :close, id: open_ticket.id + assert !open_ticket.reload.is_open 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 + test "reopen ticket" 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 - - + open_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id, is_open: false + post :open, id: open_ticket.id + assert open_ticket.reload.is_open end end diff --git a/engines/support/test/functional/tickets_list_test.rb b/engines/support/test/functional/tickets_list_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..4c4cdef08b9cd0f453ad17543b7cf4b558f24f2d --- /dev/null +++ b/engines/support/test/functional/tickets_list_test.rb @@ -0,0 +1,122 @@ +require 'test_helper' + +class TicketsListTest < ActionController::TestCase + tests TicketsController + + teardown do + # destroy all records that were created during the test + Ticket.all.each{|t| t.destroy} + User.all.each{|u| u.account.destroy} + 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 "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 "own tickets include tickets commented upon" do + login + ticket = FactoryGirl.create :ticket + other_ticket = FactoryGirl.create :ticket + comment = FactoryGirl.build(:ticket_comment, posted_by: @current_user.id) + ticket.comments << comment + ticket.save + + get :index, {:open_status => "open"} + assert assigns(:all_tickets).count > 0 + assert assigns(:all_tickets).include?(ticket) + assert !assigns(:all_tickets).include?(other_ticket) + end + + test "list all tickets created by user" do + login + ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + other_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + get :index, {:open_status => "open"} + assert_equal 2, assigns[:all_tickets].count + end + + test "closing ticket removes from open tickets list" do + login + ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + other_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + other_ticket.reload + other_ticket.close + other_ticket.save + get :index, {:open_status => "open"} + assert_equal 1, assigns[:all_tickets].count + end + + test "list closed tickets only" do + login + open_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + closed_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id, is_open: false + get :index, {:open_status => "closed"} + assert_equal [closed_ticket], assigns(:all_tickets).all + end + + test "list all tickets inludes closed + open" do + login + open_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id + closed_ticket = FactoryGirl.create :ticket_with_comment, + created_by: @current_user.id, is_open: false + get :index, {:open_status => "all"} + assert_equal 2, assigns(:all_tickets).count + assert assigns(:all_tickets).include?(open_ticket) + assert assigns(:all_tickets).include?(closed_ticket) + end +end diff --git a/engines/support/test/integration/create_ticket_test.rb b/engines/support/test/integration/create_ticket_test.rb index 0f8453cf057b984e34813a404fdc4f0adafc282a..90e9a8a9ab1ec9bb341168a282f974b3748e7545 100644 --- a/engines/support/test/integration/create_ticket_test.rb +++ b/engines/support/test/integration/create_ticket_test.rb @@ -7,7 +7,7 @@ class CreateTicketTest < BrowserIntegrationTest click_on 'Get Help' fill_in 'Subject', with: 'test ticket' fill_in 'Description', with: 'description of the problem goes here' - click_on 'Create Ticket' + click_on 'Submit Ticket' assert page.has_content?("Ticket was successfully created.") assert page.has_content?("You can later access this ticket at the URL") assert page.has_content?(current_url) @@ -20,12 +20,12 @@ class CreateTicketTest < BrowserIntegrationTest click_on 'Get Help' fill_in 'Subject', with: 'test ticket' fill_in 'Email', with: 'invalid data' - fill_in 'Regarding user', with: 'some user' + fill_in 'Regarding User', with: 'some user' fill_in 'Description', with: 'description of the problem goes here' - click_on 'Create Ticket' + click_on 'Submit Ticket' assert page.has_content?("is invalid") assert_equal 'invalid data', find_field('Email').value - assert_equal 'some user', find_field('Regarding user').value + assert_equal 'some user', find_field('Regarding User').value end test "prefills fields" do @@ -35,7 +35,7 @@ class CreateTicketTest < BrowserIntegrationTest click_on "New Ticket" email = "#{@user.login}@#{APP_CONFIG[:domain]}" assert_equal email, find_field('Email').value - assert_equal @user.login, find_field('Regarding user').value + assert_equal @user.login, find_field('Regarding User').value end test "no prefill of email without email service" do @@ -44,7 +44,7 @@ class CreateTicketTest < BrowserIntegrationTest click_on "Support Tickets" click_on "New Ticket" assert_equal "", find_field('Email').value - assert_equal @user.login, find_field('Regarding user').value + assert_equal @user.login, find_field('Regarding User').value end test "cleared email field should remain clear" do @@ -55,7 +55,7 @@ class CreateTicketTest < BrowserIntegrationTest fill_in 'Subject', with: 'test ticket' fill_in 'Email', with: '' fill_in 'Description', with: 'description of the problem goes here' - click_on 'Create Ticket' + click_on 'Submit Ticket' ticket = Ticket.last assert_equal "", ticket.email ticket.destroy diff --git a/lib/extensions/couchrest.rb b/lib/extensions/couchrest.rb index 95f5d92ea2c97afa73bbf1ee5c11719ea2f199b3..df83c9f6e303f017f4c4fe23462b3c610459d1b4 100644 --- a/lib/extensions/couchrest.rb +++ b/lib/extensions/couchrest.rb @@ -1,5 +1,9 @@ module CouchRest module Model + class Base + extend ActiveModel::Naming + extend ActiveModel::Translation + end module Designs class View diff --git a/test/integration/browser/account_test.rb b/test/integration/browser/account_test.rb index 8e6d433502463deb616b15d820d3c3338add4990..aea54061e61863cb0b6bc561c007ac26df8bfdcd 100644 --- a/test/integration/browser/account_test.rb +++ b/test/integration/browser/account_test.rb @@ -9,7 +9,7 @@ class AccountTest < BrowserIntegrationTest test "signup successfully" do username, password = submit_signup assert page.has_content?("Welcome #{username}") - click_on 'Logout' + click_on 'Log Out' assert page.has_content?("Log In") assert_equal '/', current_path assert user = User.find_by_login(username) @@ -30,7 +30,7 @@ class AccountTest < BrowserIntegrationTest test "successful login" do username, password = submit_signup - click_on 'Logout' + click_on 'Log Out' attempt_login(username, password) assert page.has_content?("Welcome #{username}") within('.sidenav li.active') do @@ -90,7 +90,7 @@ class AccountTest < BrowserIntegrationTest fill_in 'Password confirmation', with: "other password" click_on 'Save' end - click_on 'Logout' + click_on 'Log Out' attempt_login(@user.login, "other password") assert page.has_content?("Welcome #{@user.login}") end diff --git a/test/integration/browser/session_test.rb b/test/integration/browser/session_test.rb index fb20847288499296b5e5cdf4dffbdad8f86e92c5..d52508aef638815c68dbf0afe4f1658291c321c3 100644 --- a/test/integration/browser/session_test.rb +++ b/test/integration/browser/session_test.rb @@ -4,7 +4,7 @@ class SessionTest < BrowserIntegrationTest test "valid session" do login - assert page.has_content?("Logout") + assert page.has_content?("Log Out") end test "expired session" do diff --git a/test/test_helper.rb b/test/test_helper.rb index d001ac7eeaed9126fd9debecac5c260e2414b848..7959ddb56c454a07d8a5b5ca423d919102ca4674 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -27,4 +27,6 @@ class ActiveSupport::TestCase File.join(Rails.root, 'test', 'files', name) end + require 'i18n/missing_translations' + at_exit { I18n.missing_translations.dump } end