<%= link_to _("Click here to confirm your account"), confirmation_url(@resource, :confirmation_token => @token) %> (<%= _("or copy") %> <%= confirmation_url(@resource, :confirmation_token => @token) %> <%= _("into your browser") %>).
+<% end %>
\ No newline at end of file
diff --git a/app/views/devise/mailer/invitation_instructions.html.erb b/app/views/devise/mailer/invitation_instructions.html.erb
index 7beb75d..b3366dc 100644
--- a/app/views/devise/mailer/invitation_instructions.html.erb
+++ b/app/views/devise/mailer/invitation_instructions.html.erb
@@ -1,7 +1,12 @@
-
<%= _("A colleague has invited you to contribute to their Data Management Plan at ") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %>
<%= link_to _("Click here to accept the invitation"), accept_invitation_url(@resource, :invitation_token => @token) %> (<%= _("or copy") %> <%= accept_invitation_url(@resource, :invitation_token => @token) %> <%= _("into your browser") %>).
-<%= t('custom_devise.ignore_wont_be_created') %>
\ No newline at end of file
+
+ <%= _("If you don't want to accept the invitation, please ignore this email.") %>
+ <%= _("Your account won't be created until you access the link above and set your password.") %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
index b4c0ac6..21ba9ff 100644
--- a/app/views/devise/mailer/reset_password_instructions.html.erb
+++ b/app/views/devise/mailer/reset_password_instructions.html.erb
@@ -1,7 +1,10 @@
-
<%= _("Someone has requested a link to change your") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %> <%= _("password. You can do this through the link below.") %>
-
<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) %>
+
<%= link_to _('Change my password'), edit_password_url(@resource, :reset_password_token => @token) %>
<%= _("If you didn't request this, please ignore this email.") %>
+
<%= _("Your password won't change until you access the link above and create a new one.") %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
index 0346189..3787b7a 100644
--- a/app/views/devise/mailer/unlock_instructions.html.erb
+++ b/app/views/devise/mailer/unlock_instructions.html.erb
@@ -1,7 +1,9 @@
-
<%= _("Your") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %> <%= _("account has been locked due to an excessive number of unsuccessful sign in attempts.") %>
-
<%= t('custom_devise.click_to_unlock') %>
+
<%= _("Click the link below to unlock your account") %>:
<%= link_to _("Unlock my account"), unlock_url(@resource, :unlock_token => @token) %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index 71f1e7b..a13492c 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -46,20 +46,22 @@
as: :string,
class: "text_field has-tooltip reg-input", "data-toggle" => "tooltip", "data-container" => "body", "title" => _('Please enter the name of your organisation.') %>
-
-
<%= _('Language') %>
-
-
- <% locale = current_user.get_locale(); %>
-
-
-
+ <% if MANY_LANGUAGES %>
+
+
<%= _('Language') %>
+
+
+ <% locale = current_user.get_locale(); %>
+
+
+
+ <% end %>
<% @identifier_schemes.each do |scheme| %>
You can write pieces of guidance to be displayed by theme (e.g. generic guidance on storage and backup that should present across the board) or you can write guidance for specific questions. Writing generic guidance by theme saves you time and effort as your advice will be automatically displayed across all templates rather than having to write guidance to accompany each.
You will usually want your guidance to display on all templates, however there may be cases where you only want it to show for specific funders e.g. if you have specific instructions for applicants to BBSRC for example. This can be set too if needed.
')%>
+ <%= raw _('
You can write pieces of guidance to be displayed by theme (e.g. generic guidance on storage and backup that should present across the board). Writing generic guidance by theme saves you time and effort as your advice will be automatically displayed across all templates rather than having to write guidance to accompany each.
If you do have a need to provide guidance for specific funders that would not be useful to a wider audience (e.g. if you have specific instructions for applicants to BBSRC for example), you can do so by adding guidance to a specific question when you edit your template.
')%>
@@ -90,7 +90,6 @@
<%= _('Text') %>
<%= _('Themes') %>
-
<%= _('Question') %>
<%= _('Guidance group') %>
<%= _('Last updated') %>
<%= _('Actions') %>
@@ -114,15 +113,6 @@
-
<% end %>
- <% if !guidance.question_id.nil? then %>
-
- <%= raw guidance.question.text.truncate(70, omission: _('... (continued)')) %>
-
- <% else %>
-
- -
-
- <% end %>
<% if guidance.guidance_group.present? then %>
diff --git a/app/views/phases/_answer.html.erb b/app/views/phases/_answer.html.erb
index 3383519..5793739 100644
--- a/app/views/phases/_answer.html.erb
+++ b/app/views/phases/_answer.html.erb
@@ -14,7 +14,7 @@
right now you'd need to parameterise it with the form parameter which doesn't exist
if you are coming from /answer/update -->
-
While you were editing <%= answer.user.name %> saved the following answer:
+
<%= _("While you were editing #{answer.user.name} saved the following answer:") %>
-<% _('You have been granted permission by your organisation to use our API.') %>"<%= link_to _('Your API token and instructions for using the API endpoints can be found here.'), controller: "users", action: "edit" %>".
-
+<% FastGettext.with_locale FastGettext.default_locale do %>
+
<% _('Hello') %> <%= @user.name %>
+
+
+ <% _('You have been granted permission by your organisation to use our API.') %>" <%= link_to _('Your API token and instructions for using the API endpoints can be found here.'), controller: "users", action: "edit" %>".
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/user_mailer/permissions_change_notification.html.erb b/app/views/user_mailer/permissions_change_notification.html.erb
index f44b1fd..a0ef8fe 100644
--- a/app/views/user_mailer/permissions_change_notification.html.erb
+++ b/app/views/user_mailer/permissions_change_notification.html.erb
@@ -1,13 +1,15 @@
-
<%= _('Hello') %> <%= @role.user.name %>
+<% FastGettext.with_locale FastGettext.default_locale do %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/user_mailer/project_access_removed_notification.html.erb b/app/views/user_mailer/project_access_removed_notification.html.erb
index 778d50b..7ad6e31 100644
--- a/app/views/user_mailer/project_access_removed_notification.html.erb
+++ b/app/views/user_mailer/project_access_removed_notification.html.erb
@@ -1,3 +1,5 @@
-
<%= _('Hello') %><%= @user.name %>
+<% FastGettext.with_locale FastGettext.default_locale do %>
+
<%= _('Hello') %> <%= @user.name %>
-
<%= _('Your access to ') %>"<%= @plan.title %>"<%= _(' has been removed.') %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/user_mailer/sharing_notification.html.erb b/app/views/user_mailer/sharing_notification.html.erb
index 5f36e6c..befc872 100644
--- a/app/views/user_mailer/sharing_notification.html.erb
+++ b/app/views/user_mailer/sharing_notification.html.erb
@@ -1,16 +1,18 @@
-
<%= _('Hello ') %> <%= @role.user.name %>
+<% FastGettext.with_locale FastGettext.default_locale do %>
+
+<% end %>
\ No newline at end of file
diff --git a/config/locale/app.pot b/config/locale/app.pot
index 6f79684..6f281bc 100644
--- a/config/locale/app.pot
+++ b/config/locale/app.pot
@@ -2933,4 +2933,10 @@
msgstr ""
#years
msgid "%{d} years"
-msgstr ""
\ No newline at end of file
+msgstr ""
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr "can't be blank"
+msgid "activerecord.errors.models.user.attributes.password.blank"
+msgstr "can't be blank"
\ No newline at end of file
diff --git a/config/locale/de/app.po b/config/locale/de/app.po
index 7176a36..a37ae09 100644
--- a/config/locale/de/app.po
+++ b/config/locale/de/app.po
@@ -2924,4 +2924,10 @@
msgstr ""
#years
msgid "%{d} years"
+msgstr ""
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr ""
+msgid "activerecord.errors.models.user.attributes.password.blank"
msgstr ""
\ No newline at end of file
diff --git a/config/locale/en_GB/app.po b/config/locale/en_GB/app.po
index 74ca475..d4bf139 100644
--- a/config/locale/en_GB/app.po
+++ b/config/locale/en_GB/app.po
@@ -2924,4 +2924,10 @@
msgstr "about a year"
#years
msgid "%{d} years"
-msgstr "%{d} years"
\ No newline at end of file
+msgstr "%{d} years"
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr "can't be blank"
+msgid "activerecord.errors.models.user.attributes.password.blank"
+msgstr "can't be blank"
\ No newline at end of file
diff --git a/config/locale/en_US/app.po b/config/locale/en_US/app.po
index 181c931..a6660d2 100644
--- a/config/locale/en_US/app.po
+++ b/config/locale/en_US/app.po
@@ -2924,4 +2924,10 @@
msgstr "about a year"
#years
msgid "%{d} years"
-msgstr "%{d} years"
\ No newline at end of file
+msgstr "%{d} years"
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr "can't be blank"
+msgid "activerecord.errors.models.user.attributes.password.blank"
+msgstr "can't be blank"
\ No newline at end of file
diff --git a/config/locale/es/app.po b/config/locale/es/app.po
index b67e612..a8b2cbe 100644
--- a/config/locale/es/app.po
+++ b/config/locale/es/app.po
@@ -2828,4 +2828,10 @@
msgstr ""
#years
msgid "%{d} years"
+msgstr ""
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr ""
+msgid "activerecord.errors.models.user.attributes.password.blank"
msgstr ""
\ No newline at end of file
diff --git a/config/locale/fr/app.po b/config/locale/fr/app.po
index 189cc3c..01dd7a9 100644
--- a/config/locale/fr/app.po
+++ b/config/locale/fr/app.po
@@ -2932,4 +2932,10 @@
msgstr ""
#years
msgid "%{d} years"
+msgstr ""
+
+# ActiveRecord model errors that we could not override in the model's validation definition
+msgid "activerecord.errors.models.user.attributes.email.blank"
+msgstr ""
+msgid "activerecord.errors.models.user.attributes.password.blank"
msgstr ""
\ No newline at end of file
diff --git a/lib/assets/stylesheets/bootstrap_and_overrides.css.less b/lib/assets/stylesheets/bootstrap_and_overrides.css.less
index c8a9992..2796d1b 100644
--- a/lib/assets/stylesheets/bootstrap_and_overrides.css.less
+++ b/lib/assets/stylesheets/bootstrap_and_overrides.css.less
@@ -995,6 +995,10 @@
text-shadow: none;
}
+.answer-notice {
+ width: 90%;
+}
+
ol .choices-group{
padding-left: 20px;
}
diff --git a/test/configuration_test.rb b/test/configuration_test.rb
index 7840311..05c9622 100644
--- a/test/configuration_test.rb
+++ b/test/configuration_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class ConfigurationHelper < ActionDispatch::IntegrationTest
+class ConfigurationTest < ActionDispatch::IntegrationTest
# --------------------------------------------------------------------
test "Make sure that all of the example YAML files have been setup properly" do
diff --git a/test/functional/answers_controller_test.rb b/test/functional/answers_controller_test.rb
index 36cceea..1d5a3eb 100644
--- a/test/functional/answers_controller_test.rb
+++ b/test/functional/answers_controller_test.rb
@@ -22,6 +22,9 @@
plan = Plan.create(title: "Testing Answer For #{format.title}",
template: template)
+
+ Role.create!(user_id: @user.id, plan_id: plan.id, access: 4)
+ plan.reload
referrer = "/#{FastGettext.locale}/plans/#{plan.id}/phases/#{question.section.phase.id}/edit"
@@ -43,7 +46,8 @@
# Try editing it
form_attributes = {"answer-text-#{question.id}": "Tested",
- answer: {user_id: answer.user.id,
+ answer: {id: answer.id,
+ user_id: answer.user.id,
plan_id: answer.plan.id,
question_id: answer.question.id,
lock_version: answer.lock_version}}
diff --git a/test/functional/templates_controller_test.rb b/test/functional/templates_controller_test.rb
index 6e390ee..2266a6f 100644
--- a/test/functional/templates_controller_test.rb
+++ b/test/functional/templates_controller_test.rb
@@ -124,7 +124,7 @@
# POST /org/admin/templates/:id/admin_create (admin_create_template_path)
# ----------------------------------------------------------
test "create a admin template" do
- params = {org_id: @user.org.id, version: 0, title: 'Testing create route'}
+ params = {title: 'Testing create route'}
# Should redirect user to the root path if they are not logged in!
post admin_create_template_path(Template.last.id), {template: params}
diff --git a/test/integration/answer_locking_test.rb b/test/integration/answer_locking_test.rb
new file mode 100644
index 0000000..e083dda
--- /dev/null
+++ b/test/integration/answer_locking_test.rb
@@ -0,0 +1,106 @@
+require 'test_helper'
+
+class AnswerLockingTest < ActionDispatch::IntegrationTest
+ include Devise::Test::IntegrationHelpers
+
+ setup do
+ scaffold_template
+ scaffold_plan
+ @question = Question.create(text: 'Test question', section: @plan.template.phases.first.sections.first,
+ question_format: QuestionFormat.where(option_based: false).first, number: 99)
+
+ @collaborator = (User.first == @plan.owner ? User.last : User.first)
+
+ # Make the 2nd user an editor of the plan
+ Role.create!(user_id: @collaborator.id, plan_id: @plan.id, access: 4)
+ @plan.reload
+ end
+
+ # ----------------------------------------------------------
+ test 'user receives a lock notification if the answer was CREATED while they were working' do
+ userA = Answer.new(user: @plan.owner, plan: @plan, question: @question,
+ text: "Initial answer - by UserA")
+
+ userB = Answer.new(user: @collaborator, plan: @plan, question: @question,
+ text: "Version conflict at onset - by UserB")
+
+ # Signin as UserA and insert the new answer
+ sign_in @plan.owner
+ put answer_path(FastGettext.locale, userA, format: "js"), obj_to_params(userA.attributes)
+ assert_response :success
+ assert_equal "text/javascript", @response.content_type
+ updated = Answer.find_by(plan: @plan, question: @question)
+ assert_equal "Initial answer - by UserA", updated.text
+ assert_equal @plan.owner.id, updated.user_id
+
+ # Make sure the answer-notice is NOT displayed
+ assert_not @response.body.include?(_('Combine their changes with your answer below and then save the answer again.')), "expected there to be no lock error messaging"
+ assert @response.body.include?("#{_('by')} #{@plan.owner.name}"), "expected the messaging to say the plan was updated by the plan owner"
+ assert @response.body.include?(_('answered')), "expected the messaging to include the status"
+
+ # Signin as UserB and try to insert the new answer but fail
+ sign_in @collaborator
+ put answer_path(FastGettext.locale, userB, format: "js"), obj_to_params(userB.attributes)
+ assert_response :success
+ assert_equal "text/javascript", @response.content_type
+ updated = Answer.find_by(plan: @plan, question: @question)
+ assert_equal "Initial answer - by UserA", updated.text
+ assert_equal @plan.owner.id, updated.user_id
+
+ # Make sure the answer-notice IS displayed
+ assert @response.body.include?(_('Combine their changes with your answer below and then save the answer again.')), "expected there to be lock error messaging"
+ assert @response.body.include?("#{_('by')} #{@plan.owner.name}"), "expected the messaging to STILL say the plan was updated by the plan owner"
+ assert @response.body.include?(_('answered')), "expected the messaging to include the status"
+ end
+
+ # ----------------------------------------------------------
+ test 'user receives a lock notification if the answer was UPDATED while they were working' do
+ userA = Answer.create!(user: @plan.owner, plan: @plan, question: @question,
+ text: "Initial answer - by UserA").attributes
+ userB = userA.clone
+
+ # Signin as UserA and insert the new answer
+ sign_in @plan.owner
+ userA['text'] += " - Updated by userA"
+
+ put answer_path(FastGettext.locale, userA['id'], format: "js"), obj_to_params(userA)
+ assert_response :success
+ assert_equal "text/javascript", @response.content_type
+ updated = Answer.find_by(plan: @plan, question: @question)
+ assert_equal "Initial answer - by UserA - Updated by userA", updated.text
+ assert_equal @plan.owner.id, updated.user_id
+
+ # Make sure the answer-notice is NOT displayed
+ assert_not @response.body.include?(_('Combine their changes with your answer below and then save the answer again.')), "expected there to be no lock error messaging"
+ assert @response.body.include?("#{_('by')} #{@plan.owner.name}"), "expected the messaging to say the plan was updated by the plan owner"
+ assert @response.body.include?(_('answered')), "expected the messaging to include the status"
+
+ # Signin as UserB and try to insert the new answer but fail
+ sign_in @collaborator
+ userB['text'] += " - Updated by userB"
+
+ put answer_path(FastGettext.locale, userB['id'], format: "js"), obj_to_params(userB)
+ assert_response :success
+ assert_equal "text/javascript", @response.content_type
+ updated = Answer.find_by(plan: @plan, question: @question)
+ assert_equal "Initial answer - by UserA - Updated by userA", updated.text
+ assert_equal @plan.owner.id, updated.user_id
+
+ # Make sure the answer-notice IS displayed
+ assert @response.body.include?(_('Combine their changes with your answer below and then save the answer again.')), "expected there to be lock error messaging"
+ assert @response.body.include?("#{_('by')} #{@plan.owner.name}"), "expected the messaging to STILL say the plan was updated by the plan owner"
+ assert @response.body.include?(_('answered')), "expected the messaging to include the status"
+ end
+
+# ----------------------------------------------------------
+ private
+ def obj_to_params(attributes)
+ {"answer-text-#{attributes['question_id']}": "#{attributes['text']}",
+ answer: {id: attributes['id'],
+ user_id: attributes['user_id'],
+ plan_id: attributes['plan_id'],
+ question_id: attributes['question_id'],
+ lock_version: attributes['lock_version']}
+ }
+ end
+end
diff --git a/test/missing_translation_test.rb b/test/missing_translation_test.rb
new file mode 100644
index 0000000..53c81d9
--- /dev/null
+++ b/test/missing_translation_test.rb
@@ -0,0 +1,66 @@
+require 'test_helper'
+
+class MissingTranslationTest < ActionDispatch::IntegrationTest
+
+ # --------------------------------------------------------------------
+ test "Make sure that all FastGettext localisations are defined in the .pot/.po files" do
+ missing = []
+
+ # Scan through the /app directory for localisations
+ getFiles(Rails.root.join("app")).each do |file|
+ contents = File.open(file, 'r').read
+ localisations = contents.scan(/_\(['"][^\)]+['"]\)/)
+
+ localisations.each do |localisation|
+ translation = _(localisation)
+ missing << localisation if translation.include?('translation missing')
+ end
+ end
+
+ assert missing.empty?, "Found some missing translations: #{missing.join("\n")}"
+ missing = []
+
+ # Loop through all of the models and force all validation errors to be translated
+ dir = Rails.root.join("app", "models").to_s
+ getFiles(dir).each do |model|
+ unless model.start_with?('.')
+ name = model.gsub('.rb', '').gsub("#{dir}/", '').split('_').collect{|p| p.capitalize }.join('')
+ name = name.split('/').collect{|p| p }.join('::').gsub(/::[a-z]{1}/, &:upcase)
+
+ # Skip the Settings module classes since they throw errors when validating
+ unless name.include?('Settings::')
+ clazz = name.split('::').inject(Object){ |o,c| o.const_get(c) }
+ obj = clazz.new
+
+ unless obj.valid?
+ obj.errors.each do |e,m|
+ missing << m if _(m).include?('translation missing')
+ end
+ end
+ end
+ end
+ end
+
+ assert missing.empty?, "Found some missing translations for model errors:\n #{missing.join("\n")}"
+
+ end
+
+
+ private
+ # Recursively collect the file names within the directory and its subdirectories
+ # --------------------------------------------------------------------
+ def getFiles(dir)
+ files = []
+ Dir.foreach(dir) do |f|
+ unless f.start_with?('.')
+ if File.directory?("#{dir}/#{f}")
+ files << getFiles("#{dir}/#{f}")
+ else
+ files << "#{dir}/#{f}"
+ end
+ end
+ end
+ files.flatten.uniq
+ end
+
+end