diff --git a/app/controllers/attribute_help_texts_controller.rb b/app/controllers/attribute_help_texts_controller.rb
new file mode 100644
index 00000000000..7bd3c6636cf
--- /dev/null
+++ b/app/controllers/attribute_help_texts_controller.rb
@@ -0,0 +1,112 @@
+#-- encoding: UTF-8
+#-- 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.
+#++
+
+class AttributeHelpTextsController < ApplicationController
+ layout 'admin'
+ menu_item :attribute_help_texts
+
+ before_action :require_admin
+ before_action :find_entry, only: %i(edit update destroy)
+ before_action :find_type_scope
+
+ def new
+ @attribute_help_text = AttributeHelpText.new type: @attribute_scope
+ end
+
+ def edit; end
+
+ def update
+ @attribute_help_text.attributes = permitted_params.attribute_help_text
+
+ if @attribute_help_text.save
+ flash[:notice] = t(:notice_successful_update)
+ redirect_to attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)
+ else
+ render action: 'edit'
+ end
+ end
+
+ def create
+ @attribute_help_text = AttributeHelpText.new permitted_params.attribute_help_text
+
+ if @attribute_help_text.save
+ flash[:notice] = t(:notice_successful_create)
+ redirect_to attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)
+ else
+ render action: 'new'
+ end
+ end
+
+ def destroy
+ if @attribute_help_text.destroy
+ flash[:notice] = t(:notice_successful_delete)
+ else
+ flash[:error] = t(:error_can_not_delete_entry)
+ end
+
+ redirect_to attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)
+ end
+
+ def index
+ @texts_by_type = AttributeHelpText.all.group_by(&:attribute_scope)
+ end
+
+ protected
+
+ def default_breadcrumb
+ if action_name == 'index'
+ t('attribute_help_texts.label_plural')
+ else
+ ActionController::Base.helpers.link_to(t('attribute_help_texts.label_plural'), attribute_help_texts_path)
+ end
+ end
+
+ def show_local_breadcrumb
+ true
+ end
+
+ private
+
+ def find_entry
+ @attribute_help_text = AttributeHelpText.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ def find_type_scope
+ name = params.fetch(:name, 'WorkPackage')
+ submodule = AttributeHelpText.available_types.find { |mod| mod == name }
+
+ if submodule.nil?
+ render_404
+ end
+
+ @attribute_scope = AttributeHelpText.const_get(submodule)
+ end
+end
diff --git a/app/helpers/attribute_help_texts_helper.rb b/app/helpers/attribute_help_texts_helper.rb
new file mode 100644
index 00000000000..7290a3db9ec
--- /dev/null
+++ b/app/helpers/attribute_help_texts_helper.rb
@@ -0,0 +1,40 @@
+#-- encoding: UTF-8
+
+#-- copyright
+# OpenProject is a project management system.
+# Copyright (C) 2012-2017 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-2017 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.
+#++
+
+module AttributeHelpTextsHelper
+ def selectable_attributes(instance)
+ available = instance.class.available_attributes
+ used = AttributeHelpText.used_attributes(instance.type)
+
+ available.reject! { |k, | used.include? k }
+
+ available.map { |k, v| [v, k] }
+ end
+end
diff --git a/app/models/attribute_help_text.rb b/app/models/attribute_help_text.rb
new file mode 100644
index 00000000000..ba463928b5b
--- /dev/null
+++ b/app/models/attribute_help_text.rb
@@ -0,0 +1,30 @@
+
+class AttributeHelpText < ActiveRecord::Base
+ def self.available_types
+ subclasses.map { |child| child.name.demodulize }
+ end
+
+ def self.used_attributes(scope)
+ where(type: scope)
+ .select(:attribute_name)
+ .distinct
+ .pluck(:attribute_name)
+ end
+
+ validates_presence_of :help_text
+ validates_uniqueness_of :attribute_name, scope: :type
+
+ def attribute_caption
+ self.class.available_attributes[attribute_name]
+ end
+
+ def attribute_scope
+ raise 'not implemented'
+ end
+
+ def type_caption
+ raise 'not implemented'
+ end
+end
+
+require_dependency 'attribute_help_text/work_package'
diff --git a/app/models/attribute_help_text/work_package.rb b/app/models/attribute_help_text/work_package.rb
new file mode 100644
index 00000000000..d72ae133ad4
--- /dev/null
+++ b/app/models/attribute_help_text/work_package.rb
@@ -0,0 +1,15 @@
+class AttributeHelpText::WorkPackage < AttributeHelpText
+ def self.available_attributes
+ Hash[::Query.new.available_columns.map { |c| [c.name.to_s, c.caption] }]
+ end
+
+ validates_inclusion_of :attribute_name, in: available_attributes.keys
+
+ def attribute_scope
+ 'WorkPackage'
+ end
+
+ def type_caption
+ I18n.t(:label_work_package)
+ end
+end
diff --git a/app/models/permitted_params.rb b/app/models/permitted_params.rb
index 3b95c131672..b475c9922ba 100644
--- a/app/models/permitted_params.rb
+++ b/app/models/permitted_params.rb
@@ -57,6 +57,10 @@ class PermittedParams
permitted_attributes[key].concat(params)
end
+ def attribute_help_text
+ params.require(:attribute_help_text).permit(*self.class.permitted_attributes[:attribute_help_text])
+ end
+
def auth_source
params.require(:auth_source).permit(*self.class.permitted_attributes[:auth_source])
end
@@ -446,6 +450,11 @@ class PermittedParams
def self.permitted_attributes
@whitelisted_params ||= begin
params = {
+ attribute_help_text: [
+ :type,
+ :attribute_name,
+ :help_text
+ ],
auth_source: [
:name,
:host,
diff --git a/app/views/attribute_help_texts/_form.html.erb b/app/views/attribute_help_texts/_form.html.erb
new file mode 100644
index 00000000000..d755a1030f2
--- /dev/null
+++ b/app/views/attribute_help_texts/_form.html.erb
@@ -0,0 +1,47 @@
+<%#-- copyright
+OpenProject is a project management system.
+Copyright (C) 2012-2017 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-2017 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.
+
+++#%>
+<%= error_messages_for 'attribute_help_text' %>
+
+
diff --git a/app/views/attribute_help_texts/_tab.html.erb b/app/views/attribute_help_texts/_tab.html.erb
new file mode 100644
index 00000000000..18d1fe457e8
--- /dev/null
+++ b/app/views/attribute_help_texts/_tab.html.erb
@@ -0,0 +1,100 @@
+<%#-- copyright
+OpenProject is a project management system.
+Copyright (C) 2012-2017 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-2017 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.
+
+++#%>
+
+<% entries = @texts_by_type[tab[:name]] || [] %>
+<% if entries.any? %>
+
+<% else %>
+ <%= no_results_box %>
+<% end %>
+
+
+ <%= link_to new_attribute_help_text_path(name: tab[:name]),
+ { class: 'attribute-help-texts--create-button button -alt-highlight',
+ aria: {label: t(:'attribute_help_texts.add_new')},
+ title: t(:'attribute_help_texts.add_new')} do %>
+ <%= op_icon('button--icon icon-add') %>
+ <%= t('activerecord.models.attribute_help_text') %>
+ <% end %>
+
diff --git a/app/views/attribute_help_texts/edit.html.erb b/app/views/attribute_help_texts/edit.html.erb
new file mode 100644
index 00000000000..0a6224ed276
--- /dev/null
+++ b/app/views/attribute_help_texts/edit.html.erb
@@ -0,0 +1,44 @@
+<%#-- copyright
+OpenProject is a project management system.
+Copyright (C) 2012-2017 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-2017 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.
+
+++#%>
+
+<% html_title t(:label_administration), l(:'attribute_help_texts.edit', attribute_caption: @attribute_help_text.attribute_caption) %>
+<% local_assigns[:additional_breadcrumb] = link_to(@attribute_help_text.type_caption,
+ attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)),
+ t(:'attribute_help_texts.edit', attribute_caption: @attribute_help_text.attribute_caption) %>
+<%= breadcrumb_toolbar t(:'attribute_help_texts.edit', attribute_caption: @attribute_help_text.attribute_caption)
+%>
+
+<%= labelled_tabular_form_for @attribute_help_text,
+ as: 'attribute_help_text',
+ url: { action: :update },
+ html: {id: 'attribute_help_text_form'} do |f| %>
+ <%= render partial: 'form', locals: { f: f, editing: true } %>
+ <%= hidden_field_tag 'attribute_scope', @attribute_help_text.attribute_scope %>
+ <%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %>
+<% end %>
diff --git a/app/views/attribute_help_texts/index.html.erb b/app/views/attribute_help_texts/index.html.erb
new file mode 100644
index 00000000000..ab659bab8be
--- /dev/null
+++ b/app/views/attribute_help_texts/index.html.erb
@@ -0,0 +1,42 @@
+<%#-- copyright
+OpenProject is a project management system.
+Copyright (C) 2012-2017 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-2017 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.
+
+++#%>
+<%= toolbar title: t(:'attribute_help_texts.label_plural') %>
+
+
+
+
<%= t('attribute_help_texts.text_overview') %>
+
+
+
+<%= render_tabs [
+ { name: 'WorkPackage', partial: 'attribute_help_texts/tab', label: :label_work_package },
+ ]
+%>
+
+<% html_title(t(:label_administration), t(:'attribute_help_texts.label_plural')) -%>
diff --git a/app/views/attribute_help_texts/new.html.erb b/app/views/attribute_help_texts/new.html.erb
new file mode 100644
index 00000000000..47fd4c5fb00
--- /dev/null
+++ b/app/views/attribute_help_texts/new.html.erb
@@ -0,0 +1,44 @@
+<%#-- copyright
+OpenProject is a project management system.
+Copyright (C) 2012-2017 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-2017 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.
+
+++#%>
+
+<% html_title t(:label_administration), t(:'attribute_help_texts.add_new') %>
+<% local_assigns[:additional_breadcrumb] = link_to(@attribute_help_text.type_caption,
+ attribute_help_texts_path(tab: @attribute_help_text.attribute_scope)),
+ t(:'attribute_help_texts.add_new') %>
+<%= breadcrumb_toolbar t(:'attribute_help_texts.add_new')
+%>
+
+<%= labelled_tabular_form_for @attribute_help_text,
+ as: 'attribute_help_text',
+ url: { action: :create },
+ html: {id: 'attribute_help_text_form'} do |f| %>
+ <%= render partial: 'form', locals: { f: f } %>
+ <%= f.hidden_field :type, value: @attribute_help_text.type %>
+ <%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark' %>
+<% end %>
diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb
index c418e021c6d..b16a2e05582 100644
--- a/config/initializers/menus.rb
+++ b/config/initializers/menus.rb
@@ -162,6 +162,11 @@ Redmine::MenuManager.map :admin_menu do |menu|
icon: 'icon2 icon-custom-fields',
html: { class: 'custom_fields' }
+ menu.push :attribute_help_texts,
+ { controller: '/attribute_help_texts' },
+ caption: :'attribute_help_texts.label_plural',
+ icon: 'icon2 icon-help2'
+
menu.push :enumerations,
{ controller: '/enumerations' },
icon: 'icon2 icon-enumerations'
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 0fd1fa183ce..a00d426bb8a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -69,6 +69,13 @@ en:
is_active: currently displayed
is_inactive: currently not displayed
+ attribute_help_texts:
+ text_overview: 'In this view, you can create custom help texts for attributes view. When defined, these texts can be shown by clicking the help icon next to its belonging attribute.'
+ label_plural: 'Attribute help texts'
+ show_preview: 'Preview text'
+ add_new: 'Add help text'
+ edit: "Edit help text for %{attribute_caption}"
+
auth_sources:
index:
no_results_content_title: There are currently no authentication modes.
@@ -297,6 +304,9 @@ en:
file: "File"
filename: "File"
filesize: "Size"
+ attribute_help_text:
+ attribute_name: 'Attribute'
+ help_text: 'Help text'
auth_source:
account: "Account"
attr_firstname: "Firstname attribute"
@@ -543,6 +553,7 @@ en:
models:
attachment: "File"
+ attribute_help_text: "Attribute help text"
board: "Forum"
comment: "Comment"
custom_field: "Custom field"
@@ -868,6 +879,7 @@ en:
enumeration_reported_project_statuses: "Reported project status"
error_can_not_archive_project: "This project cannot be archived"
+ error_can_not_delete_entry: "Unable to delete entry"
error_can_not_delete_custom_field: "Unable to delete custom field"
error_can_not_delete_type: "This type contains work packages and cannot be deleted."
error_can_not_delete_standard_type: "Standard types cannot be deleted."
diff --git a/config/routes.rb b/config/routes.rb
index 055cac04a2e..0e2456febf5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -381,6 +381,8 @@ OpenProject::Application.routes.draw do
post 'design/colors' => 'custom_styles#update_colors', as: 'update_design_colors'
resource :custom_style, only: [:update, :show, :create], path: 'design'
+ resources :attribute_help_texts, only: %i(index new create edit update destroy)
+
resources :groups do
member do
get :autocomplete_for_user
diff --git a/db/migrate/20170703075208_add_attribute_help_texts.rb b/db/migrate/20170703075208_add_attribute_help_texts.rb
new file mode 100644
index 00000000000..1f2b575e8b0
--- /dev/null
+++ b/db/migrate/20170703075208_add_attribute_help_texts.rb
@@ -0,0 +1,11 @@
+class AddAttributeHelpTexts < ActiveRecord::Migration[5.0]
+ def change
+ create_table :attribute_help_texts do |t|
+ t.text :help_text, null: false
+ t.string :type, null: false
+ t.string :attribute_name, null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/spec/controllers/attribute_help_texts_controller_spec.rb b/spec/controllers/attribute_help_texts_controller_spec.rb
new file mode 100644
index 00000000000..26a8413aba0
--- /dev/null
+++ b/spec/controllers/attribute_help_texts_controller_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe AttributeHelpTextsController, type: :controller do
+ let(:model) { FactoryGirl.build :work_package_help_text }
+ before do
+ expect(controller).to receive(:require_admin)
+ end
+
+ describe '#index' do
+ before do
+ allow(AttributeHelpText).to receive(:all).and_return [model]
+
+ get :index
+ end
+
+ it do
+ expect(response).to be_success
+ expect(assigns(:texts_by_type)).to eql('WorkPackage' => [model])
+ end
+ end
+
+ describe '#edit' do
+ context 'when not found'
+ before do
+ get :edit, params: { id: 1234 }
+ end
+
+ it do
+ expect(response.status).to eq 404
+ end
+ end
+
+ context 'when found' do
+ before do
+ allow(AttributeHelpText).to receive(:find).and_return(model)
+
+ get :edit
+ end
+
+ it do
+ expect(response).to be_success
+ expect(assigns(:attribute_help_text)).to eql model
+ end
+ end
+
+ describe '#update' do
+ before do
+ allow(AttributeHelpText).to receive(:find).and_return(model)
+ expect(model).to receive(:save).and_return(success)
+ put :update,
+ params: {
+ attribute_help_text: {
+ help_text: 'my new help text'
+ }
+ }
+ end
+
+ context 'when save is failure' do
+ let(:success) { false }
+ it 'fails to update the announcement' do
+ expect(response).to be_success
+ expect(response).to render_template 'edit'
+ end
+ end
+
+ context 'when save is success' do
+ let(:success) { true }
+ it 'edits the announcement' do
+ expect(response).to redirect_to action: :index, tab: 'WorkPackage'
+ expect(controller).to set_flash[:notice].to I18n.t(:notice_successful_update)
+
+ expect(model.help_text).to eq('my new help text')
+ end
+ end
+ end
+end
diff --git a/spec/factories/attribute_help_text_factory.rb b/spec/factories/attribute_help_text_factory.rb
new file mode 100644
index 00000000000..821a7ff4d7d
--- /dev/null
+++ b/spec/factories/attribute_help_text_factory.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+ factory :work_package_help_text, class: AttributeHelpText::WorkPackage do
+ type 'AttributeHelpText::WorkPackage'
+ help_text 'Attribute help text'
+ attribute_name 'id'
+ end
+end
diff --git a/spec/features/admin/attribute_help_texts_spec.rb b/spec/features/admin/attribute_help_texts_spec.rb
new file mode 100644
index 00000000000..8b3de6256a1
--- /dev/null
+++ b/spec/features/admin/attribute_help_texts_spec.rb
@@ -0,0 +1,90 @@
+#-- copyright
+# OpenProject is a project management system.
+# Copyright (C) 2012-2017 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-2017 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.
+#++
+
+require 'spec_helper'
+
+describe 'Attribute help texts', type: :feature, js: true do
+ let(:admin) { FactoryGirl.create(:admin) }
+
+ describe 'Work package help texts' do
+ before do
+ login_as(admin)
+ visit attribute_help_texts_path
+ end
+
+ it 'allows CRUD to attribute help texts' do
+ expect(page).to have_selector('.generic-table--no-results-container')
+
+ # Create help text
+ # -> new
+ page.find('.attribute-help-texts--create-button').click
+
+ # Set attributes
+ # -> create
+ select 'Status', from: 'attribute_help_text_attribute_name'
+ fill_in 'Help text', with: 'My attribute help text'
+ click_button 'Save'
+
+ # Should now show on index for editing
+ expect(page).to have_selector('.attribute-help-text--entry td', text: 'Status')
+ instance = AttributeHelpText.last
+ expect(instance.attribute_scope).to eq 'WorkPackage'
+ expect(instance.attribute_name).to eq 'status'
+ expect(instance.help_text).to eq 'My attribute help text'
+
+ # -> edit
+ page.find('.attribute-help-text--entry td a', text: 'Status').click
+ expect(page).to have_selector('#attribute_help_text_attribute_name[disabled]')
+ fill_in 'Help text', with: ''
+ click_button 'Save'
+
+ # Handle errors
+ expect(page).to have_selector('#errorExplanation', text: "Help text can't be blank.")
+ fill_in 'Help text', with: 'New help text'
+ click_button 'Save'
+
+ # On index again
+ expect(page).to have_selector('.attribute-help-text--entry td', text: 'Status')
+ instance.reload
+ expect(instance.help_text).to eq 'New help text'
+
+ # Create new, status is now blocked
+ page.find('.attribute-help-texts--create-button').click
+ expect(page).to have_selector('#attribute_help_text_attribute_name option', text: 'ID')
+ expect(page).to have_no_selector('#attribute_help_text_attribute_name option', text: 'Status')
+ visit attribute_help_texts_path
+
+ # Destroy
+ page.find('.attribute-help-text--entry a.icon-delete').click
+ page.driver.browser.switch_to.alert.accept
+
+ expect(page).to have_selector('.generic-table--no-results-container')
+ expect(AttributeHelpText.count).to be_zero
+ end
+ end
+end
diff --git a/spec/models/attribute_help_text/work_package_spec.rb b/spec/models/attribute_help_text/work_package_spec.rb
new file mode 100644
index 00000000000..0a88a6cc0cb
--- /dev/null
+++ b/spec/models/attribute_help_text/work_package_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe AttributeHelpText::WorkPackage, type: :model do
+ describe '.available_attributes' do
+ subject { described_class.available_attributes }
+ it 'returns an array of potential attributes' do
+ expect(subject).to be_a Hash
+ end
+ end
+
+ describe '.used_attributes' do
+ let!(:instance) { FactoryGirl.create :work_package_help_text }
+ subject { described_class.used_attributes instance.type }
+
+ it 'returns used attributes' do
+ expect(subject).to eq([instance.attribute_name])
+ end
+ end
+
+ describe 'validations' do
+ before do
+ allow(described_class).to receive(:available_attributes).and_return(id: 'ID')
+ end
+
+ let(:attribute_name) { 'id' }
+ let(:help_text) { 'foobar' }
+
+ subject { described_class.new attribute_name: attribute_name, help_text: help_text }
+
+ context 'help_text is nil' do
+ let(:help_text) { nil }
+
+ it 'validates presence of help text' do
+ expect(subject.valid?).to be_falsey
+ expect(subject.errors[:help_text].count).to eql(1)
+ expect(subject.errors[:help_text].first)
+ .to eql(I18n.t('activerecord.errors.messages.blank'))
+ end
+ end
+
+ context 'attribute_name is nil' do
+ let(:attribute_name) { nil }
+
+ it 'validates presence of attribute name' do
+ expect(subject.valid?).to be_falsey
+ expect(subject.errors[:attribute_name].count).to eql(1)
+ expect(subject.errors[:attribute_name].first)
+ .to eql(I18n.t('activerecord.errors.messages.inclusion'))
+ end
+ end
+
+ context 'attribute_name is invalid' do
+ let(:attribute_name) { 'foobar' }
+
+ it 'validates inclusion of attribute name' do
+ expect(subject.valid?).to be_falsey
+ expect(subject.errors[:attribute_name].count).to eql(1)
+ expect(subject.errors[:attribute_name].first)
+ .to eql(I18n.t('activerecord.errors.messages.inclusion'))
+ end
+ end
+ end
+
+ describe 'instance' do
+ subject { FactoryGirl.build :work_package_help_text }
+
+ it 'provides a caption of its type' do
+ expect(subject.attribute_scope).to eq 'WorkPackage'
+ expect(subject.type_caption).to eq I18n.t(:label_work_package)
+ end
+ end
+end
diff --git a/spec/routing/attribute_help_text_spec.rb b/spec/routing/attribute_help_text_spec.rb
new file mode 100644
index 00000000000..c01bb9e1746
--- /dev/null
+++ b/spec/routing/attribute_help_text_spec.rb
@@ -0,0 +1,48 @@
+#-- copyright
+# OpenProject is a project management system.
+# Copyright (C) 2012-2017 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-2017 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.
+#++
+
+require 'spec_helper'
+
+describe AttributeHelpTextsController, type: :routing do
+ it 'should route CRUD to the controller' do
+ expect(get('/admin/attribute_help_texts'))
+ .to route_to(controller: 'attribute_help_texts', action: 'index')
+
+ expect(get('/admin/attribute_help_texts/1/edit'))
+ .to route_to(controller: 'attribute_help_texts', action: 'edit', id: '1')
+
+ expect(post('/admin/attribute_help_texts'))
+ .to route_to(controller: 'attribute_help_texts', action: 'create')
+
+ expect(put('/admin/attribute_help_texts/1'))
+ .to route_to(controller: 'attribute_help_texts', action: 'update', id: '1')
+
+ expect(delete('/admin/attribute_help_texts/1'))
+ .to route_to(controller: 'attribute_help_texts', action: 'destroy', id: '1')
+ end
+end