diff --git a/app/controllers/checklists_controller.rb b/app/controllers/checklists_controller.rb index 66589ce..c25e9cd 100644 --- a/app/controllers/checklists_controller.rb +++ b/app/controllers/checklists_controller.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -54,6 +54,7 @@ def create respond_to do |format| format.api { if @checklist_item.save + recalculate_issue_ratio(@checklist_item) render :action => 'show', :status => :created, :location => checklist_url(@checklist_item) else render_validation_errors(@checklist_item) @@ -66,7 +67,8 @@ def update @checklist_item.safe_attributes = params[:checklist] respond_to do |format| format.api { - if @checklist_item.save + if with_issue_journal { @checklist_item.save } + recalculate_issue_ratio(@checklist_item) render_api_ok else render_validation_errors(@checklist_item) @@ -77,12 +79,12 @@ def update def done (render_403; return false) unless User.current.allowed_to?(:done_checklists, @checklist_item.issue.project) - @checklist_item.is_done = params[:is_done] == 'true' - if @checklist_item.save - if (Setting.issue_done_ratio == "issue_field") && RedmineChecklists.settings["issue_done_ratio"].to_i > 0 - Checklist.recalc_issue_done_ratio(@checklist_item.issue.id) - @checklist_item.issue.reload + with_issue_journal do + @checklist_item.is_done = params[:is_done] == 'true' + if @checklist_item.save + recalculate_issue_ratio(@checklist_item) + true end end respond_to do |format| @@ -106,4 +108,15 @@ def find_checklist_item rescue ActiveRecord::RecordNotFound render_404 end + + def with_issue_journal(&block) + return unless yield + end + + def recalculate_issue_ratio(checklist_item) + if (Setting.issue_done_ratio == 'issue_field') && RedmineChecklists.issue_done_ratio? + Checklist.recalc_issue_done_ratio(checklist_item.issue.id) + checklist_item.issue.reload + end + end end diff --git a/app/helpers/checklists_helper.rb b/app/helpers/checklists_helper.rb index 031af3e..e939ba8 100644 --- a/app/helpers/checklists_helper.rb +++ b/app/helpers/checklists_helper.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/app/models/checklist.rb b/app/models/checklist.rb index 763a2d2..770b20e 100644 --- a/app/models/checklist.rb +++ b/app/models/checklist.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -53,14 +53,14 @@ class Checklist < ActiveRecord::Base rcrm_acts_as_list validates_presence_of :subject - validates_length_of :subject, :maximum => 512 + validates_length_of :subject, maximum: 512 validates_presence_of :position validates_numericality_of :position def self.recalc_issue_done_ratio(issue_id) issue = Issue.find(issue_id) - return false if (Setting.issue_done_ratio != "issue_field") || RedmineChecklists.settings["issue_done_ratio"].to_i < 1 || issue.checklists.empty? - done_checklist = issue.checklists.map{|c| c.is_done ? 1 : 0} + return false if (Setting.issue_done_ratio != 'issue_field') || !RedmineChecklists.issue_done_ratio? || issue.checklists.reject(&:is_section).empty? + done_checklist = issue.checklists.reject(&:is_section).map { |c| c.is_done ? 1 : 0 } done_ratio = (done_checklist.count(1) * 10) / done_checklist.count * 10 issue.update_attribute(:done_ratio, done_ratio) end @@ -70,7 +70,7 @@ def self.old_format?(detail) (detail.value.is_a?(String) && detail.value.match(/^\[[ |x]\] .+$/).present?) end - safe_attributes 'subject', 'position', 'issue_id', 'is_done' + safe_attributes 'subject', 'position', 'issue_id', 'is_done', 'is_section' def editable_by?(usr = User.current) usr && (usr.allowed_to?(:edit_checklists, project) || (author == usr && usr.allowed_to?(:edit_own_checklists, project))) diff --git a/app/models/journal_checklist_history.rb b/app/models/journal_checklist_history.rb index 27b3bd8..17eeca2 100644 --- a/app/models/journal_checklist_history.rb +++ b/app/models/journal_checklist_history.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -19,27 +19,24 @@ class JournalChecklistHistory def self.can_fixup?(journal_details) - unless journal_details.journal - return false - end + return false if journal_details.journal.nil? + issue = journal_details.journal.journalized - unless issue.is_a?(Issue) - return false - end + return false unless issue.is_a?(Issue) + prev_journal_scope = issue.journals.order('id DESC') prev_journal_scope = prev_journal_scope.where('id <> ?', journal_details.journal_id) if journal_details.journal_id prev_journal = prev_journal_scope.first - unless prev_journal - return false - end + return false unless prev_journal + return false if prev_journal.user_id != journal_details.journal.user_id return false if Time.zone.now > prev_journal.created_on + 1.minute - prev_journal.details.all?{ |x| x.prop_key == 'checklist'} && - journal_details.journal.details.all?{ |x| x.prop_key == 'checklist'} && + prev_journal.details.all? { |x| x.prop_key == 'checklist' } && + journal_details.journal.details.all? { |x| x.prop_key == 'checklist' } && journal_details.journal.notes.blank? && prev_journal.notes.blank? && - prev_journal.details.select{ |x| x.prop_key == 'checklist' }.size == 1 + prev_journal.details.select { |x| x.prop_key == 'checklist' }.size == 1 end def self.fixup(journal_details) diff --git a/app/views/checklists/_checklist_item.html.erb b/app/views/checklists/_checklist_item.html.erb index 19d094a..0855cdb 100644 --- a/app/views/checklists/_checklist_item.html.erb +++ b/app/views/checklists/_checklist_item.html.erb @@ -1,8 +1,11 @@ -
  • > - <%= check_box_tag 'checklist_item', "", checklist_item.is_done, - :disabled => !User.current.allowed_to?(:done_checklists, checklist_item.issue.project) && !User.current.allowed_to?(:edit_checklists, checklist_item.issue.project), - :data_url => url_for( {:controller => "checklists", :action => "done", :id => checklist_item.id} ), :class => 'checklist-checkbox' - %> +
  • + <% unless checklist_item.is_section %> + <%= check_box_tag 'checklist_item', '', checklist_item.is_done, + disabled: !User.current.allowed_to?(:done_checklists, checklist_item.issue.project) && !User.current.allowed_to?(:edit_checklists, checklist_item.issue.project), + data_url: url_for({ controller: 'checklists', action: 'done', id: checklist_item.id }), + class: 'checklist-checkbox', + id: "checklist-checkbox-#{checklist_item.id}" + %> + <% end %> <%= textilizable(checklist_item, :subject).gsub(/<\/?(p|h\d+|li|ul)>/, '').strip.html_safe %> -
  • diff --git a/app/views/checklists/done.js.erb b/app/views/checklists/done.js.erb index 9fc91bd..baf98be 100644 --- a/app/views/checklists/done.js.erb +++ b/app/views/checklists/done.js.erb @@ -2,3 +2,16 @@ $("#checklist_item_<%= @checklist_item.id %>").toggleClass('is-done-checklist-it $('#checklist_form .checklist-item#<%= @checklist_item.id %> input[type=checkbox]').trigger('click'); $('.issue .attributes table.progress').parent().html('<%= j(progress_bar(@checklist_item.issue.done_ratio, :width => '80px', :legend => "#{@checklist_item.issue.done_ratio}%")) %>'); $('#issue_done_ratio').val('<%= @checklist_item.issue.done_ratio %>'); + +var checkedCheckboxes = $('.checklist-checkbox:checkbox:checked'); + +if(localStorage.getItem("hide_closed_checklists") === '0' && checkedCheckboxes.length > 0){ + $("#checklist_item_<%= @checklist_item.id %>").fadeOut(1000).hide(15); + $('#switch_link').text('<%= l("label_checklist_show_closed") %>' + '(' + checkedCheckboxes.length + ')'); +} +if(checkedCheckboxes.length < 1 && localStorage.getItem("hide_closed_checklists") === '1'){ + $('#switch_link').hide(); +} +if(checkedCheckboxes.length > 0){ + $('#switch_link').show(); +} diff --git a/app/views/issues/_checklist.html.erb b/app/views/issues/_checklist.html.erb index edf30ee..b2abf2b 100644 --- a/app/views/issues/_checklist.html.erb +++ b/app/views/issues/_checklist.html.erb @@ -1,7 +1,9 @@ <% if !@issue.blank? && @issue.checklists.any? && User.current.allowed_to?(:view_checklists, @project) %>
    - +
    + <%= link_to l("label_checklist_hide_closed"), '#', id: 'switch_link' %> +

    <%=l(:label_checklist_plural)%>

    - - + <%= javascript_tag do %> + new Redmine.ChecklistToggle('<%= l("label_checklist_show_closed") %>', '<%= l("label_checklist_hide_closed") %>'); + <% end %> <% end %> diff --git a/app/views/issues/_checklist_fields.html.erb b/app/views/issues/_checklist_fields.html.erb index 92dd220..8cdcb4c 100644 --- a/app/views/issues/_checklist_fields.html.erb +++ b/app/views/issues/_checklist_fields.html.erb @@ -1,21 +1,26 @@ - - <%= f.check_box :is_done %> - - <%= f.object.subject %> + + <% unless f.object.is_section %> + <%= f.check_box :is_done %> + <% end %> + + + <%= textilizable(f.object, :subject).gsub(/<\/?(p|h\d+|li|ul)>/, '').strip.html_safe %> - - <%= text_field_tag nil, f.object.subject, :class => 'edit-box'%> - <%= f.hidden_field :subject, :class => 'checklist-subject-hidden' %> + + + <%= text_field_tag nil, f.object.subject, class: 'edit-box' %> + <%= f.hidden_field :subject, class: 'checklist-subject-hidden' %> - <%= submit_tag l(:button_save), :type => "button", :class => "item item-save small"%> - <% concat l(:button_cancel) - %> - <%= link_to_remove_checklist_fields "", f, - :class => "icon icon-del" %> - <%= f.hidden_field :position, :class => 'checklist-item-position' %> - <%= f.hidden_field :id, :class => 'checklist-item-id' %> - + + <%= submit_tag l(:button_save), type: 'button', class: 'item item-save small' %> + <% concat l(:button_cancel) %> + <%= link_to_remove_checklist_fields "", f, class: 'icon icon-del' %> + + <%= f.hidden_field :position, class: 'checklist-item-position' %> + <%= f.hidden_field :is_section, class: 'checklist-item-is_section' %> + <%= f.hidden_field :id, class: 'checklist-item-id' %> + +
    - diff --git a/assets/javascripts/checklists.js b/assets/javascripts/checklists.js index 1bdb224..d5263a0 100644 --- a/assets/javascripts/checklists.js +++ b/assets/javascripts/checklists.js @@ -110,15 +110,11 @@ Redmine.Checklist = $.klass({ event.returnValue = false }, - addChecklistFields: function(templateDiv) { + addChecklistFields: function() { var new_id = new Date().getTime(); var regexp = new RegExp("new_checklist", "g"); - if (templateDiv) { - appended = $(this.content.replace(regexp, new_id)).insertBefore(templateDiv) - } else { - appended = $(this.content.replace(regexp, new_id)).appendTo(this.root) - } - appended.find('.edit-box').focus() + appended = $(this.content.replace(regexp, new_id)).appendTo(this.root); + appended.find('.edit-box').focus(); }, findSpan: function(event) { @@ -129,13 +125,16 @@ Redmine.Checklist = $.klass({ return elem.prevAll('span.checklist-item.new') }, - transformItem: function(event, elem, valueToSet) { + transformItem: function(event, elem, valueToSet, isSection) { var checklistItem; if (event) { checklistItem = this.findSpan(event) - } else { + } else if (elem) { checklistItem = this.findSpanBefore(elem) + } else { + checklistItem = this.root.find('span.checklist-item.new') } + var val; if (valueToSet) { val = valueToSet @@ -143,11 +142,17 @@ Redmine.Checklist = $.klass({ } else { val = checklistItem.find('input.edit-box').val() } + checklistItem.find('.checklist-subject').text(val) checklistItem.find('.checklist-subject-hidden').val(val) checklistItem.removeClass('edit') checklistItem.removeClass('new') checklistItem.addClass('show') + + if (isSection) { + checklistItem.addClass('checklist-section'); + checklistItem.children('.checklist-item-is_section').val(true); + } }, resetItem: function(item) { @@ -157,12 +162,9 @@ Redmine.Checklist = $.klass({ }, addChecklistItem: function(event) { - this.preventEvent(event) - this.transformItem(event) - if ($('.template-wrapper').length) - this.addChecklistFields($('.template-wrapper')) - else - this.addChecklistFields() + this.preventEvent(event); + this.transformItem(event); + this.addChecklistFields(); }, canSave: function(span) { @@ -193,6 +195,57 @@ Redmine.Checklist = $.klass({ }, this)) }, + onClickAddChecklistItemMenuButton: function() { + $('#checklist-menu .add-checklist-item').on('click', $.proxy(function(event) { + this.preventEvent(event); + var span = $('#checklist_form_items > span.checklist-item.new'); + if (this.canSave(span)) { + this.transformItem(); + this.addChecklistFields(); + this.$plusButtonMenu.hide(); + } + }, this)) + }, + + onClickNewSectionMenuButton: function() { + $('#checklist-menu .add-checklist-section').on('click', $.proxy(function(event) { + this.preventEvent(event); + var span = $('#checklist_form_items > span.checklist-item.new'); + if (this.canSave(span)) { + this.transformItem(null, null, null, true); + this.addChecklistFields(); + this.$plusButtonMenu.hide(); + } + }, this)) + }, + + onMouseEnterLeavePlusButton: function() { + var hideMenuTimer; + var $menu = this.$plusButtonMenu; + + this.root.on('mouseenter', '.save-new-by-button', function() { + var $plusButton = $(this); + var position = $plusButton.position(); + $menu.css('left', (position.left + 'px')); + $menu.css('top', (position.top + $plusButton.height() + 'px')); + $menu.show(); + }); + + this.root.on('mouseleave', '.save-new-by-button', function() { + hideMenuTimer = setTimeout(function() { + $menu.hide(); + }, 500); + }); + + $('#checklist-menu').on('mouseenter', function() { + clearTimeout(hideMenuTimer); + }); + + $('#checklist-menu').on('mouseleave', function() { + $menu.hide(); + }); + }, + onIssueFormSubmitRemoveEmptyChecklistItems: function() { $('body').on('submit', '#issue-form', function(){ $('.checklist-subject-hidden').each(function(i, elem) { @@ -219,7 +272,6 @@ Redmine.Checklist = $.klass({ makeChecklistsSortable: function() { $('#checklist_form_items').sortable({ - revert: true, items: '.checklist-item.show', helper: "clone", stop: function (event, ui) { @@ -269,13 +321,33 @@ Redmine.Checklist = $.klass({ }, onChangeCheckbox: function(){ - this.root.on('change', 'input.checklist-checkbox', $.proxy(function(event){ + this.root.on('change', 'input.checklist-checkbox', $.proxy(function(event) { + this.darkenCompletedSections(); checkbox = $(event.target) url = checkbox.attr('data_url') $.ajax({type: "PUT", url: url, data: { is_done: checkbox.prop('checked') }, dataType: 'script'}) }, this)) }, + darkenCompletedSections: function() { + var isCompletedSection = true; + var reversedChecklistItems = $('#checklist_items li').get().reverse(); + + $(reversedChecklistItems).each(function(index, element) { + var $element = $(element); + if ($element.hasClass('checklist-section')) { + if (isCompletedSection) { + $element.addClass('completed-section') + } else { + $element.removeClass('completed-section') + } + isCompletedSection = true; + } else { + isCompletedSection = isCompletedSection && $element.children('.checklist-checkbox').is(':checked') + } + }) + }, + enableUniquenessValidation: function() { this.root.on('keyup', 'input.edit-box', $.proxy(function(event) { value = $(event.target).val() @@ -307,39 +379,39 @@ Redmine.Checklist = $.klass({ }, assignTemplateSelectedEvent: function() { - var item; - this.root.on('change', '#checklist_template', $.proxy(function(){ - value = $('#checklist_template').val() - selected = $('#checklist_template option[value='+value+']').data('template-items') - items = selected.split(/\n/) - for(i = 0; i 0) { + this.onMouseEnterLeavePlusButton(); + this.onClickAddChecklistItemMenuButton(); + this.assignTemplateSelectedEvent(); + this.onClickNewSectionMenuButton(); + } + } else { + this.darkenCompletedSections() + } + this.onIssueFormSubmitRemoveEmptyChecklistItems() this.onChecklistRemove() this.makeChecklistsSortable() @@ -347,12 +419,59 @@ Redmine.Checklist = $.klass({ this.onCheckboxChanged() this.onChangeCheckbox() this.enableUniquenessValidation() - this.assignTemplateSelectedEvent() - this.clickSelectTemplateLink() } }) $.fn.checklist = function(element){ new Redmine.Checklist(this); -} +}; + +Redmine.ChecklistToggle = $.klass({ + manageToggling: function (t_val) { + var checkedCheckboxes = $('.checklist-checkbox:checkbox:checked'); + + if(localStorage.getItem("hide_closed_checklists") === t_val){ + $($(checkedCheckboxes).closest('li')).hide(); + $(this.switch_link).text(this.show_text + '(' + checkedCheckboxes.length + ')'); + } else { + $($(checkedCheckboxes).closest('li')).show(); + $(this.switch_link).text(this.hide_text); + } + }, + switch_link_click: function(){ + var th = $(this)[0]; + this.switch_link.click(function (e) { + e.preventDefault(); + th.manageToggling("1"); + var setVal = (localStorage.getItem("hide_closed_checklists") === "1") ? "0" : "1"; + localStorage.setItem("hide_closed_checklists", setVal); + }); + }, + hide_switch_link: function(){ + if($('.checklist-checkbox:checkbox:checked').length < 1){ + this.switch_link.hide(); + } + }, + init: function(show_text, hide_text) { + this.show_text = show_text; + this.hide_text = hide_text; + this.switch_link = $('#switch_link'); + this.manageToggling("0"); + this.switch_link_click(); + this.hide_switch_link(); + } +}); + + +$(document).ready(function () { + if (typeof(contextMenuCheckSelectionBox) === 'function') { + var originContextMenuCheckSelectionBox = contextMenuCheckSelectionBox; + contextMenuCheckSelectionBox = function (tr, checked) { + var $td = tr.find('td.checklist_relations'); + var $checklist = $td.find('.checklist').detach(); + originContextMenuCheckSelectionBox(tr, checked); + $checklist.appendTo($td); + }; + } +}); diff --git a/assets/stylesheets/checklists.css b/assets/stylesheets/checklists.css index 030318f..a254bda 100644 --- a/assets/stylesheets/checklists.css +++ b/assets/stylesheets/checklists.css @@ -1,3 +1,7 @@ +#checklist_form_items input[type="checkbox"], +#checklist_items input[type="checkbox"] { + height: initial; +} div#checklist ul { list-style: none; @@ -10,6 +14,8 @@ div#checklist li { margin-left: 10px; } +.checklist-checkbox {height: inherit} + #checklist li:hover a.delete {opacity: 1;} #checklist a.delete {opacity: 0.4;} @@ -74,4 +80,65 @@ span.checklist-item.edit .checklist-show-only { div#checklist ol { display: inline-block; padding-left: 0; -} \ No newline at end of file +} + +.checklist-section { + padding-top: 10px; + font-weight: bold; + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: #eee; +} + +.checklist-item.checklist-section > .checklist-checkbox { display: none; } + +#checklist_items li.checklist-section { + padding-bottom: 5px; + margin-bottom: 5px; +} + +.completed-section { color: #999; } + +.save-new-by-button { cursor: pointer; } + +table.list td.checklist_relations { text-align: left } + +/* ========================================================================= */ +/* Checklist context menu */ +/* ========================================================================= */ + +#checklist-menu ul, #checklist-menu li, #checklist-menu a { + display:block; + margin:0; + padding:0; + border:0; +} + +#checklist-menu { + display: none; + position: absolute; + font-size: 0.9em; +} + +#checklist-menu, #checklist-menu ul { + width: 150px; + border: 1px solid #ccc; + background: white; + list-style: none; + padding: 2px; + border-radius: 2px; +} + +#checklist-menu li { + position: relative; + padding: 1px; + border: 1px solid white; +} + +#checklist-menu a { + text-decoration: none !important; + padding: 2px 0px 2px 20px; +} + +#checklist-menu a:hover { color:#2A5685; } +#checklist-menu li:hover { border:1px solid #628db6; background-color:#eef5fd; border-radius:3px; } diff --git a/config/locales/de.yml b/config/locales/de.yml index 4a90806..7fdb430 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -25,5 +25,12 @@ de: label_checklist_changed_to: zu label_checklist_added: hinzugefügt label_checklist_done: als Erledigt markiert - label_checklist_undone: als Nicht Erledigt markiert - label_checklist_updated: Checklisten-Eintrag editiert \ No newline at end of file + label_checklist_undone: als Nicht erledigt markiert + label_checklist_updated: Checklisten-Eintrag editiert + label_checklist_status: Checklisten-Status + label_checklist_status_done: Erledigt + label_checklist_status_undone: Nicht erledigt + label_checklist_is_default: Standard + field_is_for_tracker: Tracker + label_checklists_must_be_completed: "Alle Checklisten-Einträge eines Tickets müssen vor dem Schließen erledigt werden." + label_checklist_block_issue_closing: "Schließen des Tickets blockieren" diff --git a/config/locales/en.yml b/config/locales/en.yml index 4052f03..b60cd28 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,9 @@ # English strings go here for Rails i18n en: + activerecord: + attributes: + checklists: + subject: Checklist subject label_checklist_plural: Checklist field_checklist: Checklist label_checklist_save_log: Save changes to issue log @@ -15,10 +19,12 @@ en: field_template_items: Template items label_checklist_template: Checklist template label_add_checklists_from_template: Add from template + label_checklists_from_template: From template label_select_template: "-- Select template --" label_checklist_category_not_specified: "-- Not specified --" label_checklists_description: 'Multiple values allowed (one line for each value)' label_checklist_item: Checklist item + label_checklist_section: Checklist section label_checklist_deleted: deleted label_checklist_changed_from: changed from label_checklist_changed_to: to @@ -29,8 +35,10 @@ en: label_checklist_status: Checklist status label_checklist_status_done: Done label_checklist_status_undone: Undone - label_checklist_status: Checklist status label_checklist_is_default: Default field_is_for_tracker: Tracker label_checklists_must_be_completed: All checklists of an issue must be done before closing - label_checklist_block_issue_closing: Block issue closing \ No newline at end of file + label_checklist_block_issue_closing: Block issue closing + label_checklist_show_closed: Show closed + label_checklist_hide_closed: Hide closed + label_checklist_new_section: New section diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6a1ed72..5c1fe44 100755 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2,3 +2,8 @@ fr: label_checklist_plural: Liste de Tâches field_checklist: Tâche + label_checklist_templates: Template de checklists + label_checklist_new_checklist_template: Ajouter un template + field_is_for_tracker: + label_checklist_is_default: Checklist par d faut + field_template_items: Eléments diff --git a/config/locales/it.yml b/config/locales/it.yml new file mode 100644 index 0000000..79dc3d5 --- /dev/null +++ b/config/locales/it.yml @@ -0,0 +1,45 @@ +# encoding: utf-8 +# Italian strings go here for Rails i18n +it: + activerecord: + attributes: + checklists: + subject: Checklist soggetto + label_checklist_plural: Checklist + field_checklist: Checklist + label_checklist_save_log: Salva le modifiche nel log delle segnalazioni + label_checklist_done_ratio: Imposta ratio checklist segnalazioni completate + permission_view_checklists: Visualizza la checklist + permission_done_checklists: Elementi checklist completati + permission_edit_checklists: Modifica elementi checklist + label_checklist_template_category_plural: Categorie di modelli + label_checklist_template_category_new: Nuova categoria + field_checklist_template_category: Categoria + label_checklist_templates: Modelli di checklist + label_checklist_new_checklist_template: Nuovo modello di checklist + field_template_items: Elementi del modello + label_checklist_template: Modello di checklist + label_add_checklists_from_template: Aggiungi dal modello + label_checklists_from_template: Dal modello + label_select_template: "- Seleziona modello -" + label_checklist_category_not_specified: "-- Non specificato --" + label_checklists_description: "Sono consentiti più valori (una riga per ciascun valore)" + label_checklist_item: Elemento della checklist + label_checklist_section: Sezione checklist + label_checklist_deleted: Eliminato + label_checklist_changed_from: Modificato da + label_checklist_changed_to: a + label_checklist_added: aggiunto + label_checklist_done: Impostato su Completato + label_checklist_undone: Impostato su Non completato + label_checklist_updated: Elemento della checklist aggiornato + label_checklist_status: Stato della checklist + label_checklist_status_done: Completato + label_checklist_status_undone: Non completato + label_checklist_is_default: Predefinito + field_is_for_tracker: Tracker + label_checklists_must_be_completed: Tutte le liste di controllo di una segnalazione devono essere eseguite prima della chiusura + label_checklist_block_issue_closing: Blocca segnalazione in chiusura + label_checklist_show_closed: Mostra completati + label_checklist_hide_closed: Nascondi completati + label_checklist_new_section: Nuova sezione diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 7e3d5d6..8c7463f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1,5 +1,9 @@ # encoding: utf-8 ru: + activerecord: + attributes: + checklists: + subject: Заголовок чеклиста label_checklist_plural: Чеклист field_checklist: Чеклист label_checklist_save_log: Сохранять изменения в истории @@ -15,10 +19,12 @@ ru: field_template_items: Элементы шаблона label_checklist_template: Шаблон чеклистов label_add_checklists_from_template: Добавить из шаблона + label_checklists_from_template: Из шаблона label_select_template: "-- Выберите шаблон --" label_checklist_category_not_specified: "-- Без категории --" label_checklists_description: 'Для ввода нескольких значений вводите по одному на строку' label_checklist_item: Пункт чеклиста + label_checklist_section: Раздел чеклиста label_checklist_deleted: удалён label_checklist_changed_from: изменён с label_checklist_changed_to: на @@ -31,3 +37,10 @@ ru: label_checklist_status_undone: Не выполнен label_checklist_is_default: По умолчанию field_is_for_tracker: Трекер + label_checklist_show_closed: Показать закрытые + label_checklist_hide_closed: Скрыть закрытые + label_checklist_new_section: Новая секция + label_checklist_block_issue_closing: Запретить закрытие заявки + label_checklists: Чеклист + label_checklists_must_be_completed: Чтобы заявка закрылась, должны быть выполнены все пункты чеклиста + field_checklists: Чеклист diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml new file mode 100644 index 0000000..2fe53c2 --- /dev/null +++ b/config/locales/zh-TW.yml @@ -0,0 +1,32 @@ +# encoding: utf-8 +# Simplified Chinese strings go here for Rails i18n +# Author: Steven.W +# Based on file: en.yml + +zh-TW: + label_checklist_plural: Checklist + field_checklist: Checklist + label_checklist_save_log: 保存檢查清單變更到問題日誌 + label_checklist_done_ratio: 設置列表完成比率 + permission_view_checklists: 查看Checklist + permission_done_checklists: 完成Checklist + permission_edit_checklists: 編輯Checklist + label_checklist_template_category_plural: 範本類別 + label_checklist_template_category_new: 新類別 + field_checklist_template_category: 類別 + label_checklist_templates: 檢查清單範本 + label_checklist_new_checklist_template: 新檢查清單範本 + field_template_items: 範本項 + label_checklist_template: 檢查清單範本 + label_add_checklists_from_template: 從範本新建 + label_select_template: "-- 選擇範本 --" + label_checklist_category_not_specified: "-- 未指定 --" + label_checklists_description: '允許多重值 (每一行的值)' + label_checklist_item: Checklist item + label_checklist_deleted: 刪除 + label_checklist_changed_from: 改變自 + label_checklist_changed_to: 到 + label_checklist_added: 添加 + label_checklist_done: 設置完成 + label_checklist_undone: 設置未完成 + label_checklist_updated: Checklist item編輯 \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f42c4d5..cc4e570 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/001_create_checklists.rb b/db/migrate/001_create_checklists.rb index f5a56c3..fee2541 100644 --- a/db/migrate/001_create_checklists.rb +++ b/db/migrate/001_create_checklists.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/002_add_time_stamps_to_checklists.rb b/db/migrate/002_add_time_stamps_to_checklists.rb index db3f1a4..f1f0444 100644 --- a/db/migrate/002_add_time_stamps_to_checklists.rb +++ b/db/migrate/002_add_time_stamps_to_checklists.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/003_create_checklist_template_category.rb b/db/migrate/003_create_checklist_template_category.rb index 700930e..3536dff 100644 --- a/db/migrate/003_create_checklist_template_category.rb +++ b/db/migrate/003_create_checklist_template_category.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/004_create_checklist_templates.rb b/db/migrate/004_create_checklist_templates.rb index 89b7a07..56bee49 100644 --- a/db/migrate/004_create_checklist_templates.rb +++ b/db/migrate/004_create_checklist_templates.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/005_modify_checklist_subject_length.rb b/db/migrate/005_modify_checklist_subject_length.rb index 7707d7c..2fec00f 100644 --- a/db/migrate/005_modify_checklist_subject_length.rb +++ b/db/migrate/005_modify_checklist_subject_length.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/db/migrate/006_add_fields_to_checklist_template.rb b/db/migrate/006_add_fields_to_checklist_template.rb index f5a75fa..cfda57f 100644 --- a/db/migrate/006_add_fields_to_checklist_template.rb +++ b/db/migrate/006_add_fields_to_checklist_template.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/redmine_checklist_setting.rb b/db/migrate/007_add_is_section_to_checklists.rb similarity index 78% rename from lib/redmine_checklists/redmine_checklist_setting.rb rename to db/migrate/007_add_is_section_to_checklists.rb index a876100..c86fd1f 100644 --- a/lib/redmine_checklists/redmine_checklist_setting.rb +++ b/db/migrate/007_add_is_section_to_checklists.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -17,10 +17,8 @@ # You should have received a copy of the GNU General Public License # along with redmine_checklists. If not, see . -module RedmineChecklists - class RedmineChecklistSetting - def self.block_issue_closing? - Setting.plugin_redmine_checklists['block_issue_closing'].to_i > 0 - end - end -end +class AddIsSectionToChecklists < (Rails.version < '5.1') ? ActiveRecord::Migration : ActiveRecord::Migration[4.2] + def change + add_column :checklists, :is_section, :boolean, default: false + end +end diff --git a/doc/CHANGELOG b/doc/CHANGELOG index c86364d..d0b9c7c 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,12 +1,57 @@ == Redmine Checklists plugin changelog Redmine Checklists plugin - managing issue checklists plugin for Redmine -Copyright (C) 2011-2018 RedmineUP +Copyright (C) 2011-2020 RedmineUP http://www.redmineup.com/ +== 2020-08-17 v3.1.18 + +* Added italian locale +* Updated zh-tw locale +* Fixed size() method error +* Fixed initial install error +* Fixed checklist ration recalculate +* Fixed display checklist element with Markdown syntax +* Fixed done ratio recalculate on checklist API update +* Fixed API call journalizing + +== 2020-01-31 v3.1.17 + +* Redmine 4.1 compatibility fixes +* Fixed view permission bug +* Fixed template permissions bug +* Fixed context menu conflicts +* Fixed Agile support +* Fixed checklist copy bug +* Fixed locale bug +* Fixed project copy bug + +== 2019-04-29 v3.1.16 + +* Checklists sections + +== 2019-04-15 v3.1.15 + +* Redmine 4.0.3 support +* Added Hide link for closed items + +== 2018-12-20 v3.1.14 + +* Hotfix for Redmine 4 + +== 2018-12-18 v3.1.13 + +* Redmine 4 saving issue fixes + +== 2018-11-26 v3.1.12 + +* German translation update from Tobias Fischer +* Fixed diferent authors changes bug +* Fixed sortable animation bug + == 2018-03-23 v3.1.11 -* Rails 4 support +* Redmine 4 support * Setting for block issues with undone checklists * Fixed bug with default template * Fixed email notification bug @@ -36,10 +81,10 @@ http://www.redmineup.com/ * Redmine 3.4 support * New checklists filters for issues table -* Save log by default +* Save log by default * Chinese translation update * Polish translation update -* Fixed bug with template editing +* Fixed bug with template editing == 2016-08-15 v3.1.5 diff --git a/init.rb b/init.rb index 3f5c659..5cb6116 100644 --- a/init.rb +++ b/init.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ require 'redmine' require 'redmine_checklists/redmine_checklists' -CHECKLISTS_VERSION_NUMBER = '3.1.11'.freeze +CHECKLISTS_VERSION_NUMBER = '3.1.18'.freeze CHECKLISTS_VERSION_TYPE = "Light version" Redmine::Plugin.register :redmine_checklists do @@ -31,7 +31,7 @@ url 'https://www.redmineup.com/pages/plugins/checklists' author_url 'mailto:support@redmineup.com' - requires_redmine :version_or_higher => '2.3' + requires_redmine :version_or_higher => '2.6' settings :default => { :save_log => true, diff --git a/lib/redmine_checklists/hooks/controller_issues_hook.rb b/lib/redmine_checklists/hooks/controller_issues_hook.rb index 26851fc..c6d5a74 100644 --- a/lib/redmine_checklists/hooks/controller_issues_hook.rb +++ b/lib/redmine_checklists/hooks/controller_issues_hook.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ module Hooks class ControllerIssuesHook < Redmine::Hook::ViewListener def controller_issues_edit_after_save(context = {}) - if (Setting.issue_done_ratio == "issue_field") && RedmineChecklists.settings["issue_done_ratio"].to_i > 0 + if (Setting.issue_done_ratio == 'issue_field') && RedmineChecklists.issue_done_ratio? Checklist.recalc_issue_done_ratio(context[:issue].id) end end diff --git a/lib/redmine_checklists/hooks/views_issues_hook.rb b/lib/redmine_checklists/hooks/views_issues_hook.rb index 3923d25..320eb1b 100644 --- a/lib/redmine_checklists/hooks/views_issues_hook.rb +++ b/lib/redmine_checklists/hooks/views_issues_hook.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/hooks/views_layouts_hook.rb b/lib/redmine_checklists/hooks/views_layouts_hook.rb index 7631101..249de23 100644 --- a/lib/redmine_checklists/hooks/views_layouts_hook.rb +++ b/lib/redmine_checklists/hooks/views_layouts_hook.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb b/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb index 750e2d0..26b4976 100644 --- a/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb +++ b/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb b/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb index d7ad48c..909c7f3 100644 --- a/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb +++ b/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb b/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb deleted file mode 100644 index 22a5595..0000000 --- a/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb +++ /dev/null @@ -1,41 +0,0 @@ -# This file is a part of Redmine Checklists (redmine_checklists) plugin, -# issue checklists management plugin for Redmine -# -# Copyright (C) 2011-2018 RedmineUP -# http://www.redmineup.com/ -# -# redmine_checklists 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 3 of the License, or -# (at your option) any later version. -# -# redmine_checklists 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 redmine_checklists. If not, see . - -module RedmineChecklists - module Patches - module ApplicationControllerPatch - def self.included(base) # :nodoc: - base.extend(ClassMethods) - base.class_eval do - unloadable # Send unloadable so it will not be unloaded in development - end - end - - module ClassMethods - def before_action(*filters, &block) - before_filter(*filters, &block) - end - end - end - end -end - -unless ApplicationController.included_modules.include?(RedmineChecklists::Patches::ApplicationControllerPatch) - ApplicationController.send(:include, RedmineChecklists::Patches::ApplicationControllerPatch) -end diff --git a/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb b/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb index a627386..0b2a2e1 100644 --- a/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb +++ b/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/compatibility/journal_patch.rb b/lib/redmine_checklists/patches/compatibility/journal_patch.rb index b5dd698..c6b72de 100644 --- a/lib/redmine_checklists/patches/compatibility/journal_patch.rb +++ b/lib/redmine_checklists/patches/compatibility/journal_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -17,13 +17,11 @@ # You should have received a copy of the GNU General Public License # along with redmine_checklists. If not, see . - require_dependency 'journal' module RedmineChecklists module Patches module JournalPatch - def self.included(base) # :nodoc: base.send(:include, InstanceMethods) base.class_eval do @@ -40,7 +38,7 @@ def send_notification (Setting.notified_events.include?('issue_status_updated') && new_status.present?) || (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?) ) - checklist_email_nootification(self).deliver + deliver_checklist_notification end end @@ -49,7 +47,17 @@ def detail_for_attribute(attribute) end end - def checklist_email_nootification(journal) + def deliver_checklist_notification + if Redmine::VERSION.to_s >= '4.0' + (notified_watchers | notified_users).each do |user| + Mailer.issue_edit(user, self).deliver + end + else + checklist_email_notification(self).deliver + end + end + + def checklist_email_notification(journal) if Redmine::VERSION.to_s < '2.4' Mailer.issue_edit(journal) else @@ -57,7 +65,6 @@ def checklist_email_nootification(journal) end end end - end end end diff --git a/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb b/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb index d0d27e1..3a9e43b 100644 --- a/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb +++ b/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/compatibility_patch.rb b/lib/redmine_checklists/patches/compatibility_patch.rb index 3e1460e..3b824da 100644 --- a/lib/redmine_checklists/patches/compatibility_patch.rb +++ b/lib/redmine_checklists/patches/compatibility_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/issue_patch.rb b/lib/redmine_checklists/patches/issue_patch.rb index ab57df5..fd11daa 100644 --- a/lib/redmine_checklists/patches/issue_patch.rb +++ b/lib/redmine_checklists/patches/issue_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -46,23 +46,21 @@ def self.included(base) # :nodoc: safe_attributes 'checklists_attributes', :if => lambda { |issue, user| (user.allowed_to?(:done_checklists, issue.project) || user.allowed_to?(:edit_checklists, issue.project)) } + end + end - def copy_checklists(arg) - issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg) - issue.checklists.each{ |checklist| Checklist.create(checklist.attributes.except('id', 'issue_id').merge(:issue => self)) } if issue - end - - def block_issue_closing_if_checklists_unclosed - if RedmineChecklistSetting.block_issue_closing? && checklists.any? && status.is_closed? - errors.add(:checklists, l(:label_checklists_must_be_completed)) unless (checklists - checklists.where(:id => removed_checklist_ids)).all?(&:is_done) + module InstanceMethods + def copy_checklists(arg) + issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg) + if issue + issue.checklists.each do |checklist| + Checklist.create(checklist.attributes.except('id', 'issue_id').merge(issue: self)) end end end - end - module InstanceMethods def copy_subtask_checklists - return if !copy? || parent_id.nil? || checklists.any? + return if !copy? || parent_id.nil? || checklists.reload.any? copy_checklists(@copied_from) end @@ -71,6 +69,23 @@ def copy_with_checklist(attributes = nil, copy_options = {}) copy.copy_checklists(self) copy end + + def all_checklist_items_is_done? + (checklists - checklists.where(id: removed_checklist_ids)).reject(&:is_section).all?(&:is_done) + end + + def need_to_block_issue_closing? + RedmineChecklists.block_issue_closing? && + checklists.reject(&:is_section).any? && + status.is_closed? && + !all_checklist_items_is_done? + end + + def block_issue_closing_if_checklists_unclosed + if need_to_block_issue_closing? + errors.add(:checklists, l(:label_checklists_must_be_completed)) + end + end end end end diff --git a/lib/redmine_checklists/patches/issue_query_patch.rb b/lib/redmine_checklists/patches/issue_query_patch.rb index be23e88..3754e00 100644 --- a/lib/redmine_checklists/patches/issue_query_patch.rb +++ b/lib/redmine_checklists/patches/issue_query_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ module InstanceMethods end end -unless IssueQuery.included_modules.include?(RedmineChecklists::Patches::IssueQueryPatch) +if (ActiveRecord::Base.connection.tables.include?('queries') rescue false) && + IssueQuery.included_modules.exclude?(RedmineChecklists::Patches::IssueQueryPatch) IssueQuery.send(:include, RedmineChecklists::Patches::IssueQueryPatch) end diff --git a/lib/redmine_checklists/patches/issues_controller_patch.rb b/lib/redmine_checklists/patches/issues_controller_patch.rb index 7298ac2..f7322cd 100644 --- a/lib/redmine_checklists/patches/issues_controller_patch.rb +++ b/lib/redmine_checklists/patches/issues_controller_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -69,9 +69,12 @@ def fill_checklist_attributes def add_checklists_to_params(checklists) params[:issue].blank? ? params[:issue] = { :checklists_attributes => {} } : params[:issue][:checklists_attributes] = {} checklists.each_with_index do |checklist_item, index| - params[:issue][:checklists_attributes][index.to_s] = { :is_done => checklist_item.is_done, - :subject => checklist_item.subject, - :position => checklist_item.position } + params[:issue][:checklists_attributes][index.to_s] = { + is_done: checklist_item.is_done, + subject: checklist_item.subject, + position: checklist_item.position, + is_section: checklist_item.is_section + } end end end diff --git a/lib/redmine_checklists/patches/issues_helper_patch.rb b/lib/redmine_checklists/patches/issues_helper_patch.rb index 4efeb55..6707f4b 100644 --- a/lib/redmine_checklists/patches/issues_helper_patch.rb +++ b/lib/redmine_checklists/patches/issues_helper_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -28,28 +28,16 @@ def self.included(base) alias_method :details_to_strings_without_checklists, :details_to_strings alias_method :details_to_strings, :details_to_strings_with_checklists - if Redmine::VERSION.to_s >= '2.2' && Redmine::VERSION.to_s <= '2.4' - alias_method :render_email_issue_attributes_without_checklists, :render_email_issue_attributes - alias_method :render_email_issue_attributes, :render_email_issue_attributes_with_checklists - end end end module InstanceMethods - def render_email_issue_attributes_with_checklists(issue, html = false) - journal = issue.journals.order(:id).last - return render_email_issue_attributes_without_checklists(issue, html) unless journal - details = journal.details - return render_email_issue_attributes_without_checklists(issue, html) unless details - checklist_details = details.select{ |x| x.prop_key == 'checklist'} - return render_email_issue_attributes_without_checklists(issue, html) unless checklist_details.any? - return render_email_issue_attributes_without_checklists(issue, html) + details_to_strings_with_checklists(checklist_details, !html).join(html ? "
    ".html_safe : "\n") - end - def details_to_strings_with_checklists(details, no_html = false, options = {}) details_checklist, details_other = details.partition{ |x| x.prop_key == 'checklist' } + return details_to_strings_without_checklists(details_other, no_html, options) unless User.current.allowed_to?(:view_checklists, @issue.project) + details_checklist.map do |detail| result = [] diff = Hash.new([]) @@ -60,15 +48,19 @@ def details_to_strings_with_checklists(details, no_html = false, options = {}) diff = JournalChecklistHistory.new(detail.old_value, detail.value).diff end + checklist_item_label = lambda do |item| + item[:is_section] ? l(:label_checklist_section) : l(:label_checklist_item) + end + if diff[:done].any? diff[:done].each do |item| - result << "#{ERB::Util.h l(:label_checklist_item)} #{ERB::Util.h item[:subject]} #{ERB::Util.h l(:label_checklist_done)}" + result << "#{ERB::Util.h l(:label_checklist_item)} #{ERB::Util.h item[:subject]} #{ERB::Util.h l(:label_checklist_done)}" end end if diff[:undone].any? diff[:undone].each do |item| - result << "#{ERB::Util.h l(:label_checklist_item)} #{ERB::Util.h item[:subject]} #{ERB::Util.h l(:label_checklist_undone)}" + result << "#{ERB::Util.h l(:label_checklist_item)} #{ERB::Util.h item[:subject]} #{ERB::Util.h l(:label_checklist_undone)}" end end @@ -76,8 +68,8 @@ def details_to_strings_with_checklists(details, no_html = false, options = {}) result = nil if result.blank? if result && no_html result = result.gsub /<\/li>
  • /, "\n" - result = result.gsub /]*checked[^>]*>/, '[x]' - result = result.gsub /]*>/, '[ ]' + result = result.gsub /]*checked[^>]*>/, '[x]' + result = result.gsub /]*>/, '[ ]' result = result.gsub /<[^>]*>/, '' result = CGI.unescapeHTML(result) end diff --git a/lib/redmine_checklists/patches/notifiable_patch.rb b/lib/redmine_checklists/patches/notifiable_patch.rb index 0d353bd..13f4e25 100644 --- a/lib/redmine_checklists/patches/notifiable_patch.rb +++ b/lib/redmine_checklists/patches/notifiable_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/lib/redmine_checklists/patches/project_patch.rb b/lib/redmine_checklists/patches/project_patch.rb index 944fc23..e8fbbc2 100644 --- a/lib/redmine_checklists/patches/project_patch.rb +++ b/lib/redmine_checklists/patches/project_patch.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -35,7 +35,7 @@ module InstanceMethods def copy_issues_with_checklist(project) copy_issues_without_checklist(project) - issues.each{ |issue| issue.copy_checklists(issue.copied_from)} + issues.each{ |issue| issue.copy_checklists(issue.copied_from) if issue.reload.checklists.empty? } end end end diff --git a/lib/redmine_checklists/redmine_checklists.rb b/lib/redmine_checklists/redmine_checklists.rb index fb69b88..d157c06 100644 --- a/lib/redmine_checklists/redmine_checklists.rb +++ b/lib/redmine_checklists/redmine_checklists.rb @@ -1,7 +1,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -19,7 +19,6 @@ Rails.configuration.to_prepare do require 'redmine_checklists/patches/compatibility/application_helper_patch' - require 'redmine_checklists/patches/compatibility/application_controller_patch' if Rails::VERSION::MAJOR < 4 require 'redmine_checklists/hooks/views_issues_hook' require 'redmine_checklists/hooks/views_layouts_hook' @@ -36,5 +35,13 @@ end module RedmineChecklists - def self.settings() Setting[:plugin_redmine_checklists].blank? ? {} : Setting[:plugin_redmine_checklists] end + def self.settings() Setting.plugin_redmine_checklists.blank? ? {} : Setting.plugin_redmine_checklists end + + def self.block_issue_closing? + settings['block_issue_closing'].to_i > 0 + end + + def self.issue_done_ratio? + settings['issue_done_ratio'].to_i > 0 + end end diff --git a/scripts/run_local.sh b/scripts/run_local.sh deleted file mode 100755 index dc77740..0000000 --- a/scripts/run_local.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -RUBY=$1 -DB=$2 -REDMINE=$3 - -docker run -t -i -v `pwd`:/var/www/$RUBY/$DB/$REDMINE/plugins/redmine_checklists \ - --env RUBY=$1 \ - --env DB=$2 \ - --env REDMINE=$3 \ - --env PLUGIN=redmine_checklists \ - redmineup/redmine_checklists \ - /root/run_local.sh diff --git a/test/fixtures/checklists.yml b/test/fixtures/checklists.yml index 573b24a..399383b 100644 --- a/test/fixtures/checklists.yml +++ b/test/fixtures/checklists.yml @@ -1,14 +1,25 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html +--- +# === Checklist for Issue(1) === one: id: 1 is_done: false subject: First todo issue_id: 1 + two: id: 2 is_done: true subject: Second todo issue_id: 1 + +# === Checklist for Issue(2) === +section_one: + id: 4 + is_done: false + subject: New section + is_section: true + issue_id: 2 + three: id: 3 is_done: true diff --git a/test/functional/checklists_controller_test.rb b/test/functional/checklists_controller_test.rb index 3ac9bed..a887ecb 100644 --- a/test/functional/checklists_controller_test.rb +++ b/test/functional/checklists_controller_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 0cd30c4..28d08f9 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -53,6 +53,7 @@ class IssuesControllerTest < ActionController::TestCase def setup @request.session[:user_id] = 1 + RedmineChecklists::TestCase.prepare end def test_new_issue_without_project @@ -159,6 +160,24 @@ def test_create_issue_without_checklists assert_not_nil issue end + def test_create_issue_with_checklists + @request.session[:user_id] = 1 + assert_difference 'Issue.count' do + compatible_request :post, :create, :project_id => 1, :issue => { :tracker_id => 3, + :status_id => 2, + :subject => 'NEW issue with checklists', + :description => 'This is the description', + :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'item 001', 'position' => '1' } } + } + end + assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id + + issue = Issue.find_by_subject('NEW issue with checklists') + assert_equal 1, issue.checklists.count + assert_equal 'item 001', issue.checklists.last.subject + assert_not_nil issue + end + def test_create_issue_using_json old_value = Setting.rest_api_enabled Setting.rest_api_enabled = '1' @@ -180,4 +199,28 @@ def test_create_issue_using_json ensure Setting.rest_api_enabled = old_value end + + def test_history_displaying_for_checklist + @request.session[:user_id] = 1 + Setting[:plugin_redmine_checklists] = { save_log: 1, issue_done_ratio: 0 } + + issue = Issue.find(1) + journal = issue.journals.create!(user_id: 1) + journal.details.create!(:property => 'attr', + :prop_key => 'checklist', + :old_value => '[ ] TEST', + :value => '[x] TEST') + + # With permissions + @request.session[:user_id] = 1 + compatible_request :get, :show, id: issue.id + assert_response :success + assert_include 'changed from [ ] TEST to [x] TEST', response.body + + # Without permissions + @request.session[:user_id] = 5 + compatible_request :get, :show, id: issue.id + assert_response :success + assert_not_include 'changed from [ ] TEST to [x] TEST', response.body + end end diff --git a/test/integration/api_test/checklists_test.rb b/test/integration/api_test/checklists_test.rb index 613b7b5..e5d3eb0 100644 --- a/test/integration/api_test/checklists_test.rb +++ b/test/integration/api_test/checklists_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -89,7 +89,7 @@ def test_post_checklists_xml end def test_put_checklists_1_xml - parameters = { :checklist => { :subject => 'Item_UPDATED' } } + parameters = { :checklist => { subject: 'Item_UPDATED', is_done: '1' } } assert_no_difference('Checklist.count') do compatible_api_request :put, '/checklists/1.xml', parameters, credentials('admin') @@ -104,7 +104,7 @@ def test_delete_1_xml compatible_api_request :delete, '/checklists/1.xml', {}, credentials('admin') end - assert_response :ok + assert ['200', '204'].include?(response.code) assert_equal '', @response.body assert_nil Checklist.find_by_id(1) end diff --git a/test/integration/common_issue_test.rb b/test/integration/common_issue_test.rb index 8321387..2324f45 100644 --- a/test/integration/common_issue_test.rb +++ b/test/integration/common_issue_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/test/test_helper.rb b/test/test_helper.rb index c457c3c..683ef93 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -71,6 +71,11 @@ def self.create_fixtures(fixtures_directory, table_names, class_names = {}) end def self.prepare + Role.find([1,2]).each do |r| # For anonymous + r.permissions << :view_checklists + r.save + end + Role.find(1, 2, 3, 4).each do |r| r.permissions << :edit_checklists r.save diff --git a/test/unit/checklist_test.rb b/test/unit/checklist_test.rb index 75d7c92..2360079 100644 --- a/test/unit/checklist_test.rb +++ b/test/unit/checklist_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify @@ -86,4 +86,53 @@ def setup assert_equal "[x] #{@checklist_1.subject}", @checklist_1.info, "Helper info broken" end + def test_should_correct_recalculate_rate + issues = [ + [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #1", done_ratio: 0, + checklists_attributes: { + '0' => { subject: 'item 1', is_done: false }, + '1' => { subject: 'item 2', is_done: false }, + '2' => { subject: 'item 3', is_done: true }, + }), + 30], + [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #2", done_ratio: 0, + checklists_attributes: { + '0' => { subject: 'item 1', is_done: false }, + '1' => { subject: 'item 2', is_done: true }, + '2' => { subject: 'item 3', is_done: true }, + }), + 60], + [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #3", done_ratio: 0, + checklists_attributes: { + '0' => { subject: 'item 1', is_done: true }, + '1' => { subject: 'item 2', is_done: true }, + '2' => { subject: 'item 3', is_done: true }, + }), + 100], + [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #4", done_ratio: 0, + checklists_attributes: { + '0' => { subject: 'item 1', is_done: false }, + '1' => { subject: 'item 2', is_done: false }, + '2' => { subject: 'section 1', is_done: true, is_section: true }, + '3' => { subject: 'item 3', is_done: true }, + }), + 30], + [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #5", done_ratio: 0, + checklists_attributes: { + '0' => { subject: 'section 1', is_done: true, is_section: true } + }), + 0] + ] + + with_checklists_settings('issue_done_ratio' => '1') do + issues.each do |issue, after_ratio| + assert_equal 0, issue.done_ratio + Checklist.recalc_issue_done_ratio(issue.id) + issue.reload + assert_equal after_ratio, issue.done_ratio + end + end + ensure + issues.each { |issue, ratio| issue.destroy } + end end diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index cbf31d8..24ff68f 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb index baf52b1..621af31 100644 --- a/test/unit/project_test.rb +++ b/test/unit/project_test.rb @@ -3,7 +3,7 @@ # This file is a part of Redmine Checklists (redmine_checklists) plugin, # issue checklists management plugin for Redmine # -# Copyright (C) 2011-2018 RedmineUP +# Copyright (C) 2011-2020 RedmineUP # http://www.redmineup.com/ # # redmine_checklists is free software: you can redistribute it and/or modify