diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb
index b033c4ec82..5b9636d6b9 100644
--- a/app/controllers/account_controller.rb
+++ b/app/controllers/account_controller.rb
@@ -30,6 +30,7 @@
class AccountController < ApplicationController
include CustomFieldsHelper
include Concerns::OmniauthLogin
+ include Concerns::RedirectAfterLogin
# prevents login action to be filtered by check_if_login_required application scope filter
skip_before_filter :check_if_login_required
@@ -162,7 +163,7 @@ class AccountController < ApplicationController
if user.save
token.destroy
- flash[:notice] = I18n.t(:notice_account_activated)
+ flash[:notice] = with_accessibility_notice I18n.t(:notice_account_activated)
else
flash[:error] = I18n.t(:notice_activation_failed)
end
@@ -406,7 +407,7 @@ class AccountController < ApplicationController
if @user.save
session[:auth_source_registration] = nil
self.logged_user = @user
- flash[:notice] = l(:notice_account_activated)
+ flash[:notice] = with_accessibility_notice l(:notice_account_activated)
redirect_to controller: '/my', action: 'account'
end
# Otherwise render register view again
@@ -477,7 +478,7 @@ class AccountController < ApplicationController
self.logged_user = user
opts[:after_login].call user if opts[:after_login]
- flash[:notice] = l(:notice_account_registered_and_logged_in)
+ flash[:notice] = with_accessibility_notice l(:notice_account_registered_and_logged_in)
redirect_after_login(user)
else
yield if block_given?
@@ -558,15 +559,6 @@ class AccountController < ApplicationController
redirect_to action: 'login', back_url: params[:back_url]
end
- def redirect_after_login(user)
- if user.first_login
- user.update_attribute(:first_login, false)
- redirect_to controller: '/my', action: 'first_login', back_url: params[:back_url]
- else
- redirect_back_or_default controller: '/my', action: 'page'
- end
- end
-
def invited_user
if session.include? :invitation_token
token = Token.find_by(value: session[:invitation_token])
@@ -574,4 +566,10 @@ class AccountController < ApplicationController
token.user
end
end
+
+ def with_accessibility_notice(text)
+ notice = link_translate(:notice_accessibility_mode, url: my_settings_url)
+
+ "#{text} #{notice}".html_safe
+ end
end
diff --git a/app/controllers/concerns/redirect_after_login.rb b/app/controllers/concerns/redirect_after_login.rb
new file mode 100644
index 0000000000..8d5170b776
--- /dev/null
+++ b/app/controllers/concerns/redirect_after_login.rb
@@ -0,0 +1,73 @@
+#-- copyright
+# OpenProject is a project management system.
+# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See doc/COPYRIGHT.rdoc for more details.
+#++
+
+##
+# Intended to be used by the AccountController to decide where to
+# send the user when they logged in.
+module Concerns::RedirectAfterLogin
+ def redirect_after_login(user)
+ if user.first_login
+ user.update_attribute(:first_login, false)
+
+ welcome_redirect
+ else
+ default_redirect
+ end
+ end
+
+ # * * *
+
+ def welcome_redirect
+ project = welcome_project
+
+ if project && redirect_to_welcome_project?(current_user, project)
+ redirect_to welcome_redirect_url(project)
+ else
+ default_redirect
+ end
+ end
+
+ def welcome_redirect_url(project)
+ url_for controller: :work_packages, project_id: project.identifier
+ end
+
+ ##
+ # Only the first user as the creator of the OpenProject installation is
+ # supposed to be redirected like this.
+ def redirect_to_welcome_project?(user, _project)
+ User.not_builtin.count == 1 && user.admin?
+ end
+
+ def welcome_project
+ DemoData::ProjectSeeder::Data.find_demo_project
+ end
+
+ def default_redirect
+ redirect_back_or_default controller: '/my', action: 'page'
+ end
+end
diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb
index 4831d6dfe2..00e5d31ef3 100644
--- a/app/controllers/my_controller.rb
+++ b/app/controllers/my_controller.rb
@@ -122,20 +122,6 @@ class MyController < ApplicationController
write_email_settings(redirect_to: :mail_notifications) if request.patch?
end
- def first_login
- if request.get?
- @user = User.current
- @back_url = url_for(params[:back_url])
-
- elsif request.post? || request.put?
- User.current.pref.attributes = permitted_params.pref || {}
- User.current.pref.save
-
- flash[:notice] = l(:notice_account_updated)
- redirect_back_or_default(controller: '/my', action: 'page')
- end
- end
-
# Create a new feeds key
def reset_rss_key
if request.post?
diff --git a/app/seeders/demo_data/project_seeder.rb b/app/seeders/demo_data/project_seeder.rb
index c6e3ca8ac7..8a724669fb 100644
--- a/app/seeders/demo_data/project_seeder.rb
+++ b/app/seeders/demo_data/project_seeder.rb
@@ -70,16 +70,18 @@ module DemoData
end
def reset_demo_project
- if delete_me = Project.find_by(identifier: I18n.t('seeders.demo_data.project.identifier'))
+ delete_demo_project
+ create_demo_project
+ end
+
+ def create_demo_project
+ Project.create! project_data
+ end
+
+ def delete_demo_project
+ if delete_me = find_demo_project
delete_me.destroy
end
-
- Project.create!(
- name: I18n.t('seeders.demo_data.project.name'),
- identifier: I18n.t('seeders.demo_data.project.identifier'),
- description: I18n.t('seeders.demo_data.project.description'),
- types: Type.all
- )
end
def set_members(project)
@@ -125,5 +127,40 @@ module DemoData
description: I18n.t('seeders.demo_data.board.description')
)
end
+
+ module Data
+ module_function
+
+ def project_data
+ {
+ name: project_name,
+ identifier: project_identifier,
+ description: project_description,
+ types: project_types
+ }
+ end
+
+ def project_name
+ I18n.t('seeders.demo_data.project.name')
+ end
+
+ def project_identifier
+ I18n.t('seeders.demo_data.project.identifier')
+ end
+
+ def project_description
+ I18n.t('seeders.demo_data.project.description')
+ end
+
+ def project_types
+ Type.all
+ end
+
+ def find_demo_project
+ Project.find_by(identifier: project_identifier)
+ end
+ end
+
+ include Data
end
end
diff --git a/app/views/my/first_login.html.erb b/app/views/my/first_login.html.erb
deleted file mode 100644
index 082b541cc5..0000000000
--- a/app/views/my/first_login.html.erb
+++ /dev/null
@@ -1,40 +0,0 @@
-<%#-- copyright
-OpenProject is a project management system.
-Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 3.
-
-OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-Copyright (C) 2006-2013 Jean-Philippe Lang
-Copyright (C) 2010-2013 the ChiliProject Team
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-See doc/COPYRIGHT.rdoc for more details.
-
-++#%>
-
-<%= styled_form_tag({ controller: '/my', action: "first_login" }, {method: :put}) do %>
-
- <%= back_url_hidden_field_tag %>
-
-
-
-
- <%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-yes' %>
-<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 58ce00224b..d7036ac5cb 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1345,6 +1345,7 @@ en:
noscript_heading: "JavaScript disabled"
noscript_learn_more: "Learn more"
+ notice_accessibility_mode: The accessibility mode can be enabled in your account [settings](url).
notice_account_activated: "Your account has been activated. You can now log in."
notice_account_already_activated: The account has already been activated.
notice_account_invalid_token: Invalid activation token
diff --git a/config/routes.rb b/config/routes.rb
index e63213dcf4..948ca01435 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -543,7 +543,6 @@ OpenProject::Application.routes.draw do
get '/my/page_layout', action: 'page_layout'
get '/my/password', action: 'password'
post '/my/change_password', action: 'change_password'
- match '/my/first_login', action: 'first_login', via: [:get, :put]
get '/my/page', action: 'page'
match '/my/account', action: 'account', via: [:get, :patch]
match '/my/settings', action: 'settings', via: [:get, :patch]
diff --git a/lib/redmine/i18n.rb b/lib/redmine/i18n.rb
index 823bd5854d..f17af8f145 100644
--- a/lib/redmine/i18n.rb
+++ b/lib/redmine/i18n.rb
@@ -69,6 +69,47 @@ module Redmine
Setting.date_format.blank? ? ::I18n.l(date.to_date) : date.strftime(Setting.date_format)
end
+ ##
+ # Gives a translation and inserts links into designated spots within it
+ # in the style of markdown links. Instead of the actual URL only names for
+ # the respective links are used in the translation.
+ #
+ # The method then expects a hash mapping each of those keys to actual URLs.
+ #
+ # For example:
+ #
+ # en.yml:
+ # en:
+ # logged_out: You have been logged out. Click [here](login) to login again.
+ #
+ # Which would then be used like this:
+ #
+ # link_translate(:logged_out, login: login_url)
+ #
+ # @param i18n_key [String] The I18n key to translate.
+ # @param links [Hash] Link names mapped to URLs.
+ def link_translate(i18n_key, links = {})
+ translation = ::I18n.t(i18n_key.to_s)
+ result = translation.scan(link_regex).inject(translation) do |t, matches|
+ link, text, key = matches
+ href = String(links[key.to_sym])
+
+ t.sub(link, "#{text}")
+ end
+
+ result.html_safe
+ end
+
+ ##
+ # Example: in `foo [bar](name) baz` matches:
+ #
+ # - `[bar](name)`
+ # - `bar`
+ # - `name`
+ def link_regex
+ /(\[(.+?)\]\((.+?)\))/
+ end
+
# format the time in the user time zone if one is set
# if none is set and the time is in utc time zone (meaning it came from active record), format the date in the system timezone
# otherwise just use the date in the time zone attached to the time
diff --git a/spec/controllers/account_controller_spec.rb b/spec/controllers/account_controller_spec.rb
index 02439fc269..88f55d9f74 100644
--- a/spec/controllers/account_controller_spec.rb
+++ b/spec/controllers/account_controller_spec.rb
@@ -81,7 +81,7 @@ describe AccountController, type: :controller do
auth_source_id: 66)
post :login, username: 'foo', password: 'bar'
- expect(response).to redirect_to my_first_login_path
+ expect(response).to redirect_to my_page_path
user = User.find_by_login('foo')
expect(user).to be_an_instance_of User
expect(user.auth_source_id).to eq(66)
@@ -295,28 +295,61 @@ describe AccountController, type: :controller do
end
context 'with password login enabled' do
- before do
- post :register, user: {
- login: 'register',
- password: 'adminADMIN!',
- password_confirmation: 'adminADMIN!',
- firstname: 'John',
- lastname: 'Doe',
- mail: 'register@example.com'
- }
+ # expects `redirect_to_path`
+ shared_examples 'automatic self registration succeeds' do
+ before do
+ post :register, user: {
+ login: 'register',
+ password: 'adminADMIN!',
+ password_confirmation: 'adminADMIN!',
+ firstname: 'John',
+ lastname: 'Doe',
+ mail: 'register@example.com'
+ }
+ end
+
+ it 'redirects to my page' do
+ is_expected.to respond_with :redirect
+ expect(assigns[:user]).not_to be_nil
+ is_expected.to redirect_to(redirect_to_path)
+ expect(User.where(login: 'register').last).not_to be_nil
+ end
+
+ it 'set the user status to active' do
+ user = User.where(login: 'register').last
+ expect(user).not_to be_nil
+ expect(user.status).to eq(User::STATUSES[:active])
+ end
end
- it 'redirects to first_login page' do
- is_expected.to respond_with :redirect
- expect(assigns[:user]).not_to be_nil
- is_expected.to redirect_to(my_first_login_path)
- expect(User.where(login: 'register').last).not_to be_nil
+ context 'without demo project' do
+ let(:redirect_to_path) { my_page_path }
+
+ it_behaves_like 'automatic self registration succeeds'
end
- it 'set the user status to active' do
- user = User.where(login: 'register').last
- expect(user).not_to be_nil
- expect(user.status).to eq(User::STATUSES[:active])
+ context 'with demo project' do
+ let!(:project) { FactoryGirl.create :project, identifier: 'demo' }
+
+ before do
+ allow(controller).to receive(:welcome_project).and_return(project)
+ end
+
+ context 'with the user not being admin' do
+ let(:redirect_to_path) { my_page_path }
+
+ it_behaves_like 'automatic self registration succeeds'
+ end
+
+ context 'with the user being admin' do
+ let(:redirect_to_path) { '/projects/demo/work_packages' }
+
+ before do
+ allow_any_instance_of(User).to receive(:admin?).and_return(true)
+ end
+
+ it_behaves_like 'automatic self registration succeeds'
+ end
end
end
diff --git a/spec/controllers/concerns/omniauth_login_spec.rb b/spec/controllers/concerns/omniauth_login_spec.rb
index 4647d37e48..6ebebba166 100644
--- a/spec/controllers/concerns/omniauth_login_spec.rb
+++ b/spec/controllers/concerns/omniauth_login_spec.rb
@@ -73,8 +73,7 @@ describe AccountController, type: :controller do
end
it 'redirects to the first login page with a back_url' do
- expect(response).to redirect_to(
- my_first_login_path(back_url: 'https://example.net/some_back_url'))
+ expect(response).to redirect_to(my_page_path)
end
end
@@ -167,7 +166,7 @@ describe AccountController, type: :controller do
firstname: 'Foo',
lastname: 'Smith',
mail: 'foo@bar.com' }
- expect(response).to redirect_to my_first_login_path
+ expect(response).to redirect_to my_page_path
user = User.find_by_login('login@bar.com')
expect(user).to be_an_instance_of(User)
@@ -382,7 +381,7 @@ describe AccountController, type: :controller do
post :omniauth_login
- expect(response).to redirect_to my_first_login_path
+ expect(response).to redirect_to my_page_path
# authorization is successful which results in the registration
# of a new user in this case because we changed the provider
# and there isn't a user with that identity URL yet ...
diff --git a/spec/features/auth/login_spec.rb b/spec/features/auth/login_spec.rb
index 40166dc9ac..ef36c7b468 100644
--- a/spec/features/auth/login_spec.rb
+++ b/spec/features/auth/login_spec.rb
@@ -82,12 +82,6 @@ describe 'Login', type: :feature do
fill_in('new_password_confirmation', with: new_user_password)
click_link_or_button I18n.t(:button_save)
end
- expect(current_path).to eql my_first_login_path
-
- # we just save the form and go on
- within('#main') do
- click_link_or_button I18n.t(:button_save)
- end
# on the my page
expect(current_path).to eql my_page_path
diff --git a/spec/features/auth/omniauth_spec.rb b/spec/features/auth/omniauth_spec.rb
index 47a69e3403..a8471ebd61 100644
--- a/spec/features/auth/omniauth_spec.rb
+++ b/spec/features/auth/omniauth_spec.rb
@@ -137,7 +137,8 @@ describe 'Omniauth authentication', type: :feature do
shared_examples 'omniauth user registration' do
it 'should register new user' do
- visit '/auth/developer'
+ visit '/'
+ find_link('Omniauth Developer').click
# login form developer strategy
fill_in('first_name', with: user.firstname)
@@ -186,9 +187,6 @@ describe 'Omniauth authentication', type: :feature do
fill_in('user_lastname', with: user.lastname)
click_link_or_button 'Submit'
- # now, we see the my/first_login page and just save
- click_link_or_button 'Save'
-
expect(current_url).to eql account_lost_password_url
end
diff --git a/spec/features/users/create_spec.rb b/spec/features/users/create_spec.rb
index 8bd5521c49..1599274c69 100644
--- a/spec/features/users/create_spec.rb
+++ b/spec/features/users/create_spec.rb
@@ -125,9 +125,9 @@ describe 'create users', type: :feature, selenium: true do
click_button 'Sign in'
- expect(page).to have_text 'My account'
+ expect(page).to have_text 'My page'
expect(page).to have_text 'bobfirst boblast'
- expect(current_path).to eq '/my/first_login'
+ expect(current_path).to eq '/my/page'
end
end
end
diff --git a/spec/lib/redmine/i18n_spec.rb b/spec/lib/redmine/i18n_spec.rb
index e729a86a9c..8b7a5102c5 100644
--- a/spec/lib/redmine/i18n_spec.rb
+++ b/spec/lib/redmine/i18n_spec.rb
@@ -151,5 +151,24 @@ module OpenProject
expect(find_language(:DE)).to eql :de
end
end
+
+ describe 'link_translation' do
+ before do
+ allow(::I18n)
+ .to receive(:t)
+ .with('translation_with_a_link')
+ .and_return('There is a [link](url_1) in this translation! Maybe even [two](url_2)?')
+ end
+
+ it 'allows to insert links into translations' do
+ translated = link_translate :translation_with_a_link,
+ url_1: 'http://openproject.com/foobar',
+ url_2: '/baz'
+
+ expect(translated).to eq(
+ "There is a link in this translation!" +
+ " Maybe even two?")
+ end
+ end
end
end