diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 498603b..a6fe02a 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -14,18 +14,30 @@ lock_version = ans_params[:lock_version] question_id = ans_params[:question_id] @question = Question.find(question_id); - @answer = Answer.find_by( - plan_id: plan_id, - user_id: user_id, - question_id: question_id) + # If an answer id is present load that answer otherwise load by plan/question + @answer = Answer.find_by(plan_id: plan_id, question_id: question_id) - @old_answer = nil + @old_answer, race_on_creation = nil, false + # This is the first answer for the question if @answer.nil? @answer = Answer.new(params[:answer]) @answer.text = params["answer-text-#{@answer.question_id}".to_sym] authorize @answer - @answer.save + + @answer.save + + @lock_version = @answer.lock_version + + # Someone else already added an answer while the user was working + elsif ans_params[:id].nil? || ans_params[:id].empty? + @old_answer = Marshal::load(Marshal.dump(@answer)) + @answer.text = params["answer-text-#{@answer.question_id}".to_sym] + authorize @answer + + @lock_version = @answer.lock_version + + # We're updating an answer (let ActiveRecord check for a race condition) else # if you do the obvious clone here it will overwrite the old_answer text # in the next line @@ -33,7 +45,13 @@ @old_answer = Marshal::load(Marshal.dump(@answer)) @answer.text = params["answer-text-#{@answer.question_id}".to_sym] authorize @answer + @answer.update(params[:answer]) + + # The save was successful so get the lock version and nil the + # old answer + @lock_version = @answer.lock_version + @old_answer = nil end @section_id = @answer.question.section.id @@ -72,13 +90,11 @@ end respond_to do |format| - # pass new lock_version back to the client or they'll never save again - @lock_version = @answer.lock_version - @old_answer = nil format.js {} end rescue ActiveRecord::StaleObjectError + @username = @old_answer.user.name @lock_version = @old_answer.lock_version respond_to do |format| format.js {} diff --git a/app/controllers/guidances_controller.rb b/app/controllers/guidances_controller.rb index 47c5004..2356ef0 100644 --- a/app/controllers/guidances_controller.rb +++ b/app/controllers/guidances_controller.rb @@ -39,7 +39,6 @@ @guidance = Guidance.new(guidance_params) authorize @guidance @guidance.text = params["guidance-text"] - @guidance.question_id = params["question_id"] @guidance.themes = [] if !guidance_params[:theme_ids].nil? @@ -70,7 +69,6 @@ @guidance = Guidance.find(params[:id]) authorize @guidance @guidance.text = params["guidance-text"] - @guidance.question_id = params["question_id"] if @guidance.save(guidance_params) redirect_to admin_show_guidance_path(params[:guidance]), notice: _('Guidance was successfully updated.') diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 80e72c0..14549b7 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -36,7 +36,7 @@ set_access_level(access_level) if @role.update_attributes(role_params) flash[:notice] = _('Sharing details successfully updated.') - UserMailer.permissions_change_notification(@role).deliver + UserMailer.permissions_change_notification(@role).deliver_now redirect_to controller: 'plans', action: 'share', id: @role.plan.id else flash[:notice] = generate_error_notice(@role, _('role')) @@ -51,7 +51,7 @@ plan = @role.plan @role.destroy flash[:notice] = _('Access removed') - UserMailer.project_access_removed_notification(user, plan).deliver + UserMailer.project_access_removed_notification(user, plan).deliver_now redirect_to controller: 'plans', action: 'share', id: @role.plan.id end diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index c150673..9108ba9 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -190,13 +190,12 @@ def admin_create @template = Template.new(params[:template]) authorize @template - - @template.org_id = current_user.org_id + @template.org_id = current_user.org.id @template.description = params['template-desc'] @template.published = false @template.version = 0 @template.visibility = 0 - + # Generate a unique identifier for the dmptemplate_id @template.dmptemplate_id = loop do random = rand 2147483647 diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index fc37cca..681679b 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,25 +1,36 @@ class UserMailer < ActionMailer::Base - default from: I18n.t('helpers.main_email.from') + default from: Rails.configuration.branding[:organisation][:email] def sharing_notification(role, user) @role = role - @user = user - mail(to: @role.user.email, subject: I18n.t('helpers.main_email.access_given')) + FastGettext.with_locale FastGettext.default_locale do + mail(to: @role.user.email, + subject: _("You have been given access to a Data Management Plan")) + end end def permissions_change_notification(role) @role = role - mail(to: @role.user.email, subject: I18n.t('helpers.main_email.permission_changed')) + FastGettext.with_locale FastGettext.default_locale do + mail(to: @role.user.email, + subject: _("DMP permissions changed")) + end end def project_access_removed_notification(user, plan) @user = user @plan = plan - mail(to: @user.email, subject: I18n.t('helpers.main_email.access_removed')) + FastGettext.with_locale FastGettext.default_locale do + mail(to: @user.email, + subject: _("DMP access removed")) + end end def api_token_granted_notification(user) @user = user - mail(to: @user.email, subject: I18n.t('helper.api_mail_subject')) + FastGettext.with_locale FastGettext.default_locale do + mail(to: @user.email, + subject: _('API Permission Granted')) + end end end \ No newline at end of file diff --git a/app/models/answer.rb b/app/models/answer.rb index b84aeba..54ac937 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -14,7 +14,7 @@ # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 attr_accessible :text, :plan_id, :lock_version, :question_id, :user_id, :question_option_ids, - :question, :user, :plan, :question_options, :notes, :note_ids, + :question, :user, :plan, :question_options, :notes, :note_ids, :id, :as => [:default, :admin] ## diff --git a/app/models/exported_plan.rb b/app/models/exported_plan.rb index 15cbcdc..9fdbf97 100644 --- a/app/models/exported_plan.rb +++ b/app/models/exported_plan.rb @@ -17,7 +17,7 @@ _('%{value} is not a valid format') % { :value => data[:value] } end } - validates :plan, :format, presence: true + validates :plan, :format, presence: {message: _("can't be blank")} # Store settings with the exported plan so it can be recreated later # if necessary (otherwise the settings associated with the plan at a diff --git a/app/models/guidance.rb b/app/models/guidance.rb index f22cf3b..dfec5dc 100644 --- a/app/models/guidance.rb +++ b/app/models/guidance.rb @@ -25,7 +25,7 @@ - validates :text, presence: true + validates :text, presence: {message: _("can't be blank")} ## diff --git a/app/models/guidance_group.rb b/app/models/guidance_group.rb index 9686c27..96e6dab 100644 --- a/app/models/guidance_group.rb +++ b/app/models/guidance_group.rb @@ -13,7 +13,7 @@ attr_accessible :org_id, :name, :optional_subset, :published, :org, :guidances, :as => [:default, :admin] - validates :name, :org, presence: true + validates :name, :org, presence: {message: _("can't be blank")} # EVALUATE CLASS AND INSTANCE METHODS BELOW diff --git a/app/models/identifier_scheme.rb b/app/models/identifier_scheme.rb index 45b8922..d07aafc 100644 --- a/app/models/identifier_scheme.rb +++ b/app/models/identifier_scheme.rb @@ -2,5 +2,5 @@ has_many :user_identifiers has_many :users, through: :user_identifiers - validates :name, uniqueness: true, presence: true + validates :name, uniqueness: {message: _("must be unique")}, presence: {message: _("can't be blank")} end \ No newline at end of file diff --git a/app/models/language.rb b/app/models/language.rb index 3e5e3bd..2d02783 100644 --- a/app/models/language.rb +++ b/app/models/language.rb @@ -6,7 +6,8 @@ ## # Validations - validates :abbreviation, presence: true, uniqueness: true + # Cannot do FastGettext translations here because we constantize LANGUAGES in initializers/constants.rb + validates :abbreviation, presence: {message: "can't be blank"}, uniqueness: {message: "must be unique"} scope :sorted_by_abbreviation, -> { all.order(:abbreviation) } scope :default, -> { where(default_language: true).first } diff --git a/app/models/note.rb b/app/models/note.rb index 74b3078..4341c2a 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -10,5 +10,5 @@ attr_accessible :text, :user_id, :answer_id, :archived, :archived_by, :answer, :user, :as => [:default, :admin] - validates :text, :answer, :user, presence: true + validates :text, :answer, :user, presence: {message: _("can't be blank")} end diff --git a/app/models/org.rb b/app/models/org.rb index 61e6691..3c23dcc 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -26,14 +26,14 @@ ## # Validators validates :contact_email, email: true, allow_nil: true - validates :name, presence: true, uniqueness: true + validates :name, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} # allow validations for logo upload dragonfly_accessor :logo do after_assign :resize_image end - validates_property :height, of: :logo, in: (0..100) - validates_property :format, of: :logo, in: ['jpeg', 'png', 'gif','jpg','bmp'] - validates_size_of :logo, maximum: 500.kilobytes + validates_property :height, of: :logo, in: (0..100), message: _("height must be less than 100px") + validates_property :format, of: :logo, in: ['jpeg', 'png', 'gif','jpg','bmp'], message: _("must be one of the following formats: jpeg, jpg, png, gif, bmp") + validates_size_of :logo, maximum: 500.kilobytes, message: _("can't be larger than 500KB") ## # Define Bit Field values diff --git a/app/models/perm.rb b/app/models/perm.rb index 2bc5aad..58aad3e 100644 --- a/app/models/perm.rb +++ b/app/models/perm.rb @@ -8,7 +8,7 @@ # -relies on protected_attributes gem as syntax depricated in rails 4.2 #attr_accessible :name, :as => [:default, :admin] - validates :name, presence: true, uniqueness: true + validates :name, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} ## # Constant perms diff --git a/app/models/phase.rb b/app/models/phase.rb index 88429fd..d3e813a 100644 --- a/app/models/phase.rb +++ b/app/models/phase.rb @@ -23,7 +23,7 @@ #friendly_id :title, use: [:slugged, :history, :finders] - validates :title, :number, :template, presence: true + validates :title, :number, :template, presence: {message: _("can't be blank")} diff --git a/app/models/question.rb b/app/models/question.rb index 4c0efdc..eac588d 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -27,7 +27,7 @@ :question_options, :suggested_answers, :answers, :themes, :modifiable, :option_comment_display, :as => [:default, :admin] - validates :text, :section, :number, presence: true + validates :text, :section, :number, presence: {message: _("can't be blank")} # EVALUATE CLASS AND INSTANCE METHODS BELOW diff --git a/app/models/question_format.rb b/app/models/question_format.rb index 3a08be8..061961a 100644 --- a/app/models/question_format.rb +++ b/app/models/question_format.rb @@ -7,7 +7,7 @@ enum formattype: [ :textarea, :textfield, :radiobuttons, :checkbox, :dropdown, :multiselectbox, :date ] attr_accessible :formattype - validates :title, presence: true, uniqueness: true + validates :title, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} ## # Possibly needed for active_admin diff --git a/app/models/question_option.rb b/app/models/question_option.rb index 11552b0..84266ab 100644 --- a/app/models/question_option.rb +++ b/app/models/question_option.rb @@ -10,7 +10,7 @@ attr_accessible :text, :question_id, :is_default, :number, :question, :as => [:default, :admin] - validates :text, :question, :number, presence: true + validates :text, :question, :number, presence: {message: _("can't be blank")} ## # deep copy the given question_option and all it's associations diff --git a/app/models/region.rb b/app/models/region.rb index 2c57e79..a7e366a 100644 --- a/app/models/region.rb +++ b/app/models/region.rb @@ -3,6 +3,6 @@ belongs_to :super_region, class_name: 'Region' - validates :name, presence: true, uniqueness: true - validates :abbreviation, uniqueness: true, allow_nil: true + validates :name, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} + validates :abbreviation, uniqueness: {message: _("must be unique")}, allow_nil: true end \ No newline at end of file diff --git a/app/models/role.rb b/app/models/role.rb index 75ad26c..a51177d 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -16,8 +16,8 @@ 4 => :commenter, # 8 column: 'access' - validates :user, :plan, :access, presence: true - validates :access, numericality: {greater_than: 0} + validates :user, :plan, :access, presence: {message: _("can't be blank")} + validates :access, numericality: {greater_than: 0, message: _("can't be less than zero")} ## # return the access level for the current project group diff --git a/app/models/section.rb b/app/models/section.rb index b88ee79..2a38972 100644 --- a/app/models/section.rb +++ b/app/models/section.rb @@ -14,7 +14,7 @@ :questions_attributes, :organisation, :phase, :modifiable, :as => [:default, :admin] - validates :phase, :title, :number, presence: true + validates :phase, :title, :number, presence: {message: _("can't be blank")} ## # return the title of the section diff --git a/app/models/suggested_answer.rb b/app/models/suggested_answer.rb index 91ce3aa..881f302 100644 --- a/app/models/suggested_answer.rb +++ b/app/models/suggested_answer.rb @@ -12,7 +12,7 @@ :org, :question, :as => [:default, :admin] - validates :question, :org, presence: true + validates :question, :org, presence: {message: _("can't be blank")} # EVALUATE CLASS AND INSTANCE METHODS BELOW # diff --git a/app/models/template.rb b/app/models/template.rb index 16b7ac4..de44b10 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -24,7 +24,7 @@ s.key :export, defaults: Settings::Template::DEFAULT_SETTINGS end - validates :org, :title, :version, presence: true + validates :org, :title, :version, presence: {message: _("can't be blank")} # EVALUATE CLASS AND INSTANCE METHODS BELOW # diff --git a/app/models/theme.rb b/app/models/theme.rb index 315dfe8..bd787f6 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -13,7 +13,7 @@ attr_accessible :description, :title, :locale , :as => [:default, :admin] - validates :title, presence: true + validates :title, presence: {message: _("can't be blank")} # EVALUATE CLASS AND INSTANCE METHODS BELOW # diff --git a/app/models/token_permission_type.rb b/app/models/token_permission_type.rb index 71d4acb..5770e6c 100644 --- a/app/models/token_permission_type.rb +++ b/app/models/token_permission_type.rb @@ -12,7 +12,7 @@ ## # Validators - validates :token_type, presence: true, uniqueness: true + validates :token_type, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} ## # Constant Token Permission Types diff --git a/app/models/user.rb b/app/models/user.rb index dc53469..4cfa675 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -49,7 +49,7 @@ # :organisation, :language, :language_id, :org, :perms, # :confirmed_at, :org_id - validates :email, email: true, allow_nil: true, uniqueness: true + validates :email, email: true, allow_nil: true, uniqueness: {message: _("must be unique")} ## # Settings diff --git a/app/models/user_identifier.rb b/app/models/user_identifier.rb index 6623765..840e394 100644 --- a/app/models/user_identifier.rb +++ b/app/models/user_identifier.rb @@ -5,5 +5,5 @@ # Should only be able to have one identifier per scheme! validates_uniqueness_of :identifier_scheme, scope: :user - validates :identifier, :user, :identifier_scheme, presence: true + validates :identifier, :user, :identifier_scheme, presence: {message: _("can't be blank")} end \ No newline at end of file diff --git a/app/policies/answer_policy.rb b/app/policies/answer_policy.rb index 805efac..1e9502c 100644 --- a/app/policies/answer_policy.rb +++ b/app/policies/answer_policy.rb @@ -8,9 +8,10 @@ @answer = answer end - def create? - # is the plan editable by the user, and is the user_id that of the user - @answer.plan.editable_by(@user.id) && (@answer.user_id == @user.id) + def update? + # TODO: Remove the owner check after the Roles have been updated + # is the plan editable by the user or the user is the owner of the plan + @answer.plan.editable_by?(@user.id) || @user == @answer.plan.owner end end \ No newline at end of file diff --git a/app/policies/template_policy.rb b/app/policies/template_policy.rb index 41e1f7c..3c9fb80 100644 --- a/app/policies/template_policy.rb +++ b/app/policies/template_policy.rb @@ -30,7 +30,7 @@ end def admin_create? - user.can_modify_templates? && (template.org_id == user.org_id) + user.can_modify_templates? && (template.org_id.nil? || (template.org_id == user.org_id)) end def admin_destroy? diff --git a/app/views/answers/update.js.erb b/app/views/answers/update.js.erb index 3ddb49f..40907ae 100644 --- a/app/views/answers/update.js.erb +++ b/app/views/answers/update.js.erb @@ -15,9 +15,10 @@ // have to update the lock_version on success or failure // so that the next save can work. If you don't do -// this it will fail forever. +// this it will fail forever. Also update the answer id $("#answer_lock_version-<%=@question.id%>").val(<%= @lock_version %>); +$("#question-form-<%= @question.id %> #answer_id").val(<%= @answer.id %>); // update the answer status @@ -26,16 +27,14 @@ if( timestamp != "") { q_status.text(""); - // TODO: i18n this - q_status.append( "Answered \" title=\"<%=@timestamp%>\"><%=@timestamp%> by <%=@username%>"); + q_status.append("<%= _('Answered') %> \" title=\"<%=@timestamp%>\"><%=@timestamp%> <%= _('by') %> <%=@username%>"); $('abbr.timeago').timeago(); } // update plan progress bar -$(".progress").html("<%= escape_javascript(render :partial => "/plans/progress", locals: {nquestions: @nquestions, nanswers: @nanswers}) %>"); +$(".progress").html("<%= escape_javascript(render :partial => '/plans/progress', locals: {nquestions: @nquestions, nanswers: @nanswers}) %>"); // update the section progress message -//TODO: I18n this -$("#<%=@section_id%>-status").html("(<%=@n_section_questions%> questions, <%=@n_section_answers%> answered)"); +$("#<%=@section_id%>-status").html("(<%=@n_section_questions%> <%= _('questions') %>, <%=@n_section_answers%> <%= _('answered') %>)") diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb index c1faca5..0576cc7 100644 --- a/app/views/devise/mailer/confirmation_instructions.html.erb +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -1,5 +1,7 @@ -

<%= t('custom_devise.welcome_to_DMP') %>, <%= @email %>!

+<% FastGettext.with_locale FastGettext.default_locale do %> +

<%= _("Welcome to %{application_name}") % {application_name: Rails.configuration.branding[:application][:name]} %>, <%= @email %>!

-

<%= t('custom_devise.thank_you_and_confirm') %>

+

<%= _("Thank you for registering. Please confirm your email address") %>:

-

<%= link_to t('custom_devise.click_to_confirm'), confirmation_url(@resource, :confirmation_token => @token) %> <%= t('custom_devise.1st_part_copy') %> <%= confirmation_url(@resource, :confirmation_token => @token) %> <%= t('custom_devise.2nd_part_copy') %>

\ No newline at end of file +

<%= 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 @@ -

<%= t('custom_devise.hello') %> <%= @resource.email %>!

+<% FastGettext.with_locale FastGettext.default_locale do %> +

<%= _("Hello") %> <%= @resource.email %>!

-

<%= t('custom_devise.colleague_invited_you') %> <%= link_to t('tool_title'), root_url %>

+

<%= _("A colleague has invited you to contribute to their Data Management Plan at ") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %>

-

<%= link_to t('custom_devise.click_to_accept'), accept_invitation_url(@resource, :invitation_token => @token) %> <%= t('custom_devise.1st_part_copy') %> <%= accept_invitation_url(@resource, :invitation_token => @token) %> <%= t('custom_devise.2nd_part_copy') %>

+

<%= 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 @@ -

<%= t('custom_devise.hello') %> <%= @resource.email %>!

+<% FastGettext.with_locale FastGettext.default_locale do %> +

<%= _("Hello") %> <%= @resource.email %>!

-

<%= t('custom_devise.1st_part_change_password') %> <%= link_to t('tool_title'), root_url %> <%= t('custom_devise.2nd_part_change_password') %>

+

<%= _("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) %>

-<%= t('custom_devise.1st_part_change_password') %> +

<%= _("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 @@ -

<%= t('custom_devise.hello') %> <%= @resource.email %>!

+<% FastGettext.with_locale FastGettext.default_locale do %> +

<%= _("Hello") %> <%= @resource.email %>!

-

<%= t('custom_devise.1st_part_locked') %><%= link_to t('tool_title'), root_url %><%= t('custom_devise.2nd_part_locked') %>

+

<%= _("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 t('custom_devise.unlock'), unlock_url(@resource, :unlock_token => @token) %>

\ No newline at end of file +

<%= 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| %> diff --git a/app/views/guidances/admin_index.html.erb b/app/views/guidances/admin_index.html.erb index 406b45f..6010b35 100644 --- a/app/views/guidances/admin_index.html.erb +++ b/app/views/guidances/admin_index.html.erb @@ -70,7 +70,7 @@
- <%= 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) 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 %> <%= guidance.guidance_group.name %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index a811480..483511b 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -66,10 +66,10 @@
<% if notice %> -

<%= notice %>

+

<%= raw notice %>

<% end %> <% if alert %> -

<%= alert %>

+

<%= raw alert %>

<% end %>
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:") %>

<%= semantic_form_for answer, :url => {:controller => :answers, :action => :update }, :remote => true do |af| %> <%= af.inputs do %> @@ -59,19 +59,19 @@ <% if question.option_comment_display %> <%= label_tag("answer-text-Ans#{question.id}".to_sym, _('Comment')) %> - <%= text_area_tag("answer-text-Ans#{question.id}".to_sym, answer.text, class: "tinymce") %> + <%= text_area_tag("answer-text-Ans#{question.id}".to_sym, answer.text, class: "tinymce answer-notice") %> <%end%> <% end %> <% if qformat.textfield? %> <%= text_field_tag("answer-text-Ans#{question.id}".to_sym, strip_tags(answer.text), class: "question_text_field") %> <% elsif qformat.textarea? %> - <%= text_area_tag("answer-text-Ans#{question["id"]}".to_sym, strip_tags(answer.text), class: "tinymce") %> + <%= text_area_tag("answer-text-Ans#{question["id"]}".to_sym, strip_tags(answer.text), class: "tinymce answer-notice") %> <% end %> <% end %> <% end %> -

Combine with your answer and save.

+

<%= _('Combine their changes with your answer below and then save the answer again.') %>

diff --git a/app/views/phases/_answer_form.html.erb b/app/views/phases/_answer_form.html.erb index 5b412bf..e14ec26 100644 --- a/app/views/phases/_answer_form.html.erb +++ b/app/views/phases/_answer_form.html.erb @@ -21,6 +21,7 @@
<%= semantic_form_for answer, :url => {:controller => :answers, :action => :update }, method: "put", :remote => true do |f| %> <%= f.inputs do %> + <%= f.input :id, :as => :hidden, :input_html => { :value => answer.id } %> <%= f.input :plan_id, :as => :hidden, :input_html => { :value => @plan.id } %> <%= f.input :user_id, :as => :hidden, :input_html => { :value => current_user.id } %> <%= f.input :question_id, :as => :hidden, :input_html => { :value => question_id, :class => "question_id" } %> diff --git a/app/views/plans/_plan_details.html.erb b/app/views/plans/_plan_details.html.erb index 656e6e4..b18eb89 100644 --- a/app/views/plans/_plan_details.html.erb +++ b/app/views/plans/_plan_details.html.erb @@ -206,8 +206,9 @@ <%= plan.template.org.name %> <%end%> + <%if based_on.present? %> - <%= " (Customised from " + based_on.org.name + ")" %> + <%= " (#{_('Customised from')} " + based_on.org.name + ")" %> <%end%> diff --git a/app/views/plans/share.html.erb b/app/views/plans/share.html.erb index 2e27110..bf8837b 100644 --- a/app/views/plans/share.html.erb +++ b/app/views/plans/share.html.erb @@ -4,7 +4,8 @@ <%= render :partial => "plan_title", locals: {plan: @plan} %> -<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, active: "share_project"} %> +<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, plan_data: @plan_data, active: "share"} %> +
diff --git a/app/views/templates/admin_template.html.erb b/app/views/templates/admin_template.html.erb index 19ed6e1..d7734f6 100644 --- a/app/views/templates/admin_template.html.erb +++ b/app/views/templates/admin_template.html.erb @@ -42,12 +42,12 @@
-
+
<%= render partial: 'templates/show_phases_sections', locals: {phase: phase[:data], phase_hash: phase, template: @template}%> diff --git a/app/views/user_mailer/api_token_granted_notification.html.erb b/app/views/user_mailer/api_token_granted_notification.html.erb index 82eb955..bf752fd 100644 --- a/app/views/user_mailer/api_token_granted_notification.html.erb +++ b/app/views/user_mailer/api_token_granted_notification.html.erb @@ -1,6 +1,7 @@ -

<% _('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" %>". -

+<% 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 %> +

<%= _('Hello') %> <%= @role.user.name %>

-<% -access_level = "read-only" -if @role.editor? - access_level = "editor" -end -if @role.administrator? - access_level = "co-owner" -end -%> + <% + access_level = "read-only" + if @role.editor? + access_level = "editor" + end + if @role.administrator? + access_level = "co-owner" + end + %> -

<%= ('Your permissions relating to') %>"<%= link_to @role.plan.title, url_for(action: 'show', controller: 'plans', id: @role.plan.id, locale: I18n.default_locale) %>"<%= _(' have changed. You now have') %><%= access_level %> <%= _('access.') %>

+

<%= _('Your permissions relating to') %> "<%= link_to @role.plan.title, url_for(action: 'show', controller: 'plans', id: @role.plan.id, locale: I18n.default_locale) %>" <%= _('have changed. You now have') %> <%= access_level %> <%= _('access.') %>

+<% 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.') %>

+

<%= _('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 %> +

<%= _('Hello ') %> <%= @role.user.name %>

-<% -access_level = "read-only" -if @role.editor? - access_level = "editor" -elsif @role.administrator? - access_level = "co-owner" -end -%> + <% + access_level = "read-only" + if @role.editor? + access_level = "editor" + elsif @role.administrator? + access_level = "co-owner" + end + %> -

<%= _('You have been given ') %><%= access_level %><%= _(' access to ') %>"<%= link_to @role.plan.title, url_for(action: 'show', controller: 'plans', id: @role.plan.id, locale: I18n.default_locale) %>" <%=_(' by ')%><%= @user.name %>.

+

<%= _('You have been given ') %><%= access_level %><%= _(' access to') %>"<%= link_to @role.plan.title, url_for(action: 'show', controller: 'plans', id: @role.plan.id, locale: FastGettext.default_locale) %>" <%=_(' by ')%><%= @user.name %>.

-

<%=_('Please follow the link above to login to ')%><%=Rails.configuration.branding[:application][:name]%><%=_(' to view/edit the plan')%>.

-

<%=_('Thanks')%>

-

<%=Rails.configuration.branding[:application][:name]%> <%=_(' team')%>

\ No newline at end of file +

<%=_('Please follow the link above to login to ')%><%=Rails.configuration.branding[:application][:name]%><%=_(' to view/edit the plan')%>.

+

<%=_('Thanks')%>

+

<%=Rails.configuration.branding[:application][:name]%> <%=_(' team')%>

+<% 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