diff --git a/app/controllers/custom_tables_controller.rb b/app/controllers/custom_tables_controller.rb index 3f59565..02d1cf6 100644 --- a/app/controllers/custom_tables_controller.rb +++ b/app/controllers/custom_tables_controller.rb @@ -152,7 +152,8 @@ class CustomTablesController < ApplicationController def setting_tabs @setting_tabs = [ - {name: 'general', partial: 'custom_tables/edit', label: :label_general} + {name: 'general', partial: 'custom_tables/edit', label: :label_general}, + {name: 'custom_fields', partial: 'custom_tables/settings/custom_fields', label: :label_custom_field_plural} ] call_hook(:controller_setting_tabs_after, { setting_tabs: @setting_tabs, custom_table: @custom_table }) end diff --git a/app/models/custom_table.rb b/app/models/custom_table.rb index 515fa79..2759ec0 100644 --- a/app/models/custom_table.rb +++ b/app/models/custom_table.rb @@ -6,6 +6,8 @@ class CustomTable < ActiveRecord::Base has_many :custom_entities, dependent: :destroy has_one :custom_entity has_and_belongs_to_many :projects + has_and_belongs_to_many :trackers + has_and_belongs_to_many :roles acts_as_nested_set @@ -20,7 +22,22 @@ class CustomTable < ActiveRecord::Base end } - safe_attributes 'name', 'author_id', 'main_custom_field_id', 'project_ids', 'is_for_all' + scope :visible, lambda {|*args| + user = args.shift || User.current + if user.admin? + # nop + elsif user.memberships.any? + where("#{table_name}.visible = ? OR #{table_name}.id IN (SELECT DISTINCT cfr.custom_table_id FROM #{Member.table_name} m" + + " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" + + " INNER JOIN #{table_name_prefix}custom_tables_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" + + " WHERE m.user_id = ?)", + true, user.id) + else + where(:visible => true) + end + } + + safe_attributes 'name', 'author_id', 'main_custom_field_id', 'project_ids', 'is_for_all', 'description', 'tracker_ids', 'role_ids', 'visible' validates :name, presence: true, uniqueness: true diff --git a/app/views/custom_tables/_edit.html.erb b/app/views/custom_tables/_edit.html.erb index 2e77296..3ffae54 100644 --- a/app/views/custom_tables/_edit.html.erb +++ b/app/views/custom_tables/_edit.html.erb @@ -1,11 +1,4 @@ <%= labelled_form_for :custom_table, @custom_table, url: custom_table_path(project_id: @project), html: {multipart: true, id: 'custom_table_form'} do |f| %> <%= render partial: 'form', locals: {f: f} %> - <% tab = {name: 'CustomEntityCustomField', partial: 'table_fields/index', label: :label_spent_time} %> - - <%= content_tag('div', render(partial: 'table_fields/index', locals: {tab: tab, f: f})) %> - - <%= javascript_tag do %> - $(function() { $("table.custom_fields tbody").positionedItems(); }); - <% end %> <%= submit_tag l(:button_update) %> <% end %> \ No newline at end of file diff --git a/app/views/custom_tables/_form.html.erb b/app/views/custom_tables/_form.html.erb index 2a40509..c92ae51 100644 --- a/app/views/custom_tables/_form.html.erb +++ b/app/views/custom_tables/_form.html.erb @@ -1,15 +1,49 @@ <%= error_messages_for @custom_table %> +
-
<%= l :label_general %> -
-

<%= f.text_field :name %>

-
+
+

<%= f.text_field :name, size: 50, required: true %>

+

<%= f.text_area :description, rows: 7 %>

+
+ +
<%= toggle_checkboxes_link("#custom_table_tracker_ids input[type=checkbox]") %><%=l(:label_tracker_plural)%> + <% tracker_ids = @custom_table.tracker_ids %> + <% Tracker.sorted.each do |tracker| %> + <%= check_box_tag "custom_table[tracker_ids][]", + tracker.id, + tracker_ids.include?(tracker.id), + :id => "custom_table_tracker_ids_#{tracker.id}" %> + + <% end %> + <%= hidden_field_tag "custom_table[tracker_ids][]", '' %>
+
<%= l(:field_visible) %> + + + <% role_ids = @custom_table.role_ids %> + <% Role.givable.sorted.each do |role| %> + + <% end %> + <%= hidden_field_tag 'custom_table[role_ids][]', '' %> +
+
<%= toggle_checkboxes_link("#custom_table_project_ids input[type=checkbox]:enabled") %><%= l(:label_project_plural) %> -

<%= f.check_box :is_for_all, onclick: "window.AllProjects.toggle($(this).is(':checked'))" %>

+

<%= f.check_box :is_for_all, onclick: "window.ToggleCheckboxes.projects($(this).is(':checked'))" %>

<% project_ids = @custom_table.project_ids.to_a %> @@ -22,9 +56,12 @@
\ No newline at end of file diff --git a/app/views/custom_tables/edit.html.erb b/app/views/custom_tables/edit.html.erb index ca18a52..7044199 100644 --- a/app/views/custom_tables/edit.html.erb +++ b/app/views/custom_tables/edit.html.erb @@ -1,4 +1,4 @@ <%= title [l(:label_custom_tables), custom_tables_path], [@custom_table.name, custom_table_path(@custom_table)], l(:label_settings) %> -<%= render_tabs @setting_tabs %> +<%= render_tabs @setting_tabs, params[:tab] %> <% html_title(l(:label_settings)) -%> \ No newline at end of file diff --git a/app/views/custom_tables/settings/_custom_fields.html.erb b/app/views/custom_tables/settings/_custom_fields.html.erb new file mode 100644 index 0000000..d095aa0 --- /dev/null +++ b/app/views/custom_tables/settings/_custom_fields.html.erb @@ -0,0 +1,10 @@ +<%= labelled_form_for :custom_table, @custom_table, url: custom_table_path(project_id: @project, back_url: edit_custom_table_path(@custom_table, tab: 'custom_fields')), html: {multipart: true, id: 'custom_table_form'} do |f| %> + <% tab = {name: 'CustomEntityCustomField', partial: 'table_fields/index', label: :label_spent_time} %> + + <%= content_tag('div', render(partial: 'table_fields/index', locals: {tab: tab, f: f})) %> + + <%= javascript_tag do %> + $(function() { $("table.custom_fields tbody").positionedItems(); }); + <% end %> + <%= submit_tag l(:button_update) %> +<% end %> \ No newline at end of file diff --git a/app/views/issues/_custom_tables.html.erb b/app/views/issues/_custom_tables.html.erb index 1a3199f..641828b 100644 --- a/app/views/issues/_custom_tables.html.erb +++ b/app/views/issues/_custom_tables.html.erb @@ -1,5 +1,5 @@ <% if User.current.allowed_to?(:manage_custom_tables, nil, global: true) %> - <% issue.project.all_issue_custom_tables.each do |custom_table| %> + <% issue.project.all_issue_custom_tables(issue).each do |custom_table| %> <% query = custom_table.query(totalable_all: true) %> <% query.add_short_filter('issue_id', "=#{issue.id}") %> <% back_url = issue_path(issue) %> @@ -17,7 +17,7 @@ back_url: back_url, entities: query.results_scope} %> <% end %> -<% elsif issue.project.all_issue_custom_tables.any? %> +<% elsif issue.project.all_issue_custom_tables(issue).any? %>

<%= l(:text_missing_permission_manage_custom_tables) %> diff --git a/app/views/table_fields/_form.html.erb b/app/views/table_fields/_form.html.erb index ffa47f8..ef9b763 100644 --- a/app/views/table_fields/_form.html.erb +++ b/app/views/table_fields/_form.html.erb @@ -82,9 +82,7 @@ when "IssueCustomField" %> <% else %>

<%= f.check_box :is_required %>

<%= f.check_box :is_filter, { checked: true } %>

- <% if User.current.admin? %> -

<%= f.text_field :external_name %>

- <% end %> +

<%= f.text_field :external_name %>

<% end %> <%= call_hook(:"view_custom_fields_form_#{@custom_field.type.to_s.underscore}", :custom_field => @custom_field, :form => f) %>
diff --git a/app/views/table_fields/_index.html.erb b/app/views/table_fields/_index.html.erb index 3573dfa..9164bf5 100644 --- a/app/views/table_fields/_index.html.erb +++ b/app/views/table_fields/_index.html.erb @@ -1,3 +1,8 @@ +<% back_url = edit_custom_table_path(@custom_table, tab: 'custom_fields')%> +
+ <%= link_to l(:label_new_column), new_table_field_path(custom_table_id: @custom_table, back_url: back_url), remote: true, class: 'icon icon-custom-fields' %> +
+ @@ -9,14 +14,14 @@ <% (@custom_fields_by_type[tab[:name]] || []).sort.each do |custom_field| -%> "> - + <% end; reset_cycle %> diff --git a/app/views/table_fields/edit.html.erb b/app/views/table_fields/edit.html.erb index bb2044f..fc19f9b 100644 --- a/app/views/table_fields/edit.html.erb +++ b/app/views/table_fields/edit.html.erb @@ -1,5 +1,5 @@ <%= labelled_form_for :custom_field, @custom_field, url: table_field_path(@custom_field), html: {method: :put, id: 'custom_field_form'} do |f| %> - <%= hidden_field_tag :back_url, edit_custom_table_path(@custom_field.custom_table) %> + <%= hidden_field_tag :back_url, params[:back_url] %> <%= hidden_field_tag :tab, @tab || params[:tab] %> <%= render partial: 'form', locals: {f: f} %> <% end %> \ No newline at end of file diff --git a/app/views/table_fields/new.html.erb b/app/views/table_fields/new.html.erb index b7199cc..d08d20d 100644 --- a/app/views/table_fields/new.html.erb +++ b/app/views/table_fields/new.html.erb @@ -1,4 +1,3 @@ - <%= labelled_form_for :custom_field, @custom_field, url: table_fields_path, html: {id: 'custom_field_form'} do |f| %> <%= render partial: 'form', locals: {f: f} %> <%= hidden_field_tag :type, @custom_field.type %> diff --git a/db/migrate/20190820221144_add_tracker_ids_to_custom_table.rb b/db/migrate/20190820221144_add_tracker_ids_to_custom_table.rb new file mode 100644 index 0000000..d743ca0 --- /dev/null +++ b/db/migrate/20190820221144_add_tracker_ids_to_custom_table.rb @@ -0,0 +1,16 @@ +class AddTrackerIdsToCustomTable < ActiveRecord::Migration[5.2] + def change + create_table :custom_tables_trackers, id: false do |t| + t.belongs_to :custom_table + t.belongs_to :tracker + end + + create_table :custom_tables_roles, id: false do |t| + t.belongs_to :custom_table + t.belongs_to :role + end + + add_column :custom_tables, :description, :text + add_column :custom_tables, :visible, :boolean, null: false, default: true + end +end diff --git a/lib/custom_tables/patches/project_patch.rb b/lib/custom_tables/patches/project_patch.rb index 9cc85ca..204f0d7 100644 --- a/lib/custom_tables/patches/project_patch.rb +++ b/lib/custom_tables/patches/project_patch.rb @@ -8,10 +8,13 @@ module CustomTables # Returns a scope of all custom tables enabled for project issues # (explicitly associated custom tables and custom tables enabled for all projects) - def all_issue_custom_tables - CustomTable. + def all_issue_custom_tables(issue) + @custom_tables ||= CustomTable. + joins(:trackers). + visible. sorted. - where("is_for_all = ? OR id IN (?)", true, custom_table_ids) + where("custom_tables.is_for_all = ? OR custom_tables.id IN (?)", true, custom_table_ids). + where(trackers: {id: issue.tracker}) end end diff --git a/spec/factories/factories.rb b/spec/factories/factories.rb index 7ede074..6955749 100644 --- a/spec/factories/factories.rb +++ b/spec/factories/factories.rb @@ -98,7 +98,53 @@ FactoryBot.define do tracker { project.trackers.first } start_date { Date.today } due_date { Date.today + 7.days } + priority { IssuePriority.default || FactoryBot.create(:issue_priority, :default) } association :status, factory: :issue_status association :author, :factory => :user, :firstname => "Author" end + + factory :enumeration do + name { 'Test' } + + trait :default do + name { 'Default' } + is_default { true } + end + end + + factory :role do + sequence(:name){ |n| "Role ##{n}" } + permissions { Role.new.setable_permissions.collect(&:name).uniq } + end + + factory :issue_priority, parent: :enumeration, class: 'IssuePriority' do + sequence(:name){ |n| "Priority ##{n}" } + end + + factory :member_role do + role + member + end + + factory :member do + project + user + roles { [] } + + after :build do |member, evaluator| + if evaluator.roles.empty? + member.member_roles << FactoryBot.build(:member_role, member: member) + else + evaluator.roles.each do |role| + member.member_roles << FactoryBot.build(:member_role, member: member, role: role) + end + end + end + + trait :without_roles do + after :create do |member, evaluator| + member.member_roles.clear + end + end + end end diff --git a/spec/models/custom_table_spec.rb b/spec/models/custom_table_spec.rb new file mode 100644 index 0000000..07cc41f --- /dev/null +++ b/spec/models/custom_table_spec.rb @@ -0,0 +1,24 @@ +require "spec_helper" + +describe CustomTable, type: :model do + + let(:role) { FactoryBot.create(:role) } + let(:member) { FactoryBot.create(:member) } + let(:user) { FactoryBot.create(:user, memberships: [member]) } + let(:custom_table) { FactoryBot.create(:custom_table, visible: false, roles: user.roles) } + + describe 'visible' do + + it 'when table is visible' do + expect(CustomTable.visible(user)).to include custom_table + end + + context 'should be invisible' do + let(:user) { FactoryBot.create(:user) } + it 'user is not member' do + expect(CustomTable.visible(user)).not_to include custom_table + end + end + + end +end \ No newline at end of file diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb new file mode 100644 index 0000000..cb33e01 --- /dev/null +++ b/spec/models/project_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" + +describe Project, type: :model do + + let(:tracker) { FactoryBot.create(:tracker) } + let(:project) { FactoryBot.create(:project, trackers: [tracker]) } + let(:issue) { FactoryBot.create(:issue, project: project, tracker: tracker) } + let(:custom_table) { FactoryBot.create(:custom_table, projects: [project], trackers: [tracker]) } + + + it '#all_issue_custom_tables' do + custom_table + expect(project.all_issue_custom_tables(issue)). + to include custom_table + end +end \ No newline at end of file diff --git a/spec/views/custom_tables/new.html.erb_spec.rb b/spec/views/custom_tables/new.html.erb_spec.rb new file mode 100644 index 0000000..ee3d34c --- /dev/null +++ b/spec/views/custom_tables/new.html.erb_spec.rb @@ -0,0 +1,33 @@ +require "spec_helper" + +RSpec.describe "custom_tables/new" do + include_context 'logged as admin' + + let(:tracker) { FactoryBot.create(:tracker) } + let(:role) { FactoryBot.create(:role) } + let(:custom_table) { FactoryBot.create(:custom_table, description: 'Table desc', trackers: [tracker], roles: [role]) } + + before(:each) do + @custom_table = assign(:custom_table, custom_table) + end + + it "It displays description" do + assign(:custom_table, custom_table) + render + expect(rendered).to match /Table desc/ + end + + it "It displays trackers" do + assign(:custom_table, custom_table) + render + expect(rendered).to match /#{custom_table.trackers.first.name}/ + end + + it "It displays roles" do + assign(:custom_table, custom_table) + render + expect(rendered).to match /#{custom_table.roles.first.name}/ + end + + +end \ No newline at end of file diff --git a/spec/views/issues/_custom_tables.html.erb_spec.rb b/spec/views/issues/_custom_tables.html.erb_spec.rb index 9e10104..1d18455 100644 --- a/spec/views/issues/_custom_tables.html.erb_spec.rb +++ b/spec/views/issues/_custom_tables.html.erb_spec.rb @@ -3,7 +3,8 @@ require "spec_helper" RSpec.describe "issues/_custom_tables" do include_context 'logged as' - let(:issue) { FactoryBot.create(:issue) } + let(:tracker) { FactoryBot.create(:tracker) } + let(:issue) { FactoryBot.create(:issue, tracker: tracker) } let(:custom_table) { FactoryBot.create(:custom_table) } before(:each) do @@ -19,7 +20,6 @@ RSpec.describe "issues/_custom_tables" do end context 'with permission' do - let(:issue) { FactoryBot.create(:issue) } before(:each) do allow_any_instance_of(User). diff --git a/spec/views/issues/_query_custom_table.html.erb_spec.rb b/spec/views/issues/_query_custom_table.html.erb_spec.rb deleted file mode 100644 index 79a3d5a..0000000 --- a/spec/views/issues/_query_custom_table.html.erb_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require "spec_helper" - -RSpec.describe "issues/_query_custom_table" do - include_context 'logged as admin' - helper(QueriesHelper) - - let(:issue) { FactoryBot.create(:issue) } - let(:custom_table) { FactoryBot.create(:custom_table) } - - - it "Displays no data" do - render partial: "issues/query_custom_table", locals: { - query: custom_table.query, - custom_table: custom_table, - back_url: issue_path(issue), - entities: custom_table.query.results_scope } - - expect(rendered).to match /No data to display/ - end - -end \ No newline at end of file
<%=l(:field_name)%>
<%= link_to custom_field.name, edit_table_field_path(custom_field, custom_table_id: @custom_table.id), remote: true %><%= link_to custom_field.name, edit_table_field_path(custom_field, custom_table_id: @custom_table.id, back_url: back_url), remote: true %> <%= l(custom_field.format.label) %> <%= checked_image custom_field.is_required? %> <%= f.radio_button :main_custom_field_id, custom_field.id %> <%= reorder_handle(custom_field, url: custom_field_path(custom_field), param: 'custom_field') %> - <%= link_to l(:button_edit), edit_table_field_path(id: custom_field), remote: true, title: l(:button_edit), class: 'icon-only icon-edit' %> - <%= delete_link table_field_path(custom_field, custom_table_id: @custom_table.id), class: 'icon-only icon-del' %> + <%= link_to l(:button_edit), edit_table_field_path(id: custom_field, back_url: back_url), remote: true, title: l(:button_edit), class: 'icon-only icon-edit' %> + <%= delete_link table_field_path(custom_field, custom_table_id: @custom_table.id, back_url: back_url), class: 'icon-only icon-del' %>