summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelijah <elijah@riseup.net>2015-04-30 00:32:33 -0700
committerelijah <elijah@riseup.net>2015-04-30 00:32:33 -0700
commit63871baf6061668b162972193c55b5a8f7490797 (patch)
treeca8cd5fbab18cbe59b728a123f450140ed98f519
parentc3b133cb6f02003ab934e5008e108f489ace4158 (diff)
added support for email notifications of ticket changes
-rw-r--r--Gemfile13
-rw-r--r--Gemfile.lock4
-rw-r--r--app/controllers/controller_extension/flash.rb18
-rw-r--r--app/controllers/users_controller.rb5
-rw-r--r--app/models/anonymous_user.rb4
-rw-r--r--app/models/user.rb10
-rw-r--r--app/views/users/_contact_email.html.haml9
-rw-r--r--config/defaults.yml24
-rw-r--r--config/locales/users.en.yml5
-rw-r--r--engines/support/Gemfile6
-rw-r--r--engines/support/Gemfile.lock14
-rw-r--r--engines/support/Rakefile12
-rw-r--r--engines/support/app/controllers/tickets_controller.rb8
-rw-r--r--engines/support/app/helpers/ticket_i18n_helper.rb20
-rw-r--r--engines/support/app/mailers/.gitkeep (renamed from app/mailers/.gitkeep)0
-rw-r--r--engines/support/app/mailers/ticket_mailer.rb82
-rw-r--r--engines/support/app/models/ticket.rb43
-rw-r--r--engines/support/app/models/ticket_comment.rb40
-rw-r--r--engines/support/app/views/ticket_mailer/notice.text.erb5
-rw-r--r--engines/support/config/locales/en.yml8
-rw-r--r--engines/support/config/locales/es.yml3
-rw-r--r--engines/support/leap_web_help.gemspec6
-rw-r--r--engines/support/test/factories.rb2
-rw-r--r--engines/support/test/functional/ticket_mailer_test.rb7
-rw-r--r--engines/support/test/unit/ticket_test.rb2
-rw-r--r--lib/en_US.yml106
-rw-r--r--lib/tasks/i18n.rake44
27 files changed, 404 insertions, 96 deletions
diff --git a/Gemfile b/Gemfile
index a545dd6..419b870 100644
--- a/Gemfile
+++ b/Gemfile
@@ -48,10 +48,6 @@ group :production do
# for list of supported runtimes.
end
-## MISC
-gem 'certificate_authority', # unreleased so far ... but leap_web_certs need it
- :git => 'https://github.com/cchandler/certificate_authority.git'
-
##
## ENVIRONMENT SPECIFIC GEMS
##
@@ -102,3 +98,12 @@ end
custom_gems.each do |name, gem_info|
gem gem_info[:name], :path => gem_info[:path]
end
+
+##
+## DEPENDENCIES FOR OPTIONAL ENGINES
+##
+
+gem 'certificate_authority', # unreleased so far ... but leap_web_certs need it
+ :git => 'https://github.com/cchandler/certificate_authority.git'
+
+gem 'valid_email' # used by leap_web_help
diff --git a/Gemfile.lock b/Gemfile.lock
index 4de7e05..e140303 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -235,6 +235,9 @@ GEM
uglifier (1.2.7)
execjs (>= 0.3.0)
multi_json (~> 1.3)
+ valid_email (0.0.7)
+ activemodel
+ mail
warden (1.2.3)
rack (>= 1.0)
websocket-driver (0.5.1)
@@ -284,3 +287,4 @@ DEPENDENCIES
therubyracer (~> 0.10.2)
thin
uglifier (~> 1.2.7)
+ valid_email
diff --git a/app/controllers/controller_extension/flash.rb b/app/controllers/controller_extension/flash.rb
index 1642141..45072cf 100644
--- a/app/controllers/controller_extension/flash.rb
+++ b/app/controllers/controller_extension/flash.rb
@@ -4,9 +4,13 @@ module ControllerExtension::Flash
protected
def flash_for(resource, options = {})
- return unless resource.changed?
- add_flash_message_for resource
- add_flash_errors_for resource if options[:with_errors]
+ if resource.is_a? Exception
+ add_flash_message_for_exception resource
+ else
+ return unless resource.changed?
+ add_flash_message_for resource
+ add_flash_errors_for resource if options[:with_errors]
+ end
end
def add_flash_message_for(resource)
@@ -40,4 +44,12 @@ module ControllerExtension::Flash
flash[:error] += "<br>"
flash[:error] += resource.errors.full_messages.join(". <br>")
end
+
+ #
+ # This is pretty crude. It would be good to l10n in the future.
+ #
+ def add_flash_message_for_exception(exc)
+ flash[:error] = exc.to_s
+ end
+
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index dcf7607..3943afc 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -40,7 +40,10 @@ class UsersController < ApplicationController
## added so updating service level works, but not sure we will actually want this. also not sure that this is place to prevent user from updating own effective service level, but here as placeholder:
def update
@user.update_attributes(params[:user]) unless (!admin? and params[:user][:effective_service_level])
- respond_with @user
+ if @user.valid?
+ flash[:notice] = I18n.t(:changes_saved)
+ end
+ respond_with @user, :location => edit_user_path(@user)
end
def deactivate
diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb
index 87239eb..0c1f540 100644
--- a/app/models/anonymous_user.rb
+++ b/app/models/anonymous_user.rb
@@ -28,4 +28,8 @@ class AnonymousUser < Object
def messages
[]
end
+
+ def is_anonymous?
+ true
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 52e20dd..d44df40 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -6,6 +6,8 @@ class User < CouchRest::Model::Base
property :login, String, :accessible => true
property :password_verifier, String, :accessible => true
property :password_salt, String, :accessible => true
+ property :contact_email, String, :accessible => true
+ property :contact_email_key, String, :accessible => true
property :enabled, TrueClass, :default => true
@@ -33,6 +35,10 @@ class User < CouchRest::Model::Base
:confirmation => true,
:format => { :with => /.{8}.*/, :message => "needs to be at least 8 characters long" }
+ validates :contact_email, :allow_blank => true,
+ :email => true,
+ :mx_with_fallback => true
+
timestamps!
design do
@@ -90,6 +96,10 @@ class User < CouchRest::Model::Base
APP_CONFIG['admins'].include? self.login
end
+ def is_anonymous?
+ false
+ end
+
def most_recent_tickets(count=3)
Ticket.for_user(self).limit(count).all #defaults to having most recent updated first
end
diff --git a/app/views/users/_contact_email.html.haml b/app/views/users/_contact_email.html.haml
new file mode 100644
index 0000000..ad768b7
--- /dev/null
+++ b/app/views/users/_contact_email.html.haml
@@ -0,0 +1,9 @@
+%legend= t(:contact_email)
+= simple_form_for @user, {:validate => true} do |f|
+ %p= t(:contact_email_info)
+ = f.input :contact_email, :label => false
+ -# %p= t(:public_key)
+ -# = f.text_area :contact_email_key, {:class => "full-width", :rows => 4}
+ .control-group
+ .controls
+ = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} \ No newline at end of file
diff --git a/config/defaults.yml b/config/defaults.yml
index b14955c..ca5107f 100644
--- a/config/defaults.yml
+++ b/config/defaults.yml
@@ -34,13 +34,29 @@ common: &common
token_expires_after: 60
# handles that will be blocked from being used as logins or email aliases
# in addition to the ones in /etc/passwd and http://tools.ietf.org/html/rfc2142
- handle_blacklist: ['certmaster', 'ssladmin', 'arin-admin', 'administrator', 'www-data', 'maildrop', 'postmaster', 'admin', 'contact', 'info']
+ handle_blacklist:
+ - certmaster
+ - ssladmin
+ - arin-admin
+ - administrator
+ - www-data
+ - maildrop
+ - postmaster
+ - admin
+ - contact
+ - info
+ - noreply
+ - robot
+ - helpdesk
+ - help
+ - tickets
+ - owner
# handles that will be allowed despite being in /etc/passwd or rfc2142
handle_whitelist: []
# actions enabled in the account settings
# see /users/app/views/users/_edit.html.haml for a list.
- user_actions: ['destroy_account']
- admin_actions: ['change_pgp_key', 'change_service_level', 'destroy_account']
+ user_actions: ['contact_email', 'destroy_account']
+ admin_actions: ['contact_email', 'change_pgp_key', 'change_service_level', 'destroy_account']
billing: ~
default_locale: :en
available_locales:
@@ -54,6 +70,8 @@ common: &common
soledad-service: 'public/1/config/soledad-service.json'
eip-service: 'public/1/config/eip-service.json'
smtp-service: 'public/1/config/smtp-service.json'
+ mailer:
+ from_address: 'noreply'
service_levels: &service_levels
service_levels:
diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml
index bebf1cf..89307dd 100644
--- a/config/locales/users.en.yml
+++ b/config/locales/users.en.yml
@@ -39,6 +39,11 @@ en:
payment_one_month_warning: "We hope you have been enjoying this service this past month. Please sign up to pay within the next month, by %{date_in_one_month}. Directions for payment are available at INSERT_URL"
bye: "Goodbye!"
bye_message: "So long and thanks for all the fish."
+ contact_email: "Contact Email"
+ contact_email_info: >
+ You may specify an email address where we can contact you if there is a
+ problem with your account. For privacy reasons, you might prefer to leave
+ this empty.
#
# overview
diff --git a/engines/support/Gemfile b/engines/support/Gemfile
index ad7d29b..0296338 100644
--- a/engines/support/Gemfile
+++ b/engines/support/Gemfile
@@ -1,11 +1,5 @@
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.
diff --git a/engines/support/Gemfile.lock b/engines/support/Gemfile.lock
new file mode 100644
index 0000000..fe4ce6c
--- /dev/null
+++ b/engines/support/Gemfile.lock
@@ -0,0 +1,14 @@
+PATH
+ remote: .
+ specs:
+ leap_web_help (0.6.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ leap_web_help!
diff --git a/engines/support/Rakefile b/engines/support/Rakefile
index 0e73163..4787e78 100644
--- a/engines/support/Rakefile
+++ b/engines/support/Rakefile
@@ -1,8 +1,4 @@
#!/usr/bin/env rake
-
-require 'rake/packagetask'
-require 'rubygems/package_task'
-
begin
require 'bundler/setup'
rescue LoadError
@@ -18,16 +14,14 @@ end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
- rdoc.title = 'LeapWebHelp'
+ rdoc.title = 'Blah'
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
diff --git a/engines/support/app/controllers/tickets_controller.rb b/engines/support/app/controllers/tickets_controller.rb
index 602bbd9..c1abfa2 100644
--- a/engines/support/app/controllers/tickets_controller.rb
+++ b/engines/support/app/controllers/tickets_controller.rb
@@ -62,6 +62,7 @@ class TicketsController < ApplicationController
if @ticket.comments_changed?
@ticket.comments.last.posted_by = current_user.id
@ticket.comments.last.private = false unless admin?
+ send_email_update(@ticket, @ticket.comments.last)
end
flash_for @ticket, with_errors: true
@@ -153,4 +154,11 @@ class TicketsController < ApplicationController
)
end
+ def send_email_update(ticket, comment)
+ TicketMailer.send_notice(ticket, comment, ticket_url(ticket))
+ rescue StandardError => exc
+ flash_for(exc)
+ raise exc if Rails.env == 'development'
+ end
+
end
diff --git a/engines/support/app/helpers/ticket_i18n_helper.rb b/engines/support/app/helpers/ticket_i18n_helper.rb
new file mode 100644
index 0000000..61b4cf2
--- /dev/null
+++ b/engines/support/app/helpers/ticket_i18n_helper.rb
@@ -0,0 +1,20 @@
+module TicketI18nHelper
+
+ #
+ # outputs translations for all the possible translated strings.
+ # used in emails, sense we don't know the locale of the recipient.
+ #
+ def t_all_available(key)
+ default = I18n.t(key, locale: I18n.default_locale)
+ result = []
+ result << "[#{I18n.default_locale}] #{default}"
+ I18n.available_locales.each do |locale|
+ text = I18n.t(key, locale: locale, default: default)
+ if text != default
+ result << "[#{locale}] #{text}"
+ end
+ end
+ result.join("\n")
+ end
+
+end
diff --git a/app/mailers/.gitkeep b/engines/support/app/mailers/.gitkeep
index e69de29..e69de29 100644
--- a/app/mailers/.gitkeep
+++ b/engines/support/app/mailers/.gitkeep
diff --git a/engines/support/app/mailers/ticket_mailer.rb b/engines/support/app/mailers/ticket_mailer.rb
new file mode 100644
index 0000000..9a83345
--- /dev/null
+++ b/engines/support/app/mailers/ticket_mailer.rb
@@ -0,0 +1,82 @@
+class TicketMailer < ActionMailer::Base
+
+ helper :ticket_i18n
+
+ def self.send_notice(ticket, comment, url)
+ reply_recipients(ticket, comment.user).each do |email, key|
+ TicketMailer.notice(ticket, comment, url, email, key).deliver
+ end
+ end
+
+ #
+ # ticket - the ticket in question
+ # author - user who created last comment
+ # url - url of the ticket
+ # email - email to send reply notice to
+ # key - public key for email
+ #
+ # TODO: OpenPGP encrypt email to the public key.
+ #
+ def notice(ticket, comment, url, email, key)
+ @url = url
+ @email = email
+ @key = key
+ mail({
+ :reply_to => reply_to_address,
+ :subject => "Re: [#{ticket.id}] #{ticket.subject}",
+ :to => email,
+ :from => from_address(ticket, comment)
+ })
+ end
+
+ private
+
+ #
+ # I am not sure what makes the most sense here. For now, since we do not
+ # include any reply text in the notification email, it makes sense to make
+ # the from address be the robot, not the user.
+ #
+ def from_address(ticket, comment)
+ if true
+ reply_to_address
+ else
+ from_name = if comment.user.present?
+ comment.user.login
+ elsif ticket.created_by.present?
+ ticket.created_by_user.login
+ else
+ I18n.t(:anonymous)
+ end
+ "%s <%s>" % [from_name, reply_to_address]
+ end
+ end
+
+ # TODO: change me to support virtual domains
+ def reply_to_address
+ [APP_CONFIG[:mailer][:from_address], APP_CONFIG[:domain]].join('@')
+ end
+
+ #
+ # returns a hash of {'email' => 'public key'}, where key might be nil.
+ #
+ def self.reply_recipients(ticket, author)
+ recipients = {}
+ ticket.comments.each do |comment|
+ user = comment.posted_by_user
+ if user && (author.nil? || user.id != author.id)
+ if user.email
+ recipients[user.identity.address] = (user.identity.keys[:pgp] if user.identity.keys[:pgp].present?)
+ end
+ if user.contact_email.present?
+ recipients[user.contact_email] = (user.contact_email_key if user.contact_email_key.present?)
+ end
+ end
+ end
+ if author && author.is_admin? && ticket.email.present?
+ recipients[ticket.email] = nil
+ end
+ logger.info { "emailing reply regarding ticket #{ticket.id} to #{recipients.keys.join(', ')}" }
+ recipients
+ end
+
+end
diff --git a/engines/support/app/models/ticket.rb b/engines/support/app/models/ticket.rb
index 554fbd6..b1bdf8d 100644
--- a/engines/support/app/models/ticket.rb
+++ b/engines/support/app/models/ticket.rb
@@ -3,10 +3,10 @@
# 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'
#
+# TODO: this should use associations instead of non-standard created_by.
+#
class Ticket < CouchRest::Model::Base
use_database "tickets"
@@ -19,6 +19,8 @@ class Ticket < CouchRest::Model::Base
timestamps!
+ unique_id :generate_code
+
design do
view :by_updated_at
view :by_created_at
@@ -33,13 +35,11 @@ class Ticket < CouchRest::Model::Base
validates :subject, :presence => true
- # email can have three states:
- # * nil - prefilled with created_by's email
- # * "" - cleared
- # * valid email address
- validates :email, :allow_blank => true, :format => /\A(([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,}))?\Z/
-
- # validates :comments, presence: true
+ # email can be nil, "", or valid address.
+ # validation provided by 'valid_email' gem.
+ validates :email, :allow_blank => true,
+ :email => true,
+ :mx_with_fallback => true
def self.search(options = {})
@selection = TicketSelection.new(options)
@@ -74,17 +74,21 @@ class Ticket < CouchRest::Model::Base
self.is_open = true
end
- def commenters
+ def commenters(locale = I18n.locale)
commenters = []
+ unknown = false
+ anonymous = false
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')
+ elsif !unknown
+ unknown = true
+ commenters << I18n.t(:unknown, :locale => locale)
end
- else
- commenters << 'unauthenticated user' if !commenters.include?('unauthenticated user')
+ elsif !anonymous
+ anonymous = true
+ commenters << I18n.t(:anonymous, :locale => locale)
end
end
commenters.join(', ')
@@ -113,4 +117,15 @@ class Ticket < CouchRest::Model::Base
User.find_by_login(self.regarding_user)
end
+ # Generate a unique code for identifying this ticket.
+ # This will become the ID of the document. It is also used for
+ # tracking email replies. The code must be URL friendly.
+ def generate_code
+ while code = SecureRandom.urlsafe_base64.downcase.gsub(/[li1o0_-]/,'')[0..7]
+ if code.length == 8 && self.class.find(code).nil?
+ return code
+ end
+ end
+ end
+
end
diff --git a/engines/support/app/models/ticket_comment.rb b/engines/support/app/models/ticket_comment.rb
index 2c5df41..1c04a1f 100644
--- a/engines/support/app/models/ticket_comment.rb
+++ b/engines/support/app/models/ticket_comment.rb
@@ -1,22 +1,17 @@
+#
+# elijah: the property 'posted_by' should have been a belongs_to() association
+# or at least 'posted_by_id', but I am leaving it as is because I am
+# not sure how to change it.
+#
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 :posted_by, String
+ property :posted_at, Time
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
+ property :private, TrueClass
- # ? timestamps!
validates :body, :presence => true
- #before_validation :set_time#, :set_posted_by
-
- #design do
- # view :by_posted_at
- # view :by_body
- #end
# translations are in the same scope as those of a "proper" couchrest model
def self.i18n_scope
@@ -28,21 +23,10 @@ class TicketComment
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
+ if posted_by
+ @_posted_by_user ||= User.find(posted_by)
+ end
end
-=end
+ alias user posted_by_user
end
diff --git a/engines/support/app/views/ticket_mailer/notice.text.erb b/engines/support/app/views/ticket_mailer/notice.text.erb
new file mode 100644
index 0000000..c0e6f44
--- /dev/null
+++ b/engines/support/app/views/ticket_mailer/notice.text.erb
@@ -0,0 +1,5 @@
+<%= t_all_available(:email_notice_text) %>
+
+ <%= @url %>
+
+<%= t_all_available(:email_no_reply_text) %> \ No newline at end of file
diff --git a/engines/support/config/locales/en.yml b/engines/support/config/locales/en.yml
index 8d2af67..071db97 100644
--- a/engines/support/config/locales/en.yml
+++ b/engines/support/config/locales/en.yml
@@ -1,9 +1,11 @@
en:
support_tickets: "Support"
+ email_notice_text: "A new comment has been added to this help ticket."
+ email_no_reply_text: "Do not reply to this email."
# translations used in the layout views or @title
layouts:
# fallback for all translations of "tickets" nested below:
- tickets: "Tickets"
+ tickets: "Tickets"
title:
tickets: "Tickets"
header:
@@ -90,8 +92,8 @@ en:
"false": "Closed"
# mouse over hints for the given fields
hints:
- ticket:
- email: "Please provide an email address so we can get back to you."
+ ticket:
+ email: "Provide an email address in order to notified when this ticket is updated."
# these will fallback to translations in "simple_form.hints.defaults"
# placeholders inside the fields before anything was typed
#placeholders:
diff --git a/engines/support/config/locales/es.yml b/engines/support/config/locales/es.yml
new file mode 100644
index 0000000..7773197
--- /dev/null
+++ b/engines/support/config/locales/es.yml
@@ -0,0 +1,3 @@
+es:
+ email_notice_text: "Se ha aƱadido un nuevo comentario."
+ email_no_reply_text: "No responda a este mensaje." \ No newline at end of file
diff --git a/engines/support/leap_web_help.gemspec b/engines/support/leap_web_help.gemspec
index 7b668d5..818604f 100644
--- a/engines/support/leap_web_help.gemspec
+++ b/engines/support/leap_web_help.gemspec
@@ -8,9 +8,9 @@ Gem::Specification.new do |s|
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.homepage = "https://www.leap.se"
+ s.summary = "Help Desk for LEAP webapp"
+ s.description = "Managing help tickets for a LEAP provider"
s.files = Dir["{app,config,lib}/**/*"] + ["Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
diff --git a/engines/support/test/factories.rb b/engines/support/test/factories.rb
index bcf41e8..3fc28b3 100644
--- a/engines/support/test/factories.rb
+++ b/engines/support/test/factories.rb
@@ -2,7 +2,7 @@ FactoryGirl.define do
factory :ticket do
subject { Faker::Lorem.sentence }
- email { Faker::Internet.email }
+ email { "fake@example.org" }
factory :ticket_with_comment do
comments_attributes do
diff --git a/engines/support/test/functional/ticket_mailer_test.rb b/engines/support/test/functional/ticket_mailer_test.rb
new file mode 100644
index 0000000..6bbaaaf
--- /dev/null
+++ b/engines/support/test/functional/ticket_mailer_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class TicketMailerTest < ActionMailer::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/engines/support/test/unit/ticket_test.rb b/engines/support/test/unit/ticket_test.rb
index 678d8dc..c64e8f4 100644
--- a/engines/support/test/unit/ticket_test.rb
+++ b/engines/support/test/unit/ticket_test.rb
@@ -13,7 +13,7 @@ class TicketTest < ActiveSupport::TestCase
end
test "ticket validates email format" do
- t = FactoryGirl.build :ticket, email: "aswerssfd"
+ t = FactoryGirl.build :ticket, email: "invalid email"
assert !t.valid?
end
diff --git a/lib/en_US.yml b/lib/en_US.yml
index b00f29e..5c4190f 100644
--- a/lib/en_US.yml
+++ b/lib/en_US.yml
@@ -1,6 +1,6 @@
en_US:
-### Do NOT edit this file directly, as all changes will be overwritten by `rake i18:bundle`
+### Do NOT edit this file directly, as all changes will be overwritten by `rake i18n:bundle`
### Instead, make changes in the appropriate file in config/locales.
### Source strings in transifex are automatically updated from this file (via github url)
@@ -157,6 +157,12 @@ en_US:
for payment are available at INSERT_URL
bye: Goodbye!
bye_message: So long and thanks for all the fish.
+ contact_email: Contact Email
+ contact_email_info: ! 'You may specify an email address where we can contact you if
+ there is a problem with your account. For privacy reasons, you might prefer to leave
+ this empty.
+
+ '
users:
overview:
welcome: Welcome %{username}.
@@ -183,3 +189,101 @@ en_US:
placeholders:
user:
email_forward: my_other_email@domain.net
+
+
+########################################
+### billing/config/locales/en.yml
+ 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
+
+
+########################################
+### support/config/locales/en.yml
+ support_tickets: Support
+ email_notice_text: A new comment has been added to this help ticket.
+ email_no_reply_text: Do not reply to this email.
+ layouts:
+ tickets: Tickets
+ title:
+ tickets: Tickets
+ header:
+ tickets: Tickets
+ navigation:
+ tickets: Support Tickets
+ tickets:
+ 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?
+ 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.
+
+ '
+ helpers:
+ submit:
+ ticket:
+ create: Submit Ticket
+ update: Update Ticket
+ couchrest:
+ models:
+ ticket: Ticket
+ ticket_comment: Comment
+ attributes:
+ ticket:
+ subject: Subject
+ email: Email
+ regarding_user: Regarding User
+ regarding_account: Regarding Account
+ is_open: Status
+ ticket_comment:
+ body: Description
+ private: private
+ simple_form:
+ labels:
+ ticket:
+ regarding_:
+ email: Email
+ comments:
+ body: Description
+ options:
+ ticket:
+ is_open:
+ 'true': Open
+ 'false': Closed
+ hints:
+ ticket:
+ email: Provide an email address in order to notified when this ticket is updated.
diff --git a/lib/tasks/i18n.rake b/lib/tasks/i18n.rake
index daa1834..5ccc65e 100644
--- a/lib/tasks/i18n.rake
+++ b/lib/tasks/i18n.rake
@@ -1,6 +1,19 @@
require 'yaml'
require 'fileutils'
+def process_locale_file(output, file)
+ ## print separator to see where another file begins
+ output.write("\n\n" + '#' * 40 + "\n" + '### ' + file + "\n")
+ output.write(
+ # remove the toplevel "en" key
+ YAML.dump(YAML.load_file(file)['en']).lines.map {
+ |line| " #{line}" # << prefix all lines with two spaces (we're nested below "en:")
+ }[
+ 1..-1 # << skip the first line (it's "---" and freaks out the parser if it sees it multiple times in a file)
+ ].join
+ )
+end
+
namespace :i18n do
#
# for coding, it helps to have the english strings in separate files.
@@ -14,27 +27,20 @@ namespace :i18n do
desc "combine config/locales/*.en.yml to lib/en_US.yml"
task :bundle do
en_yml = File.join(Rails.root, 'lib', 'en_US.yml')
- Dir.chdir('config/locales/') do
- if File.exists?(en_yml)
- File.unlink en_yml
- end
- File.open(en_yml, 'w') do |output|
- output.write("en_US:\n\n"+
- "### Do NOT edit this file directly, as all changes will be overwritten by `rake i18:bundle`\n"+
- "### Instead, make changes in the appropriate file in config/locales.\n"+
- "### Source strings in transifex are automatically updated from this file (via github url)")
+ File.open(en_yml, 'w') do |output|
+ output.write("en_US:\n\n"+
+ "### Do NOT edit this file directly, as all changes will be overwritten by `rake i18n:bundle`\n"+
+ "### Instead, make changes in the appropriate file in config/locales.\n"+
+ "### Source strings in transifex are automatically updated from this file (via github url)")
+ Dir.chdir('config/locales/') do
Dir.glob('*.en.yml').sort.each do |file|
- ## print separator to see where another file begins
- output.write("\n\n" + '#' * 40 + "\n" + '### ' + file + "\n")
- output.write(
- # remove the toplevel "en" key
- YAML.load_file(file)['en'].
- to_yaml.
- # prefix all lines with two spaces (we're nested below "en:")
- lines.map {|line| " #{line}"}[
- 1..-1 # << skip the first line (it's "---" and freaks out the parser if it sees it multiple times in a file)
- ].join)
+ process_locale_file(output, file)
+ end
+ end
+ Dir.chdir('engines/') do
+ Dir.glob('*/config/locales/en.yml').sort.each do |file|
+ process_locale_file(output, file)
end
end
puts "You can now find the bundled locale yml in: #{en_yml}"