redmineup 1.1.14 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/images/icons.svg +9 -0
- data/app/assets/javascripts/select2_helpers.js +2 -0
- data/app/assets/stylesheets/redmineup.css +28 -10
- data/config/locales/ar.yml +13 -0
- data/config/locales/az.yml +13 -0
- data/config/locales/bg.yml +13 -0
- data/config/locales/bs.yml +13 -0
- data/config/locales/ca.yml +13 -0
- data/config/locales/cs.yml +1 -0
- data/config/locales/da.yml +13 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/el.yml +13 -0
- data/config/locales/en-GB.yml +14 -0
- data/config/locales/es-PA.yml +14 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/et.yml +13 -0
- data/config/locales/eu.yml +13 -0
- data/config/locales/fa.yml +13 -0
- data/config/locales/fi.yml +13 -0
- data/config/locales/fr.yml +13 -0
- data/config/locales/gl.yml +13 -0
- data/config/locales/he.yml +13 -0
- data/config/locales/hr.yml +13 -0
- data/config/locales/hu.yml +13 -0
- data/config/locales/id.yml +13 -0
- data/config/locales/it.yml +13 -0
- data/config/locales/ja.yml +13 -0
- data/config/locales/ko.yml +13 -0
- data/config/locales/lt.yml +13 -0
- data/config/locales/lv.yml +13 -0
- data/config/locales/mk.yml +13 -0
- data/config/locales/mn.yml +13 -0
- data/config/locales/nl.yml +13 -0
- data/config/locales/no.yml +13 -0
- data/config/locales/pl.yml +13 -0
- data/config/locales/pt-BR.yml +13 -0
- data/config/locales/pt.yml +13 -0
- data/config/locales/ro.yml +13 -0
- data/config/locales/ru.yml +1 -1
- data/config/locales/sk.yml +13 -0
- data/config/locales/sl.yml +13 -0
- data/config/locales/sq.yml +13 -0
- data/config/locales/sr-YU.yml +13 -0
- data/config/locales/sr.yml +13 -0
- data/config/locales/sv.yml +13 -0
- data/config/locales/ta-IN.yml +13 -0
- data/config/locales/th.yml +13 -0
- data/config/locales/tr.yml +13 -0
- data/config/locales/uk.yml +13 -0
- data/config/locales/vi.yml +13 -0
- data/config/locales/zh-TW.yml +13 -0
- data/config/locales/zh.yml +13 -0
- data/doc/CHANGELOG +14 -0
- data/doc/tagging-and-select2.md +7 -0
- data/lib/action_cable/server/rup_server.rb +5 -1
- data/lib/redmineup/acts_as_taggable/up_acts_as_taggable.rb +23 -23
- data/lib/redmineup/acts_as_viewed/up_acts_as_viewed.rb +7 -2
- data/lib/redmineup/currency/formatting.rb +1 -0
- data/lib/redmineup/helpers/tags_helper.rb +67 -0
- data/lib/redmineup/version.rb +1 -1
- data/redmineup.gemspec +1 -1
- data/test/acts_as_viewed/rup_acts_as_viewed_test.rb +12 -0
- data/test/currency_test.rb +7 -0
- data/test/tags_helper_view_test.rb +61 -0
- metadata +47 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
sl:
|
|
2
|
+
label_redmineup_settings: Nastavitve valute
|
|
3
|
+
label_redmineup_money: Valuta
|
|
4
|
+
label_redmineup_disable_taxes: Onemogoči davke
|
|
5
|
+
label_redmineup_default_tax: Privzeta vrednost davka
|
|
6
|
+
label_redmineup_tax_type: Vrsta davka
|
|
7
|
+
label_redmineup_tax_type_exclusive: Davek ni vključen
|
|
8
|
+
label_redmineup_tax_type_inclusive: Davek je vključen
|
|
9
|
+
label_redmineup_default_currency: Privzeta valuta
|
|
10
|
+
label_redmineup_major_currencies: Glavne valute
|
|
11
|
+
label_redmineup_thousands_delimiter: Ločilo tisočic
|
|
12
|
+
label_redmineup_decimal_separator: Decimalno ločilo
|
|
13
|
+
lable_redmineup_add_tag: dodaj oznako
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
sq:
|
|
2
|
+
label_redmineup_settings: Cilësimet e parasë
|
|
3
|
+
label_redmineup_money: Para
|
|
4
|
+
label_redmineup_disable_taxes: Çaktivizo taksat
|
|
5
|
+
label_redmineup_default_tax: Vlera e parazgjedhur e taksës
|
|
6
|
+
label_redmineup_tax_type: Lloji i taksës
|
|
7
|
+
label_redmineup_tax_type_exclusive: Pa taksë të përfshirë
|
|
8
|
+
label_redmineup_tax_type_inclusive: Me taksë të përfshirë
|
|
9
|
+
label_redmineup_default_currency: Monedha e parazgjedhur
|
|
10
|
+
label_redmineup_major_currencies: Monedhat kryesore
|
|
11
|
+
label_redmineup_thousands_delimiter: Ndarësi i mijësheve
|
|
12
|
+
label_redmineup_decimal_separator: Ndarësi dhjetor
|
|
13
|
+
lable_redmineup_add_tag: shto etiketë
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
sr-YU:
|
|
2
|
+
label_redmineup_settings: Podešavanja novca
|
|
3
|
+
label_redmineup_money: Novac
|
|
4
|
+
label_redmineup_disable_taxes: Onemogući poreze
|
|
5
|
+
label_redmineup_default_tax: Podrazumevana vrednost poreza
|
|
6
|
+
label_redmineup_tax_type: Vrsta poreza
|
|
7
|
+
label_redmineup_tax_type_exclusive: Porez nije uključen
|
|
8
|
+
label_redmineup_tax_type_inclusive: Porez je uključen
|
|
9
|
+
label_redmineup_default_currency: Podrazumevana valuta
|
|
10
|
+
label_redmineup_major_currencies: Glavne valute
|
|
11
|
+
label_redmineup_thousands_delimiter: Razdvajač hiljada
|
|
12
|
+
label_redmineup_decimal_separator: Decimalni razdvajač
|
|
13
|
+
lable_redmineup_add_tag: dodaj oznaku
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
sr:
|
|
2
|
+
label_redmineup_settings: Подешавања новца
|
|
3
|
+
label_redmineup_money: Новац
|
|
4
|
+
label_redmineup_disable_taxes: Онемогући порезе
|
|
5
|
+
label_redmineup_default_tax: Подразумевана вредност пореза
|
|
6
|
+
label_redmineup_tax_type: Врста пореза
|
|
7
|
+
label_redmineup_tax_type_exclusive: Порез није укључен
|
|
8
|
+
label_redmineup_tax_type_inclusive: Порез је укључен
|
|
9
|
+
label_redmineup_default_currency: Подразумевана валута
|
|
10
|
+
label_redmineup_major_currencies: Главне валуте
|
|
11
|
+
label_redmineup_thousands_delimiter: Раздвајач хиљада
|
|
12
|
+
label_redmineup_decimal_separator: Децимални раздвајач
|
|
13
|
+
lable_redmineup_add_tag: додај ознаку
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
sv:
|
|
2
|
+
label_redmineup_settings: Penninginställningar
|
|
3
|
+
label_redmineup_money: Pengar
|
|
4
|
+
label_redmineup_disable_taxes: Inaktivera skatter
|
|
5
|
+
label_redmineup_default_tax: Standardskattevärde
|
|
6
|
+
label_redmineup_tax_type: Skatttyp
|
|
7
|
+
label_redmineup_tax_type_exclusive: Exklusive skatt
|
|
8
|
+
label_redmineup_tax_type_inclusive: Inklusive skatt
|
|
9
|
+
label_redmineup_default_currency: Standardvaluta
|
|
10
|
+
label_redmineup_major_currencies: Huvudvalutor
|
|
11
|
+
label_redmineup_thousands_delimiter: Tusentalsavgränsare
|
|
12
|
+
label_redmineup_decimal_separator: Decimalavgränsare
|
|
13
|
+
lable_redmineup_add_tag: lägg till tagg
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ta-IN:
|
|
2
|
+
label_redmineup_settings: நாணய அமைப்புகள்
|
|
3
|
+
label_redmineup_money: நாணயம்
|
|
4
|
+
label_redmineup_disable_taxes: வரிகளை முடக்கு
|
|
5
|
+
label_redmineup_default_tax: இயல்புநிலை வரி மதிப்பு
|
|
6
|
+
label_redmineup_tax_type: வரி வகை
|
|
7
|
+
label_redmineup_tax_type_exclusive: வரி சேர்க்கப்படாதது
|
|
8
|
+
label_redmineup_tax_type_inclusive: வரி சேர்க்கப்பட்டது
|
|
9
|
+
label_redmineup_default_currency: இயல்புநிலை நாணயம்
|
|
10
|
+
label_redmineup_major_currencies: முக்கிய நாணயங்கள்
|
|
11
|
+
label_redmineup_thousands_delimiter: ஆயிரம் பிரிப்பான்
|
|
12
|
+
label_redmineup_decimal_separator: தசம பிரிப்பான்
|
|
13
|
+
lable_redmineup_add_tag: குறிச்சொல் சேர்
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
th:
|
|
2
|
+
label_redmineup_settings: การตั้งค่าเงิน
|
|
3
|
+
label_redmineup_money: เงิน
|
|
4
|
+
label_redmineup_disable_taxes: ปิดใช้งานภาษี
|
|
5
|
+
label_redmineup_default_tax: ค่าภาษีเริ่มต้น
|
|
6
|
+
label_redmineup_tax_type: ประเภทภาษี
|
|
7
|
+
label_redmineup_tax_type_exclusive: ไม่รวมภาษี
|
|
8
|
+
label_redmineup_tax_type_inclusive: รวมภาษี
|
|
9
|
+
label_redmineup_default_currency: สกุลเงินเริ่มต้น
|
|
10
|
+
label_redmineup_major_currencies: สกุลเงินหลัก
|
|
11
|
+
label_redmineup_thousands_delimiter: ตัวคั่นหลักพัน
|
|
12
|
+
label_redmineup_decimal_separator: ตัวคั่นทศนิยม
|
|
13
|
+
lable_redmineup_add_tag: เพิ่มแท็ก
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
tr:
|
|
2
|
+
label_redmineup_settings: Para ayarları
|
|
3
|
+
label_redmineup_money: Para
|
|
4
|
+
label_redmineup_disable_taxes: Vergileri devre dışı bırak
|
|
5
|
+
label_redmineup_default_tax: Varsayılan vergi değeri
|
|
6
|
+
label_redmineup_tax_type: Vergi türü
|
|
7
|
+
label_redmineup_tax_type_exclusive: Vergi hariç
|
|
8
|
+
label_redmineup_tax_type_inclusive: Vergi dahil
|
|
9
|
+
label_redmineup_default_currency: Varsayılan para birimi
|
|
10
|
+
label_redmineup_major_currencies: Ana para birimleri
|
|
11
|
+
label_redmineup_thousands_delimiter: Binlik ayracı
|
|
12
|
+
label_redmineup_decimal_separator: Ondalık ayracı
|
|
13
|
+
lable_redmineup_add_tag: etiket ekle
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
uk:
|
|
2
|
+
label_redmineup_settings: Налаштування грошей
|
|
3
|
+
label_redmineup_money: Гроші
|
|
4
|
+
label_redmineup_disable_taxes: Вимкнути податки
|
|
5
|
+
label_redmineup_default_tax: Типове значення податку
|
|
6
|
+
label_redmineup_tax_type: Тип податку
|
|
7
|
+
label_redmineup_tax_type_exclusive: Податок не включено
|
|
8
|
+
label_redmineup_tax_type_inclusive: Податок включено
|
|
9
|
+
label_redmineup_default_currency: Типова валюта
|
|
10
|
+
label_redmineup_major_currencies: Основні валюти
|
|
11
|
+
label_redmineup_thousands_delimiter: Роздільник тисяч
|
|
12
|
+
label_redmineup_decimal_separator: Десятковий роздільник
|
|
13
|
+
lable_redmineup_add_tag: додати тег
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
vi:
|
|
2
|
+
label_redmineup_settings: Cài đặt tiền tệ
|
|
3
|
+
label_redmineup_money: Tiền tệ
|
|
4
|
+
label_redmineup_disable_taxes: Tắt thuế
|
|
5
|
+
label_redmineup_default_tax: Giá trị thuế mặc định
|
|
6
|
+
label_redmineup_tax_type: Loại thuế
|
|
7
|
+
label_redmineup_tax_type_exclusive: Chưa bao gồm thuế
|
|
8
|
+
label_redmineup_tax_type_inclusive: Đã bao gồm thuế
|
|
9
|
+
label_redmineup_default_currency: Tiền tệ mặc định
|
|
10
|
+
label_redmineup_major_currencies: Tiền tệ chính
|
|
11
|
+
label_redmineup_thousands_delimiter: Dấu phân cách hàng nghìn
|
|
12
|
+
label_redmineup_decimal_separator: Dấu phân cách thập phân
|
|
13
|
+
lable_redmineup_add_tag: thêm thẻ
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
zh-TW:
|
|
2
|
+
label_redmineup_settings: 貨幣設定
|
|
3
|
+
label_redmineup_money: 貨幣
|
|
4
|
+
label_redmineup_disable_taxes: 停用稅金
|
|
5
|
+
label_redmineup_default_tax: 預設稅額
|
|
6
|
+
label_redmineup_tax_type: 稅金類型
|
|
7
|
+
label_redmineup_tax_type_exclusive: 未含稅
|
|
8
|
+
label_redmineup_tax_type_inclusive: 含稅
|
|
9
|
+
label_redmineup_default_currency: 預設貨幣
|
|
10
|
+
label_redmineup_major_currencies: 主要貨幣
|
|
11
|
+
label_redmineup_thousands_delimiter: 千位分隔符
|
|
12
|
+
label_redmineup_decimal_separator: 小數分隔符
|
|
13
|
+
lable_redmineup_add_tag: 新增標籤
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
zh:
|
|
2
|
+
label_redmineup_settings: 货币设置
|
|
3
|
+
label_redmineup_money: 货币
|
|
4
|
+
label_redmineup_disable_taxes: 禁用税费
|
|
5
|
+
label_redmineup_default_tax: 默认税额
|
|
6
|
+
label_redmineup_tax_type: 税费类型
|
|
7
|
+
label_redmineup_tax_type_exclusive: 不含税
|
|
8
|
+
label_redmineup_tax_type_inclusive: 含税
|
|
9
|
+
label_redmineup_default_currency: 默认货币
|
|
10
|
+
label_redmineup_major_currencies: 主要货币
|
|
11
|
+
label_redmineup_thousands_delimiter: 千位分隔符
|
|
12
|
+
label_redmineup_decimal_separator: 小数分隔符
|
|
13
|
+
lable_redmineup_add_tag: 添加标签
|
data/doc/CHANGELOG
CHANGED
|
@@ -4,6 +4,20 @@ Redmine UP gem - general functions for plugins (tags, vote, viewing, currency)
|
|
|
4
4
|
Copyright (C) 2011-2026 Kirill Bezrukov (RedmineUP)
|
|
5
5
|
https://www.redmineup.com/
|
|
6
6
|
|
|
7
|
+
== 2026-10-24 v1.1.16
|
|
8
|
+
|
|
9
|
+
* Added shared styles for tag chips: colored, gray, link
|
|
10
|
+
* Added tag_label, tag_label_link(s), tag_sidebar_cloud helpers
|
|
11
|
+
* Added tag and tag-merge icons
|
|
12
|
+
* Added translations for all languages supported by Redmine
|
|
13
|
+
* Fixed websocket reconnection every minute behind nginx
|
|
14
|
+
* Fixed error on deleting a person when the viewed model loads later
|
|
15
|
+
* Fixed trailing ".0" in amounts of currencies without a fractional unit
|
|
16
|
+
|
|
17
|
+
== 2026-10-11 v1.1.15
|
|
18
|
+
|
|
19
|
+
* Speeded up the tags list query (available_tags) on pages with a lot of tagged records
|
|
20
|
+
|
|
7
21
|
== 2026-10-09 v1.1.14
|
|
8
22
|
|
|
9
23
|
* Fixed missing calendar icons and extra padding in Redmine 6/7
|
data/doc/tagging-and-select2.md
CHANGED
|
@@ -11,6 +11,13 @@ Provided by `Redmineup::TagsHelper`:
|
|
|
11
11
|
- `tag_links(tag_list, options = {}) { |tag| url }`
|
|
12
12
|
- `tag_cloud(tags, classes) { |tag, css_class| ... }`
|
|
13
13
|
- `tag_cloud_links(tags, key, options = {})`
|
|
14
|
+
|
|
15
|
+
Since 1.1.16 (the display mode, sidebar style, counts and order come from the Tags plugin when it is installed; without it tags are colored chips in a cloud):
|
|
16
|
+
|
|
17
|
+
- `tag_display_mode` returns `color`, `plain` or `link`
|
|
18
|
+
- `tag_label(inner, color)` wraps markup in a chip (`span.tag-label.tag-label-color` / `.tag-label-plain`) or `span.tag-link`
|
|
19
|
+
- `tag_label_link(url, tag_name, options = {})`, `tag_label_links(tag_list, options = {}) { |tag| url }`, `tag_label_separator`
|
|
20
|
+
- `tag_sidebar_cloud(tags, key)` renders the sidebar tags (list, cloud or simple cloud) and resolves links through `{key}_tag_url`
|
|
14
21
|
- `tag_separator`
|
|
15
22
|
|
|
16
23
|
### Recommended pattern
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module ActionCable
|
|
2
2
|
module Server
|
|
3
3
|
class RupServer < ActionCable::Server::Base
|
|
4
|
+
BEAT_INTERVAL = 10
|
|
5
|
+
|
|
4
6
|
attr_reader :config
|
|
5
7
|
|
|
6
8
|
def initialize(config: self.class.config)
|
|
@@ -10,7 +12,9 @@ module ActionCable
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def setup_heartbeat_timer
|
|
13
|
-
|
|
15
|
+
@heartbeat_timer ||= event_loop.timer(BEAT_INTERVAL) do
|
|
16
|
+
event_loop.post { connections.each(&:beat) }
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
16
20
|
end
|
|
@@ -129,43 +129,43 @@ module Redmineup
|
|
|
129
129
|
#Return all avalible tags for a project or global
|
|
130
130
|
#Example: Question.available_tags(:project => @project_id )
|
|
131
131
|
def available_tags(options = {})
|
|
132
|
-
projects = [[options[:project]], options[:projects]].flatten.compact
|
|
133
132
|
limit = options[:limit].to_i.zero? ? 30 : options[:limit].to_i
|
|
134
|
-
|
|
135
|
-
class_name = quote_string_value(base_class.name)
|
|
136
|
-
join = []
|
|
137
|
-
join << "JOIN #{Tagging.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id "
|
|
138
|
-
join << "JOIN #{table_name} ON #{table_name}.id = #{Tagging.table_name}.taggable_id
|
|
139
|
-
AND #{Tagging.table_name}.taggable_type = #{class_name} "
|
|
140
|
-
if attribute_names.include?('project_id') && projects.any?
|
|
141
|
-
join << "JOIN #{Project.table_name} ON #{Project.table_name}.id = #{table_name}.project_id"
|
|
142
|
-
scope = scope.where("#{table_name}.project_id IN (%s)", projects.map(&:id).join(','))
|
|
143
|
-
end
|
|
133
|
+
counts_sql = Tag.connection.unprepared_statement { tagged_records_scope(options).to_sql }
|
|
144
134
|
|
|
135
|
+
scope = Tag.joins("JOIN (#{counts_sql}) tag_counts ON tag_counts.tag_id = #{Tag.table_name}.id")
|
|
136
|
+
scope = scope.select("#{Tag.table_name}.*, tag_counts.count AS count")
|
|
145
137
|
if options[:name_like].present?
|
|
146
138
|
scope = scope.where("LOWER(#{Tag.table_name}.name) LIKE LOWER(?)", "%#{options[:name_like]}%")
|
|
147
139
|
end
|
|
140
|
+
scope.order("#{Tag.table_name}.name").limit(limit)
|
|
141
|
+
end
|
|
148
142
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
# tag_id => number of visible taggables. Extension point for available_tags.
|
|
144
|
+
def tagged_records_scope(options = {})
|
|
145
|
+
projects = [[options[:project]], options[:projects]].flatten.compact
|
|
146
|
+
scope = Tagging.unscoped.
|
|
147
|
+
joins("JOIN #{table_name} ON #{table_name}.id = #{Tagging.table_name}.taggable_id").
|
|
148
|
+
where("#{Tagging.table_name}.taggable_type = ?", base_class.name).
|
|
149
|
+
group("#{Tagging.table_name}.tag_id").
|
|
150
|
+
select("#{Tagging.table_name}.tag_id, COUNT(DISTINCT #{Tagging.table_name}.taggable_id) AS count")
|
|
151
|
+
|
|
152
|
+
projects_joined = false
|
|
153
|
+
if attribute_names.include?('project_id') && projects.any?
|
|
154
|
+
scope = scope.joins("JOIN #{Project.table_name} ON #{Project.table_name}.id = #{table_name}.project_id").
|
|
155
|
+
where("#{table_name}.project_id IN (?)", projects.map(&:id))
|
|
156
|
+
projects_joined = true
|
|
157
|
+
end
|
|
153
158
|
|
|
154
159
|
if base_class.respond_to?(:visible_condition)
|
|
155
160
|
visible_condition = base_class.visible_condition(User.current)
|
|
156
|
-
if visible_condition.include?('project_id') &&
|
|
157
|
-
|
|
161
|
+
if visible_condition.include?('project_id') && !projects_joined
|
|
162
|
+
scope = scope.joins("JOIN #{Project.table_name} ON #{Project.table_name}.id = #{table_name}.project_id")
|
|
158
163
|
end
|
|
159
164
|
scope = scope.where(visible_condition)
|
|
160
165
|
end
|
|
161
|
-
scope = scope.joins(join.join(' '))
|
|
162
|
-
scope = scope.select("#{Tag.table_name}.*, COUNT(DISTINCT #{Tagging.table_name}.taggable_id) AS count")
|
|
163
|
-
scope = scope.group("#{Tag.table_name}.id, #{Tag.table_name}.name #{group_fields}")
|
|
164
|
-
scope = scope.having('COUNT(*) > 0')
|
|
165
|
-
scope = scope.order("#{Tag.table_name}.name")
|
|
166
|
-
scope = scope.limit(limit)
|
|
167
166
|
scope
|
|
168
167
|
end
|
|
168
|
+
|
|
169
169
|
# Returns an array of related tags.
|
|
170
170
|
# Related tags are all the other tags that are found on the models tagged with the provided tags.
|
|
171
171
|
#
|
|
@@ -113,8 +113,13 @@ module Redmineup
|
|
|
113
113
|
# Add to the User (or whatever the viewer is) a has_many viewings
|
|
114
114
|
viewer_as_class = viewer_class.constantize
|
|
115
115
|
return if viewer_as_class.instance_methods.include?('find_in_viewings')
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
# Subclasses loaded earlier (Person < User) keep their own association set, so add it to them as well.
|
|
117
|
+
([viewer_as_class] + viewer_as_class.descendants).each do |klass|
|
|
118
|
+
next if klass.reflect_on_association(:viewings)
|
|
119
|
+
|
|
120
|
+
klass.class_eval do
|
|
121
|
+
has_many :viewings, foreign_key: :viewer_id, class_name: viewing_class.to_s, dependent: :delete_all
|
|
122
|
+
end
|
|
118
123
|
end
|
|
119
124
|
end
|
|
120
125
|
end
|
|
@@ -59,6 +59,7 @@ module Redmineup
|
|
|
59
59
|
decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1)
|
|
60
60
|
decimal_part
|
|
61
61
|
end
|
|
62
|
+
formatted.sub!(/\..*/, '') if currency.decimal_places.zero?
|
|
62
63
|
formatted.gsub!(/\./, rules[:decimal_mark]) unless '.' == rules[:decimal_mark]
|
|
63
64
|
end
|
|
64
65
|
|
|
@@ -50,5 +50,72 @@ module Redmineup
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
|
|
54
|
+
def tag_display_mode
|
|
55
|
+
if defined?(RedmineupTags::TagDisplay)
|
|
56
|
+
RedmineupTags::TagDisplay.mode
|
|
57
|
+
elsif defined?(RedmineupTags) && RedmineupTags.respond_to?(:use_colors?)
|
|
58
|
+
RedmineupTags.use_colors? ? 'color' : 'plain'
|
|
59
|
+
else
|
|
60
|
+
'color'
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def tag_label_separator
|
|
65
|
+
tag_display_mode == 'link' ? ', ' : ' '
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def tag_label(inner, color)
|
|
69
|
+
case tag_display_mode
|
|
70
|
+
when 'color' then content_tag(:span, inner, class: 'tag-label tag-label-color', style: "background-color: #{color}")
|
|
71
|
+
when 'plain' then content_tag(:span, inner, class: 'tag-label tag-label-plain')
|
|
72
|
+
else content_tag(:span, inner, class: 'tag-link')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def tag_label_link(url, tag_name, options = {})
|
|
77
|
+
options = options.dup
|
|
78
|
+
count = options.delete(:count)
|
|
79
|
+
color = options.delete(:color) || Redmineup::ActsAsTaggable::Tag.color_from_name(tag_name)
|
|
80
|
+
inner = link_to(tag_name, url, options)
|
|
81
|
+
inner << content_tag('span', "(#{count})", class: 'tag-count') if count
|
|
82
|
+
tag_label(inner, color)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def tag_label_links(tag_list, options = {}, &url_block)
|
|
86
|
+
return if tag_list.blank?
|
|
87
|
+
|
|
88
|
+
content_tag(:span, class: 'tag_list') do
|
|
89
|
+
safe_join(Array(tag_list).map { |tag| tag_label_link(url_block.call(tag), tag, options) }, tag_label_separator)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tag_sidebar_cloud(tags, key)
|
|
94
|
+
return if tags.blank?
|
|
95
|
+
|
|
96
|
+
settings = defined?(RedmineupTags::TagDisplay) ? RedmineupTags::TagDisplay : nil
|
|
97
|
+
style = settings ? settings.sidebar_style : 'cloud'
|
|
98
|
+
counts = settings ? settings.sidebar_counts? : true
|
|
99
|
+
sort_by, sort_order = settings ? [settings.sort_by, settings.sort_order] : %w[name asc]
|
|
100
|
+
|
|
101
|
+
url_method = :"#{key}_tag_url"
|
|
102
|
+
raise NoMethodError, "#{url_method} is not defined in view context" unless respond_to?(url_method, true)
|
|
103
|
+
|
|
104
|
+
tags = tags.to_a.sort_by { |tag| sort_by == 'count' ? tag.count.to_i : tag.name.downcase }
|
|
105
|
+
tags.reverse! if sort_order == 'desc'
|
|
106
|
+
group_by_letter = style == 'list' && sort_by == 'name'
|
|
107
|
+
groups = group_by_letter ? tags.group_by { |tag| tag.name.downcase.first } : { nil => tags }
|
|
108
|
+
list_el, item_el = style == 'list' ? %w[ul li] : %w[div span]
|
|
109
|
+
separator = style == 'simple_cloud' ? tag_label_separator : ' '
|
|
110
|
+
content = groups.map do |letter, grouped|
|
|
111
|
+
items = []
|
|
112
|
+
tag_cloud(grouped, (1..8).to_a) do |tag, weight|
|
|
113
|
+
items << content_tag(item_el, tag_label_link(send(url_method, tag.name), tag.name, count: (tag.count if counts && tag.count.to_i > 0), color: (tag.color if tag.respond_to?(:color))), class: "tag-nube-#{weight}", style: ('font-size: 1em;' if style == 'simple_cloud'))
|
|
114
|
+
end
|
|
115
|
+
safe_join([(content_tag(item_el, letter.upcase, class: 'letter') if letter), safe_join(items, separator)])
|
|
116
|
+
end
|
|
117
|
+
content_tag(list_el, safe_join(content), class: 'tags-cloud', style: ('text-align: left;' if style == 'simple_cloud'))
|
|
118
|
+
end
|
|
119
|
+
|
|
53
120
|
end
|
|
54
121
|
end
|
data/lib/redmineup/version.rb
CHANGED
data/redmineup.gemspec
CHANGED
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.homepage = "https://www.redmineup.com"
|
|
14
14
|
spec.license = "GPL-2.0"
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.start_with?("doc/dev/") || %w[AGENTS.md GLOSSARY.md].include?(f) }
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
@@ -44,4 +44,16 @@ class RupActsAsViewedTest < ActiveSupport::TestCase
|
|
|
44
44
|
issue.view '127.0.0.1', user
|
|
45
45
|
assert_equal [issue], Issue.find_viewed_by(user)
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
def test_viewer_subclass_loaded_before_the_macro_gets_viewings
|
|
49
|
+
Object.const_set(:TmpViewer, Class.new(ActiveRecord::Base) { self.table_name = 'users' })
|
|
50
|
+
Object.const_set(:TmpViewerChild, Class.new(TmpViewer) { has_many :own_issues, class_name: 'Issue', foreign_key: :author_id })
|
|
51
|
+
viewed = Class.new(ActiveRecord::Base) { self.table_name = 'issues'; def self.name; 'TmpViewed'; end }
|
|
52
|
+
viewed.up_acts_as_viewed(viewer_class: 'TmpViewer')
|
|
53
|
+
assert TmpViewer.reflect_on_association(:viewings)
|
|
54
|
+
assert TmpViewerChild.reflect_on_association(:viewings), 'subclass has no viewings association'
|
|
55
|
+
ensure
|
|
56
|
+
Object.send(:remove_const, :TmpViewerChild) if Object.const_defined?(:TmpViewerChild)
|
|
57
|
+
Object.send(:remove_const, :TmpViewer) if Object.const_defined?(:TmpViewer)
|
|
58
|
+
end
|
|
47
59
|
end
|
data/test/currency_test.rb
CHANGED
|
@@ -283,6 +283,13 @@ module Redmineup
|
|
|
283
283
|
assert Currency.new(:usd).decimal_places == 2
|
|
284
284
|
end
|
|
285
285
|
|
|
286
|
+
def test_format_zero_decimal_currency_has_no_fraction
|
|
287
|
+
assert_equal '₩3,500,000', Currency.format(3500000, Currency.new(:krw))
|
|
288
|
+
assert_equal '¥1,200', Currency.format(1200, Currency.new(:jpy))
|
|
289
|
+
assert_equal '₫3.500.000', Currency.format(3500000, Currency.new(:vnd))
|
|
290
|
+
assert_equal '$1,234.50', Currency.format(1234.5, Currency.new(:usd))
|
|
291
|
+
end
|
|
292
|
+
|
|
286
293
|
def test_proper_place_for_custom_currency
|
|
287
294
|
register_foo
|
|
288
295
|
assert_equal 2, Currency.new(:foo).decimal_places
|
|
@@ -29,6 +29,67 @@ class TagsHelperViewTest < ActionView::TestCase
|
|
|
29
29
|
assert_raises(NoMethodError) { tag_cloud_links(Issue.tag_counts, :unknown_xyz) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def with_tag_display(mode, style: 'cloud', counts: true)
|
|
33
|
+
settings = Module.new do
|
|
34
|
+
define_singleton_method(:mode) { mode }
|
|
35
|
+
define_singleton_method(:sidebar_style) { style }
|
|
36
|
+
define_singleton_method(:sidebar_counts?) { counts }
|
|
37
|
+
define_singleton_method(:sort_by) { 'name' }
|
|
38
|
+
define_singleton_method(:sort_order) { 'asc' }
|
|
39
|
+
end
|
|
40
|
+
created = !Object.const_defined?(:RedmineupTags)
|
|
41
|
+
Object.const_set(:RedmineupTags, Module.new) if created
|
|
42
|
+
RedmineupTags.const_set(:TagDisplay, settings)
|
|
43
|
+
yield
|
|
44
|
+
ensure
|
|
45
|
+
RedmineupTags.send(:remove_const, :TagDisplay) if RedmineupTags.const_defined?(:TagDisplay, false)
|
|
46
|
+
Object.send(:remove_const, :RedmineupTags) if created
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_tag_label_without_tags_plugin_is_color
|
|
50
|
+
result = tag_label('x', '#112233')
|
|
51
|
+
assert_includes result, 'tag-label tag-label-color'
|
|
52
|
+
assert_includes result, 'background-color: #112233'
|
|
53
|
+
assert_equal ' ', tag_label_separator
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_tag_label_modes
|
|
57
|
+
with_tag_display('plain') do
|
|
58
|
+
assert_includes tag_label('x', '#112233'), 'tag-label tag-label-plain'
|
|
59
|
+
assert_not_includes tag_label('x', '#112233'), 'background-color'
|
|
60
|
+
end
|
|
61
|
+
with_tag_display('link') do
|
|
62
|
+
assert_equal '<span class="tag-link">x</span>', tag_label('x', '#112233')
|
|
63
|
+
assert_equal ', ', tag_label_separator
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_tag_label_link_and_links
|
|
68
|
+
result = tag_label_link('/tags/error', 'error', count: 3, color: '#abcdef', class: 'x')
|
|
69
|
+
assert_includes result, 'background-color: #abcdef'
|
|
70
|
+
assert_includes result, '(3)'
|
|
71
|
+
assert_includes result, 'class="x"'
|
|
72
|
+
links = tag_label_links(%w[a b]) { |t| "/tags/#{t}" }
|
|
73
|
+
assert_includes links, 'class="tag_list"'
|
|
74
|
+
assert_includes links, '/tags/a'
|
|
75
|
+
assert_nil tag_label_links([]) { |t| "/tags/#{t}" }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_tag_sidebar_cloud_styles
|
|
79
|
+
tags = Issue.tag_counts
|
|
80
|
+
assert_nil tag_sidebar_cloud([], :stub)
|
|
81
|
+
assert_includes tag_sidebar_cloud(tags, :stub), 'class="tags-cloud"'
|
|
82
|
+
with_tag_display('plain', style: 'list') do
|
|
83
|
+
result = tag_sidebar_cloud(tags, :stub)
|
|
84
|
+
assert_includes result, '<ul'
|
|
85
|
+
assert_includes result, 'class="letter"'
|
|
86
|
+
end
|
|
87
|
+
with_tag_display('color', counts: false) do
|
|
88
|
+
assert_not_includes tag_sidebar_cloud(tags, :stub), 'tag-count'
|
|
89
|
+
end
|
|
90
|
+
assert_raises(NoMethodError) { tag_sidebar_cloud(tags, :unknown_xyz) }
|
|
91
|
+
end
|
|
92
|
+
|
|
32
93
|
def test_tag_list_field_tag
|
|
33
94
|
result = tag_list_field_tag('issue[tag_list]', ['bug'],
|
|
34
95
|
url: '/auto_completes/taggable_tags',
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: redmineup
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.16
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RedmineUP
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -131,11 +131,56 @@ files:
|
|
|
131
131
|
- app/views/redmineup/_money.html.erb
|
|
132
132
|
- app/views/redmineup/settings.html.erb
|
|
133
133
|
- config/currency_iso.json
|
|
134
|
+
- config/locales/ar.yml
|
|
135
|
+
- config/locales/az.yml
|
|
136
|
+
- config/locales/bg.yml
|
|
137
|
+
- config/locales/bs.yml
|
|
138
|
+
- config/locales/ca.yml
|
|
134
139
|
- config/locales/cs.yml
|
|
140
|
+
- config/locales/da.yml
|
|
135
141
|
- config/locales/de.yml
|
|
142
|
+
- config/locales/el.yml
|
|
143
|
+
- config/locales/en-GB.yml
|
|
136
144
|
- config/locales/en.yml
|
|
145
|
+
- config/locales/es-PA.yml
|
|
137
146
|
- config/locales/es.yml
|
|
147
|
+
- config/locales/et.yml
|
|
148
|
+
- config/locales/eu.yml
|
|
149
|
+
- config/locales/fa.yml
|
|
150
|
+
- config/locales/fi.yml
|
|
151
|
+
- config/locales/fr.yml
|
|
152
|
+
- config/locales/gl.yml
|
|
153
|
+
- config/locales/he.yml
|
|
154
|
+
- config/locales/hr.yml
|
|
155
|
+
- config/locales/hu.yml
|
|
156
|
+
- config/locales/id.yml
|
|
157
|
+
- config/locales/it.yml
|
|
158
|
+
- config/locales/ja.yml
|
|
159
|
+
- config/locales/ko.yml
|
|
160
|
+
- config/locales/lt.yml
|
|
161
|
+
- config/locales/lv.yml
|
|
162
|
+
- config/locales/mk.yml
|
|
163
|
+
- config/locales/mn.yml
|
|
164
|
+
- config/locales/nl.yml
|
|
165
|
+
- config/locales/no.yml
|
|
166
|
+
- config/locales/pl.yml
|
|
167
|
+
- config/locales/pt-BR.yml
|
|
168
|
+
- config/locales/pt.yml
|
|
169
|
+
- config/locales/ro.yml
|
|
138
170
|
- config/locales/ru.yml
|
|
171
|
+
- config/locales/sk.yml
|
|
172
|
+
- config/locales/sl.yml
|
|
173
|
+
- config/locales/sq.yml
|
|
174
|
+
- config/locales/sr-YU.yml
|
|
175
|
+
- config/locales/sr.yml
|
|
176
|
+
- config/locales/sv.yml
|
|
177
|
+
- config/locales/ta-IN.yml
|
|
178
|
+
- config/locales/th.yml
|
|
179
|
+
- config/locales/tr.yml
|
|
180
|
+
- config/locales/uk.yml
|
|
181
|
+
- config/locales/vi.yml
|
|
182
|
+
- config/locales/zh-TW.yml
|
|
183
|
+
- config/locales/zh.yml
|
|
139
184
|
- config/routes.rb
|
|
140
185
|
- doc/CHANGELOG
|
|
141
186
|
- doc/LICENSE.txt
|