From 8d81c70b315cba9323868ba47d083c659f6e8d6b Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 11:03:41 +0900 Subject: [PATCH 01/16] update redmine versions in the github workflows --- .github/workflows/redmine_plugin.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index 97a1f75..ebc95e8 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -11,13 +11,15 @@ jobs: fail-fast: false matrix: mat_env: + - '3.3 redmica/redmica@v3.0.1' + - '3.2 redmica/redmica@v2.4.2' - '3.2 redmica/redmica@v2.3.2' - '3.1 redmica/redmica@v2.2.3' - '3.0 redmica/redmica@v2.2.3' - '2.7 redmica/redmica@v2.2.3' - - '3.2 redmine/redmine@5.1.0' - - '3.1 redmine/redmine@5.0.6' - - '3.0 redmine/redmine@5.0.6' + - '3.2 redmine/redmine@5.1.3' + - '3.1 redmine/redmine@5.0.9' + - '3.0 redmine/redmine@5.0.9' - '2.7 redmine/redmine@4.2.11' - '2.6 redmine/redmine@4.2.11' - '2.6 redmine/redmine@4.1.7' @@ -25,7 +27,7 @@ jobs: - '2.5 redmine/redmine@4.0.9' experimental: [false] include: - - mat_env: '3.2 redmica/redmica@master' + - mat_env: '3.3 redmica/redmica@master' experimental: true steps: - run: | From fd0f1439d1f5b5bfe2478a1367995a0c53f2bde9 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 11:14:24 +0900 Subject: [PATCH 02/16] refactor .github/workflows/redmine_plugin.yml --- .github/workflows/redmine_plugin.yml | 35 ++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index ebc95e8..50b77cb 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -83,7 +83,6 @@ jobs: - name: Before script run: | - cd ./redmine bundle config --local path ${BUNDLE_PATH:-vendor/bundle} bundle install @@ -96,31 +95,27 @@ jobs: # Execute plugin's migration bundle exec rake redmine:plugins NAME=${{ env.PLUGIN_NAME }} + working-directory: ./redmine env: REDMINE_LANG: en RAILS_ENV: test # HACK sometimes fails login error, so retry 10 times - # HACK use shell script file to avoid interruption by error - - name: Prepare test script - uses: DamianReeves/write-file-action@v1.2 - with: - path: ./run_test.sh - contents: | - #!/bin/sh - cd ./redmine - for i in `seq 0 9`; do - # Execute plugin's test - bundle exec rake redmine:plugins:test NAME=${{ env.PLUGIN_NAME }} - retval=$? - if [ "$retval" -eq 0 ]; then - break - fi - echo "${i}-th test failed; Retry test..." - done - exit $retval - name: Execute test - run: sh ./run_test.sh + run: | + #!/bin/sh + for i in `seq 0 9`; do + # Execute plugin's test + bundle exec rake redmine:plugins:test NAME=${{ env.PLUGIN_NAME }} + retval=$? + if [ "$retval" -eq 0 ]; then + break + fi + echo "${i}-th test failed; Retry test..." + done + exit $retval + shell: bash {0} + working-directory: ./redmine env: REDMINE_LANG: en RAILS_ENV: test From 53e8e3af691345e233adb4f5a4e2ddf91667adf8 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 00:57:38 +0900 Subject: [PATCH 03/16] Ignore `unloadable` if method missing --- app/controllers/hearts_controller.rb | 2 +- app/models/heart.rb | 2 +- lib/redmine_hearts/heartable_patch.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/hearts_controller.rb b/app/controllers/hearts_controller.rb index 6dcc48c..d1a8613 100644 --- a/app/controllers/hearts_controller.rb +++ b/app/controllers/hearts_controller.rb @@ -18,7 +18,7 @@ # along with this program; if not, write to the Free Software class HeartsController < ApplicationController - unloadable + unloadable if respond_to?(:unloadable) accept_api_auth :index, :heart, :unheart, :hearted_users diff --git a/app/models/heart.rb b/app/models/heart.rb index a5a7c41..67aac09 100644 --- a/app/models/heart.rb +++ b/app/models/heart.rb @@ -19,7 +19,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class Heart < ActiveRecord::Base - unloadable + unloadable if respond_to?(:unloadable) belongs_to :heartable, :polymorphic => true belongs_to :user diff --git a/lib/redmine_hearts/heartable_patch.rb b/lib/redmine_hearts/heartable_patch.rb index 65a16dc..7585b8a 100644 --- a/lib/redmine_hearts/heartable_patch.rb +++ b/lib/redmine_hearts/heartable_patch.rb @@ -34,7 +34,7 @@ def self.included(base) # :nodoc: end base.class_eval do - unloadable + unloadable if respond_to?(:unloadable) acts_as_heartable options end end From 59f151e9297999b7827399a3fa6413d2fe5386f5 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 09:49:57 +0900 Subject: [PATCH 04/16] Use ApplicationRecord instead of ActiveRecord::Base to follow https://www.redmine.org/issues/38975 --- app/models/heart.rb | 6 +++--- lib/redmine/acts/heartable.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/heart.rb b/app/models/heart.rb index 67aac09..c811f80 100644 --- a/app/models/heart.rb +++ b/app/models/heart.rb @@ -18,7 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -class Heart < ActiveRecord::Base +class Heart < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base) unloadable if respond_to?(:unloadable) belongs_to :heartable, :polymorphic => true @@ -33,7 +33,7 @@ def self.of_projects(*args) user = args.size > 0 ? args.shift : nil raise ArgumentError if args.size > 0 - ActiveRecord::Base.subclasses.select { |klass| + (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base).subclasses.select { |klass| klass.included_modules.include?(Redmine::Acts::Heartable::InstanceMethods) }.map { |klass| if user && klass.respond_to?(:visible) @@ -57,7 +57,7 @@ def self.of_projects(*args) def self.notifications_to(user) raise ArgumentError unless user - ActiveRecord::Base.subclasses.select { |klass| + (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base).subclasses.select { |klass| klass.included_modules.include?(Redmine::Acts::Heartable::InstanceMethods) }.select { |klass| klass.column_names.include?("author_id") || klass.column_names.include?("user_id") diff --git a/lib/redmine/acts/heartable.rb b/lib/redmine/acts/heartable.rb index d58f576..69d17a8 100644 --- a/lib/redmine/acts/heartable.rb +++ b/lib/redmine/acts/heartable.rb @@ -98,5 +98,5 @@ module ClassMethods; end end end -ActiveRecord::Base.send(:include, Redmine::Acts::Heartable) +(defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : ActiveRecord::Base).send(:include, Redmine::Acts::Heartable) From a978fbe9817943c2283809687a8349c501dc0967 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 10:56:23 +0900 Subject: [PATCH 05/16] use :assigns instead of @-starting keys in :locals in render_to_string --- lib/redmine_hearts/view_hook.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/redmine_hearts/view_hook.rb b/lib/redmine_hearts/view_hook.rb index 23f204a..0b66cb9 100644 --- a/lib/redmine_hearts/view_hook.rb +++ b/lib/redmine_hearts/view_hook.rb @@ -25,7 +25,8 @@ def view_layouts_base_html_head(context={}) if subject controller.send(:render_to_string, { :partial => "hooks/redmine_hearts/view_layouts_base_html_head", - :locals => context.merge(:@heartable => subject) + :locals => context, + :assigns => { :heartable => subject } }) end end @@ -36,7 +37,8 @@ def view_layouts_base_content(context={}) if subject controller.send(:render_to_string, { :partial => "hooks/redmine_hearts/view_layouts_base_content", - :locals => context.merge(:@heartable => subject) + :locals => context, + :assigns => { :heartable => subject } }) end end @@ -47,7 +49,8 @@ def view_account_left_bottom(context={}) controller = context[:controller] controller.send(:render_to_string, { :partial => "hooks/redmine_hearts/view_account_left_bottom", - :locals => context.merge(:@heart_count => heart_count) + :locals => context, + :assigns => { :heart_count => heart_count } }) end end From 4b299a07ce0cec9f16109367a1cbe329cec49b43 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 12:27:02 +0900 Subject: [PATCH 06/16] remove DateTime#to_s(:db) and use DateTime as-is Changed because DateTime#to_s(:db) is no longer supported in Rails7 --- test/fixtures/hearts.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/fixtures/hearts.yml b/test/fixtures/hearts.yml index 29a3b58..af5a0f5 100644 --- a/test/fixtures/hearts.yml +++ b/test/fixtures/hearts.yml @@ -21,29 +21,29 @@ hearts_005: heartable_type: Wiki heartable_id: 1 user_id: 3 - created_at: <%= 30.days.ago.to_date.to_s(:db) %> - updated_at: <%= 30.days.ago.to_date.to_s(:db) %> + created_at: <%= 30.days.ago %> + updated_at: <%= 30.days.ago %> hearts_006: heartable_type: WikiPage heartable_id: 1 user_id: 3 - created_at: <%= 29.days.ago.to_date.to_s(:db) %> - updated_at: <%= 29.days.ago.to_date.to_s(:db) %> + created_at: <%= 29.days.ago %> + updated_at: <%= 29.days.ago %> hearts_007: heartable_type: News heartable_id: 1 user_id: 3 - created_at: <%= 2.days.ago.to_date.to_s(:db) %> - updated_at: <%= 2.days.ago.to_date.to_s(:db) %> + created_at: <%= 2.days.ago %> + updated_at: <%= 2.days.ago %> hearts_008: heartable_type: Journal heartable_id: 1 user_id: 3 - created_at: <%= 1.day.ago.to_date.to_s(:db) %> - updated_at: <%= 1.day.ago.to_date.to_s(:db) %> + created_at: <%= 1.day.ago %> + updated_at: <%= 1.day.ago %> hearts_004: heartable_type: Board heartable_id: 1 user_id: 3 - created_at: <%= 1.minute.ago.to_date.to_s(:db) %> - updated_at: <%= 1.minute.ago.to_date.to_s(:db) %> + created_at: <%= 1.minute.ago %> + updated_at: <%= 1.minute.ago %> From 9ecf6e3ac070e140ab46dcaa8c75eaeb33ff20a1 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 12:11:33 +0900 Subject: [PATCH 07/16] add "LoadError: cannot load such file -- blankslate" Issue HACK workaround for older versions to the github workflow --- .github/workflows/redmine_plugin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index 50b77cb..a7ae690 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -80,6 +80,13 @@ jobs: contents: | gem 'loofah', '~> 2.20.0' if: contains(fromJSON('["2.6 redmine/redmine@4.1.7", "2.6 redmine/redmine@4.0.9", "2.5 redmine/redmine@4.0.9"]'), matrix.mat_env) + - name: "'LoadError: cannot load such file -- blankslate' Issue HACK ref. https://www.redmine.org/issues/40802#note-11" + uses: DamianReeves/write-file-action@v1.2 + with: + path: ./redmine/Gemfile.local + contents: | + gem 'builder', '~> 3.2.4' + if: contains(fromJSON('["3.2 redmica/redmica@v2.4.2", "3.2 redmica/redmica@v2.3.2", "3.1 redmica/redmica@v2.2.3", "3.0 redmica/redmica@v2.2.3", "2.7 redmica/redmica@v2.2.3", "2.6 redmine/redmine@4.1.7", "2.6 redmine/redmine@4.0.9", "2.5 redmine/redmine@4.0.9", "2.6 redmine/redmine@4.2.11", "2.7 redmine/redmine@4.2.11"]'), matrix.mat_env) - name: Before script run: | From c1592b6ff306c9660b1b5151f6e9815d1cde669f Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 12:50:49 +0900 Subject: [PATCH 08/16] drop support redmine 4.0 and 4.1 --- .github/workflows/redmine_plugin.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index a7ae690..ac9ea98 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -22,9 +22,6 @@ jobs: - '3.0 redmine/redmine@5.0.9' - '2.7 redmine/redmine@4.2.11' - '2.6 redmine/redmine@4.2.11' - - '2.6 redmine/redmine@4.1.7' - - '2.6 redmine/redmine@4.0.9' - - '2.5 redmine/redmine@4.0.9' experimental: [false] include: - mat_env: '3.3 redmica/redmica@master' @@ -73,13 +70,6 @@ jobs: restore-keys: | ${{ runner.os }}-gems- - - name: uninitialized constant Nokogiri::HTML4 Issue HACK ref. https://qiita.com/Mattani/items/c4bcc95a72b69d1c0489 - uses: DamianReeves/write-file-action@v1.2 - with: - path: ./redmine/Gemfile.local - contents: | - gem 'loofah', '~> 2.20.0' - if: contains(fromJSON('["2.6 redmine/redmine@4.1.7", "2.6 redmine/redmine@4.0.9", "2.5 redmine/redmine@4.0.9"]'), matrix.mat_env) - name: "'LoadError: cannot load such file -- blankslate' Issue HACK ref. https://www.redmine.org/issues/40802#note-11" uses: DamianReeves/write-file-action@v1.2 with: From 5ad0e66814928a2c78a95cfb756efc9b90ad3cac Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 13:43:12 +0900 Subject: [PATCH 09/16] update actions/checkout@v4, actions/cache@v4 and DamianReeves/write-file-action@v1.3 to run on node20 --- .github/workflows/redmine_plugin.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index ac9ea98..f10a191 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -42,11 +42,11 @@ jobs: ruby-version: ${{ env.RUBY_VERSION }} - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ./${{ env.PLUGIN_NAME }} - name: Set up Redmine - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.REDMINE_REPOSITORY }} ref: ${{ env.REDMINE_REF }} @@ -55,7 +55,7 @@ jobs: - name: Copy the plugin files to plugin directory run: cp -pr ./${{ env.PLUGIN_NAME }} ./redmine/plugins/${{ env.PLUGIN_NAME }} - name: Create redmine/config/database.yml - uses: DamianReeves/write-file-action@v1.2 + uses: DamianReeves/write-file-action@v1.3 with: path: ./redmine/config/database.yml contents: | @@ -63,7 +63,7 @@ jobs: adapter: sqlite3 database: db/redmine_test.db - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ./redmine/vendor/bundle key: ${{ runner.os }}-gems-${{ hashFiles('./redmine/**/Gemfile.lock') }} @@ -71,7 +71,7 @@ jobs: ${{ runner.os }}-gems- - name: "'LoadError: cannot load such file -- blankslate' Issue HACK ref. https://www.redmine.org/issues/40802#note-11" - uses: DamianReeves/write-file-action@v1.2 + uses: DamianReeves/write-file-action@v1.3 with: path: ./redmine/Gemfile.local contents: | From 145571047e912ceee8b7ba758304457ca63eb76f Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sat, 3 Aug 2024 16:22:36 +0900 Subject: [PATCH 10/16] Fix check condition of script[src]/link[href] test to relaxed path regexp. Due to the asset pipeline integration using Propshaft introduced in redmine 6 (https://www.redmine.org/issues/39111), the plugin assert file path changes between redmine6 and previous versions. In addition, there is no common API to get the path beyond those versions. This change does not introduce ugly version branching if-else, just relaxed condition regexp. --- test/integration/hearts_hooked_boards_test.rb | 8 ++++---- test/integration/hearts_hooked_issues_test.rb | 12 ++++++------ test/integration/hearts_hooked_news_test.rb | 12 ++++++------ test/integration/hearts_hooked_users_test.rb | 4 ++-- test/integration/hearts_hooked_wikis_test.rb | 12 ++++++------ 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/integration/hearts_hooked_boards_test.rb b/test/integration/hearts_hooked_boards_test.rb index 167ad08..69fc9de 100644 --- a/test/integration/hearts_hooked_boards_test.rb +++ b/test/integration/hearts_hooked_boards_test.rb @@ -30,16 +30,16 @@ class HeartsHookedBoardsTest < Redmine::IntegrationTest def test_index_shall_not_contain_hooks get '/projects/1/boards/' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 0 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 0 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 0 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 0 assert_select '.heart-link-with-count', :count => 0 end def test_topic_view get '/boards/1/topics/1' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.message-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.message-1-heart .heart-count-number', :text => "1" diff --git a/test/integration/hearts_hooked_issues_test.rb b/test/integration/hearts_hooked_issues_test.rb index 61454e0..1c4fa7a 100644 --- a/test/integration/hearts_hooked_issues_test.rb +++ b/test/integration/hearts_hooked_issues_test.rb @@ -42,8 +42,8 @@ class HeartsHookedIssuesTest < Redmine::IntegrationTest def test_index_shall_not_contain_hooks get '/projects/1/issues/' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 0 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 0 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 0 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 0 assert_select '.heart-link-with-count', :count => 0 end @@ -58,8 +58,8 @@ def test_index_with_heart_column def test_view get '/issues/1' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.issue-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.issue-1-heart span.heart-count-number', :text => "0" @@ -74,8 +74,8 @@ def test_view_by_hearted_user log_user('dlopper', 'foo') get '/issues/2' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.issue-2-heart', :count => 1 assert_select '#content > .heart-link-with-count.issue-2-heart a.heart-count-number', :text => "2" diff --git a/test/integration/hearts_hooked_news_test.rb b/test/integration/hearts_hooked_news_test.rb index 6ca2eab..956ea22 100644 --- a/test/integration/hearts_hooked_news_test.rb +++ b/test/integration/hearts_hooked_news_test.rb @@ -31,8 +31,8 @@ class HeartsHookedNewsTest < Redmine::IntegrationTest def test_index get '/news/' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '.heart-link-with-count', :count => 2 assert_select '#content .news-heart-holder .heart-link-with-count.news-2-heart', :count => 1 @@ -46,8 +46,8 @@ def test_view get '/news/1' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.news-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.news-1-heart span.heart-count-number', :text => "0" @@ -59,8 +59,8 @@ def test_view_by_hearted_user get '/news/1' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.news-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.news-1-heart a.heart-count-number', :text => "1" diff --git a/test/integration/hearts_hooked_users_test.rb b/test/integration/hearts_hooked_users_test.rb index 6f4dcd0..8efd6fd 100644 --- a/test/integration/hearts_hooked_users_test.rb +++ b/test/integration/hearts_hooked_users_test.rb @@ -30,8 +30,8 @@ def test_show log_user('dlopper', 'foo') get '/users/1' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 0 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 0 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 0 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 0 assert_select 'a[href="/hearts/hearted_by/1"]', :count => 1, :text => "2 Likes" Heart.where(:user_id => 1).destroy_all diff --git a/test/integration/hearts_hooked_wikis_test.rb b/test/integration/hearts_hooked_wikis_test.rb index 598e8c5..4919365 100644 --- a/test/integration/hearts_hooked_wikis_test.rb +++ b/test/integration/hearts_hooked_wikis_test.rb @@ -31,8 +31,8 @@ class HeartsHookedWikisTest < Redmine::IntegrationTest def test_index_shall_not_contain_hooks get '/projects/ecookbook/wiki/index' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 0 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 0 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 0 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 0 assert_select '.heart-link-with-count', :count => 0 end @@ -41,8 +41,8 @@ def test_view get '/projects/ecookbook/wiki/CookBook_documentation' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.wiki_page-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.wiki_page-1-heart span.heart-count-number', :text => "0" @@ -55,8 +55,8 @@ def test_view_by_hearted_user get '/projects/ecookbook/wiki/CookBook_documentation' assert_response :success - assert_select 'script[src*="transplant_heart_link_with_counter.js"]', :count => 1 - assert_select 'link[href*="redmine_hearts/stylesheets/application.css"]', :count => 1 + assert_select 'script:match("src", ?)', /\/redmine_hearts\/.*transplant_heart_link_with_counter.*\.js/, :count => 1 + assert_select 'link:match("href", ?)', /\/redmine_hearts\/.*application.*\.css/, :count => 1 assert_select '#content > .heart-link-with-count.wiki_page-1-heart', :count => 1 assert_select '#content > .heart-link-with-count.wiki_page-1-heart a.heart-count-number', :text => "1" From a358fa4d7427a9274e1350fbb6914cb79259dd8f Mon Sep 17 00:00:00 2001 From: "Yuya.Nishida" Date: Fri, 1 Nov 2024 13:23:12 +0900 Subject: [PATCH 11/16] skip Heart#validate_user validation when importing from vote_on_issues plugin --- app/models/heart.rb | 4 +++- lib/tasks/redmine_hearts.rake | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/heart.rb b/app/models/heart.rb index c811f80..f598d1c 100644 --- a/app/models/heart.rb +++ b/app/models/heart.rb @@ -24,9 +24,11 @@ class Heart < (defined?(ApplicationRecord) == 'constant' ? ApplicationRecord : A belongs_to :heartable, :polymorphic => true belongs_to :user + attribute :skip_validate_user, :boolean + validates_presence_of :user validates_uniqueness_of :user_id, :scope => [:heartable_type, :heartable_id] - validate :validate_user + validate :validate_user, :unless => :skip_validate_user def self.of_projects(*args) projects = args.size > 0 ? args.shift : Project.none diff --git a/lib/tasks/redmine_hearts.rake b/lib/tasks/redmine_hearts.rake index 891fbf5..4b41498 100644 --- a/lib/tasks/redmine_hearts.rake +++ b/lib/tasks/redmine_hearts.rake @@ -100,6 +100,7 @@ namespace :redmine_hearts do :user => user, :created_at => datetime, :updated_at => datetime, + :skip_validate_user => true, ) end end From 075903dccd6abca2b8edaefa35f62c8da8b876ff Mon Sep 17 00:00:00 2001 From: "Yuya.Nishida" Date: Fri, 1 Nov 2024 13:47:54 +0900 Subject: [PATCH 12/16] skip invalid record data migration on VoteOnIssue --- lib/tasks/redmine_hearts.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/redmine_hearts.rake b/lib/tasks/redmine_hearts.rake index 4b41498..7d16227 100644 --- a/lib/tasks/redmine_hearts.rake +++ b/lib/tasks/redmine_hearts.rake @@ -88,7 +88,7 @@ namespace :redmine_hearts do num_of_heart_before_processing = Heart.count - VoteOnIssue.where('vote_val > 0').each do |vote| + VoteOnIssue.where('vote_val > 0').where.not(issue: nil, user: nil).each do |vote| issue = vote.issue user = vote.user datetime = vote.created_at || Time.now From ee0c93cce0fffbe8124e49e1b7f153a4c801b82e Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Wed, 27 Nov 2024 00:25:48 +0900 Subject: [PATCH 13/16] upgrade redmine/redmica and ruby versions in redmine_plugin.yml --- .github/workflows/redmine_plugin.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/redmine_plugin.yml b/.github/workflows/redmine_plugin.yml index f10a191..ef8319d 100644 --- a/.github/workflows/redmine_plugin.yml +++ b/.github/workflows/redmine_plugin.yml @@ -11,15 +11,17 @@ jobs: fail-fast: false matrix: mat_env: - - '3.3 redmica/redmica@v3.0.1' + - '3.3 redmica/redmica@v3.1.0' + - '3.3 redmica/redmica@v3.0.4' - '3.2 redmica/redmica@v2.4.2' - '3.2 redmica/redmica@v2.3.2' - '3.1 redmica/redmica@v2.2.3' - '3.0 redmica/redmica@v2.2.3' - '2.7 redmica/redmica@v2.2.3' - - '3.2 redmine/redmine@5.1.3' - - '3.1 redmine/redmine@5.0.9' - - '3.0 redmine/redmine@5.0.9' + - '3.3 redmine/redmine@6.0.1' + - '3.2 redmine/redmine@5.1.4' + - '3.1 redmine/redmine@5.0.10' + - '3.0 redmine/redmine@5.0.10' - '2.7 redmine/redmine@4.2.11' - '2.6 redmine/redmine@4.2.11' experimental: [false] From a14e0a61ef05b408da0532092fbfcc4d035afdbe Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Fri, 29 Nov 2024 00:36:41 +0900 Subject: [PATCH 14/16] introduce tabler-icons for redmine 6.0 --- app/helpers/hearts_helper.rb | 17 ++++++++++++++++- assets/images/icons.svg | 8 ++++++++ assets/stylesheets/application.css | 13 +++++++++++-- config/icon_source.yml | 2 ++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 assets/images/icons.svg create mode 100644 config/icon_source.yml diff --git a/app/helpers/hearts_helper.rb b/app/helpers/hearts_helper.rb index 13e22b3..b5e3a6e 100644 --- a/app/helpers/hearts_helper.rb +++ b/app/helpers/hearts_helper.rb @@ -53,7 +53,11 @@ def heart_link_with_counter_manual(objects, heart_bool, hearted_users_count, use objects = Array.wrap(objects) css = heart_bool ? 'icon icon-heart' : 'icon icon-heart-off' - text = content_tag :span, l(:hearts_link_label), :class => 'heart-link-label' + if defined? hearts_icon_with_label # redmine >= 6.0 + text = hearts_icon_with_label('heart', l(:hearts_link_label), css_class: 'heart-link-label') + else + text = content_tag :span, l(:hearts_link_label), :class => 'heart-link-label' + end object_type = objects.first.class.to_s.underscore object_id = (objects.size == 1) ? objects.first.id : objects.map(&:id).sort @@ -127,4 +131,15 @@ def render_api_heartable_include(heartable, api) end end end + + if defined? IconsHelper # redmine >= 6.0 + include IconsHelper + def hearts_icon_with_label(icon_name, label_text, icon_only: false, size: 18, css_class: nil) + label_classes = ["icon-label"] + label_classes << "hidden" if icon_only + plugin = 'redmine_hearts' + sprite_icon(icon_name, size: size, css_class: css_class, plugin: plugin) + content_tag(:span, label_text, class: label_classes.join(' ')) + end + end + end diff --git a/assets/images/icons.svg b/assets/images/icons.svg new file mode 100644 index 0000000..1a6128b --- /dev/null +++ b/assets/images/icons.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/stylesheets/application.css b/assets/stylesheets/application.css index 76eab4c..74bb4a5 100644 --- a/assets/stylesheets/application.css +++ b/assets/stylesheets/application.css @@ -1,8 +1,17 @@ .icon-heart { background-image: url(../images/heart.png); } .icon-heart-off { background-image: url(../images/heart_off.png); } - .heart-link-with-count { display: inline-block; } -.heart-count-number { padding-left: 1ex; } +.heart-count-number { padding-left: 0.75ex; } + +/* redmine >= 6.0 */ +:is(.icon-heart, .icon-heart-off):has(svg.icon-svg) { background-image: none; } +.icon-heart .icon-svg { fill: #fb8098; stroke: none; } +.heart-link-with-count:has(svg.icon-svg) { + display: inline-flex; + align-items: center; + vertical-align: middle; +} + @media screen and (max-width: 899px) { .heart-link-with-count > *:nth-child(1) { diff --git a/config/icon_source.yml b/config/icon_source.yml new file mode 100644 index 0000000..0ea3bcd --- /dev/null +++ b/config/icon_source.yml @@ -0,0 +1,2 @@ +- name: heart + svg: heart From ca8bbb3242347051fe01fa489d1eb14b1359a481 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sun, 1 Dec 2024 19:30:23 +0900 Subject: [PATCH 15/16] Add tests for heart_link_with_counter method with IconsHelper support (Redmine6) --- test/unit/helpers/hearts_helper_test.rb | 114 ++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/test/unit/helpers/hearts_helper_test.rb b/test/unit/helpers/hearts_helper_test.rb index 665c610..7337971 100644 --- a/test/unit/helpers/hearts_helper_test.rb +++ b/test/unit/helpers/hearts_helper_test.rb @@ -49,7 +49,29 @@ class HeartsHelperTest < Redmine::HelperTest end assert_equal expected, heart_link_with_counter(Issue.find(1), User.find(1)) assert_equal expected, heart_link_with_counter([Issue.find(1)], User.find(1)) - end + end unless defined? IconsHelper # redmine < 6.0 + + test '#heart_link_with_counter with a non-hearted object' do + expected = content_tag(:span, :class => "issue-1-heart heart-link-with-count") do + safe_join( + [ + link_to( + hearts_icon_with_label('heart', 'Like', css_class: 'heart-link-label'), + heart_url(:object_id => 1, :object_type => 'issue', :only_path => true), + :remote => true, :method => 'post', :class => "icon icon-heart-off" + ), + link_to( + "0", + hearts_hearted_users_url(:object_id => 1, :object_type => 'issue', :only_path => true), + :class => "heart-count-number" + ), + ], + "" + ) + end + assert_equal expected, heart_link_with_counter(Issue.find(1), User.find(1)) + assert_equal expected, heart_link_with_counter([Issue.find(1)], User.find(1)) + end if defined? IconsHelper # redmine >= 6.0 test '#heart_link_with_counter with a non-hearted object for anonymous user' do expected = content_tag(:span, :class => "issue-1-heart heart-link-with-count") do @@ -64,7 +86,22 @@ class HeartsHelperTest < Redmine::HelperTest ) end assert_equal expected, heart_link_with_counter(Issue.find(1), User.anonymous) - end + end unless defined? IconsHelper # redmine < 6.0 + + test '#heart_link_with_counter with a non-hearted object for anonymous user' do + expected = content_tag(:span, :class => "issue-1-heart heart-link-with-count") do + safe_join( + [ + content_tag(:span, + hearts_icon_with_label('heart', 'Like', css_class: 'heart-link-label'), + :class => "icon icon-heart-off"), + content_tag(:span, "0", :class => "heart-count-number"), + ], + "" + ) + end + assert_equal expected, heart_link_with_counter(Issue.find(1), User.anonymous) + end if defined? IconsHelper # redmine >= 6.0 test '#heart_link_with_counter with a multiple objets array' do expected = content_tag(:span, :class => "issue-bulk-heart heart-link-with-count") do @@ -85,7 +122,28 @@ class HeartsHelperTest < Redmine::HelperTest ) end assert_equal expected, heart_link_with_counter([Issue.find(1), Issue.find(3)], User.find(1)) - end + end unless defined? IconsHelper # redmine < 6.0 + + test '#heart_link_with_counter with a multiple objets array' do + expected = content_tag(:span, :class => "issue-bulk-heart heart-link-with-count") do + safe_join( + [ + link_to( + hearts_icon_with_label('heart', 'Like', css_class: 'heart-link-label'), + heart_url(:object_id => [1, 3], :object_type => 'issue', :only_path => true), + :remote => true, :method => 'post', :class => "icon icon-heart-off" + ), + link_to( + "0", + hearts_hearted_users_url(:object_id => [1, 3], :object_type => 'issue', :only_path => true), + :class => "heart-count-number" + ), + ], + "" + ) + end + assert_equal expected, heart_link_with_counter([Issue.find(1), Issue.find(3)], User.find(1)) + end if defined? IconsHelper # redmine >= 6.0 def test_heart_link_with_counter_with_nil_should_return_empty_string assert_equal '', heart_link_with_counter(nil, User.find(1)) @@ -112,7 +170,30 @@ def test_heart_link_with_counter_with_nil_should_return_empty_string ) end assert_equal expected, heart_link_with_counter(Issue.find(1), User.find(1)) - end + end unless defined? IconsHelper # redmine < 6.0 + + test '#heart_link_with_counter with a hearted object' do + Heart.create!(:heartable => Issue.find(1), :user => User.find(1)) + + expected = content_tag(:span, :class => "issue-1-heart heart-link-with-count") do + safe_join( + [ + link_to( + hearts_icon_with_label('heart', 'Like', css_class: 'heart-link-label'), + heart_url(:object_id => 1, :object_type => 'issue', :only_path => true), + :remote => true, :method => 'delete', :class => "icon icon-heart" + ), + link_to( + "1", + hearts_hearted_users_url(:object_id => 1, :object_type => 'issue', :only_path => true), + :class => "heart-count-number" + ), + ], + "" + ) + end + assert_equal expected, heart_link_with_counter(Issue.find(1), User.find(1)) + end if defined? IconsHelper # redmine >= 6.0 test '#heart_link_with_counter with a hearted object for anonymous user' do Heart.create!(:heartable => Issue.find(1), :user => User.find(1)) @@ -129,7 +210,24 @@ def test_heart_link_with_counter_with_nil_should_return_empty_string ) end assert_equal expected, heart_link_with_counter(Issue.find(1), User.anonymous) - end + end unless defined? IconsHelper # redmine < 6.0 + + test '#heart_link_with_counter with a hearted object for anonymous user' do + Heart.create!(:heartable => Issue.find(1), :user => User.find(1)) + + expected = content_tag(:span, :class => "issue-1-heart heart-link-with-count") do + safe_join( + [ + content_tag(:span, + hearts_icon_with_label('heart', 'Like', css_class: 'heart-link-label'), + :class => "icon icon-heart-off"), + content_tag(:span, "1", :class => "heart-count-number"), + ], + "" + ) + end + assert_equal expected, heart_link_with_counter(Issue.find(1), User.anonymous) + end if defined? IconsHelper # redmine >= 6.0 test '#multiple_heart_links_with_counters with multiple objects' do queries = [] @@ -202,4 +300,10 @@ def test_heart_link_with_counter_with_nil_should_return_empty_string test "#link_to_heartable_with_nil" do assert_raises { link_to_heartable(nil) } end + + test "#hearts_icon_with_label" do + expected = sprite_icon('heart', size: 18, plugin: 'redmine_hearts') + + content_tag(:span, 'Like', class: 'icon-label') + assert_equal expected, hearts_icon_with_label('heart', 'Like') + end if defined? IconsHelper # redmine >= 6.0 end From 6cbd46824542bb4baf5c7fcb028178a71a1d1ea8 Mon Sep 17 00:00:00 2001 From: cat_in_136 Date: Sun, 1 Dec 2024 19:43:57 +0900 Subject: [PATCH 16/16] Update README and init.rb: add license information and update version to 4.0.0. --- README.rdoc | 7 +++++++ init.rb | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 738fcd2..94f71b5 100644 --- a/README.rdoc +++ b/README.rdoc @@ -25,3 +25,10 @@ Follow below steps to migrate from other plugins : * Restart your redmine Ditto {issue_votes}[https://github.com/KohaSuomi/issue_votes]. Run bundle exec rake -T redmine_hearts:migrate_from to check the task name. + +== License + +* Codes: MIT License. +* Legacy Icons: famfamfam-silk: {CC-By-2.5}[https://creativecommons.org/licenses/by/2.5/] by Mark James http://www.famfamfam.com/lab/icons/silk/ +* Vector Icons: {Tabler Icons}[https://tabler.io/]: MIT License + diff --git a/init.rb b/init.rb index 3d773fb..5bf2e32 100644 --- a/init.rb +++ b/init.rb @@ -22,10 +22,12 @@ require File.expand_path('../lib/redmine_hearts/view_hook.rb', __FILE__) Redmine::Plugin.register :redmine_hearts do + requires_redmine :version_or_higher => '4.2.0' + name 'Redmine Hearts plugin' author '@cat_in_136' description 'provide intra-Redmine Like/Fav reactions' - version '3.0.1' + version '4.0.0' url 'https://github.com/cat-in-136/redmine_hearts' author_url 'https://github.com/cat-in-136/'