├── .vscode
└── ltex.dictionary.en-US.txt
├── CHANGELOG.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── app
├── controllers
│ ├── rdb_dashboard_controller.rb
│ └── rdb_taskboard_controller.rb
├── helpers
│ └── rdb_dashboard_helper.rb
├── models
│ ├── rdb_assignee_filter.rb
│ ├── rdb_category_filter.rb
│ ├── rdb_column.rb
│ ├── rdb_dashboard.rb
│ ├── rdb_filter.rb
│ ├── rdb_group.rb
│ ├── rdb_taskboard.rb
│ ├── rdb_tracker_filter.rb
│ └── rdb_version_filter.rb
└── views
│ └── rdb_dashboard
│ ├── _footer.html.slim
│ ├── _taskboard.html.slim
│ ├── configure.html.slim
│ ├── error.js.erb
│ ├── index.html.slim
│ ├── index.js.erb
│ ├── issues
│ ├── _card.html.slim
│ ├── _compact.html.slim
│ ├── _issue.html.slim
│ ├── _issue_menu.html.slim
│ ├── _issue_properties.html.slim
│ └── _parent.html.slim
│ └── taskboard
│ ├── _column.html.slim
│ ├── _column_dialog.html.slim
│ ├── _column_names.html.slim
│ ├── _columns.html.slim
│ ├── _group.html.slim
│ ├── _groups.html.slim
│ ├── _header.html.slim
│ ├── _overall_progress.html.slim
│ └── column_dialog.js.erb
├── assets
├── javascripts
│ ├── dashboard.js
│ ├── dashboard.taskboard.js
│ ├── dashboard.ui.js
│ ├── jquery.autoellipsis.js
│ └── jquery.total-storage.js
└── stylesheets
│ ├── dashboard.css
│ ├── dashboard.issues.css
│ ├── dashboard.taskboard.css
│ ├── dashboard.ui.css
│ └── img
│ ├── brick.png
│ ├── cog.png
│ ├── disabled_true.png
│ ├── menu.gif
│ ├── package.png
│ ├── ticket.png
│ ├── time.png
│ └── user.png
├── config
├── default.yml
├── locales
│ ├── bg.yml
│ ├── ca.yml
│ ├── cs.yml
│ ├── de.yml
│ ├── en.yml
│ ├── es.yml
│ ├── fr.yml
│ ├── it.yml
│ ├── ja.yml
│ ├── ko.yml
│ ├── mn.yml
│ ├── nl.yml
│ ├── pl.yml
│ ├── pt-BR.yml
│ ├── ru.yml
│ ├── tr.yml
│ ├── uk.yml
│ ├── zh-TW.yml
│ └── zh.yml
└── routes.rb
└── init.rb
/.vscode/ltex.dictionary.en-US.txt:
--------------------------------------------------------------------------------
1 | Redmine
2 | redmine
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | All notable changes to this project will be documented in this file.
4 | This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Changelog](http://keepachangelog.com/).
5 |
6 | <!-- markdownlint-disable MD024 -->
7 |
8 | ## Unreleased
9 |
10 | ---
11 |
12 | ### New
13 |
14 | ### Changes
15 |
16 | ### Fixes
17 |
18 | ### Breaks
19 |
20 | ## 2.16.0 - (2025-01-17)
21 |
22 | ---
23 |
24 | ### New
25 |
26 | - Support for Redmine 6.0 by @jgraichen
27 | - Support for Redmine 5.2 by @jgraichen
28 |
29 | ### Changes
30 |
31 | - Replaced deprecated functions in new Redmine versions by @jgraichen
32 |
33 | ### Breaks
34 |
35 | - Require Ruby 3.0+ by @jgraichen
36 | - Require Redmine 5.0+ by @jgraichen
37 |
38 | ## 2.15.0 - (2023-10-31)
39 |
40 | ---
41 |
42 | ### New
43 |
44 | - Support for Redmine 5.1 by @jgraichen
45 |
46 | ### Fixes
47 |
48 | - Workaround a change in Redmine 5.1 to ignore roles with only the `:edit_own_issues` permission by @jgraichen
49 |
50 | ## 2.14.0 - (2023-03-26)
51 |
52 | ---
53 |
54 | ### New
55 |
56 | - Add Ukrainian locale by @serhiyraskoley
57 |
58 | ## 2.13.0 - (2023-03-19)
59 |
60 | ---
61 |
62 | ### New
63 |
64 | - Support `:edit_own_issues` permissions on dashboards by @jgraichen
65 |
66 | ### Changes
67 |
68 | - Wrap issue move into transaction to ensure atomic changes by @jgraichen
69 |
70 | ### Fixes
71 |
72 | - Error message when moving issue failed by @jgraichen
73 | - Do not show draggable issues when allowed by workflow but not permissions by @jgraichen
74 |
75 | ## 2.12.4 - (2023-02-10)
76 |
77 | ---
78 |
79 | ### Changes
80 |
81 | - Improve Gemfile for broken docker container environments by @jgraichen
82 |
83 | ## 2.12.3 - (2022-11-20)
84 |
85 | ---
86 |
87 | ### Fixes
88 |
89 | - I18n.t requires keyword arguments (#285) by @jgraichen
90 |
91 | ## 2.12.2 - (2022-07-09)
92 |
93 | ---
94 |
95 | ### Changes
96 |
97 | - Call issue edit hooks before save hook (#260) by @salmanmp
98 |
99 | ### Fixes
100 |
101 | - Fix missing pluralization keys (#233)
102 | - Fix wrong count placeholder in pt-BR locale (#262)
103 |
104 | ## 2.12.1 - (2022-05-03)
105 |
106 | ---
107 |
108 | ### Fixes
109 |
110 | - Zeitwerk loading issue with Redmine 5.0
111 |
112 | ## 2.12.0 - (2022-03-29)
113 |
114 | ---
115 |
116 | ### New
117 |
118 | - Support for Ruby 3.0 and 3.1 with Redmine 5.0
119 | - Support for Redmine 5.0
120 | - Show or hide spend time and estimation based on the viewers permissions (#90)
121 |
122 | ### Fixes
123 |
124 | - Locale code file mapping for pt-BR and zh-TW
125 | - Spacing and padding issues with compact card properties
126 |
127 | ## 2.11.0 - (2021-08-22)
128 |
129 | ### New
130 |
131 | - Use CSS native grid for dynamic columns and card width
132 | - Show status name as done column title if only a single closed status is shown
133 | - Include shared versions into filter (#79)
134 | - Only show issue statuses for trackers selected in filter (#108)
135 | - Limit shown statuses to the one available in the project (#121, #127, #130)
136 | - Store dashboard settings in user preferences (#39)
137 |
138 | ### Changes
139 |
140 | - Sort versions in filter (#78)
141 |
142 | ### Fixes
143 |
144 | - Fixed invisible subject in compact card layout
145 | - Duplicated filter items when including subprojects
146 | - Fix possible session cookie overflow as dashboard settings are no longer store in the session (#144)
147 |
148 | ## 2.10.0 — (2021-07-10)
149 |
150 | ### New
151 |
152 | - Sort groups similar to nested project when grouping by projects (#122)
153 |
154 | ### Changes
155 |
156 | - Diverse performance improvements when querying issues and rendering the dashboard
157 |
158 | ### Fixes
159 |
160 | - Internal server error when grouping by priority (#133)
161 | - Ajax Error: Internal Server Error when grouping by parent issue (#142)
162 |
163 | ## 2.9.0
164 |
165 | ### Changes
166 |
167 | - New Catalan and Dutch locale
168 | - Updated Turkish locale
169 | - Dropped old Redmine (< 4.0) and Ruby (< 2.5) versions from automated testing
170 |
171 | ## 2.8.0
172 |
173 | ### Changes
174 |
175 | - Test with Redmine 4.0 and 4.1
176 | - Add new locales bg and pt_BR
177 | - Add support for Redmine 4.0 (experimental)
178 | - Drop old Ruby (< 2.3) and Redmine (< 3.4) versions from automated testing
179 |
180 | ## 2.7.1
181 |
182 | ### Changes
183 |
184 | - Update locale files (new: ko)
185 |
186 | ## 2.7.0
187 |
188 | ### Changes
189 |
190 | - Add Mongolian locale
191 | - Update Russian locale
192 | - Add Turkish locale (#102)
193 | - Fix issue with version filter on parent projects (#103, #96)
194 | - Fix/Add overdue handling and styling (#104)
195 | - Small fix for group by parent issue
196 |
197 | ## 2.6.0
198 |
199 | ### Changes
200 |
201 | - Update for Redmine 3.0
202 |
203 | ## 2.5.0
204 |
205 | ### Changes
206 |
207 | - Czech translations
208 | - Global YAML configuration file for default view options
209 |
210 | ## 2.4.0
211 |
212 | ### Changes
213 |
214 | - Integration with ISSUE-ID plugin (#64)
215 | - Update locales (including new Polish translation)
216 |
217 | ## 2.3.3
218 |
219 | ### Changes
220 |
221 | - Fix error with pluralization patch (#53)
222 | - Update locales
223 | - Bundle transifex utility as gem instead of git repo
224 |
225 | ## 2.3.2
226 |
227 | ### Changes
228 |
229 | - Fix error with missing pluralization keys (#50):
230 | Redmine I18n backend does not include a CLDR pluralization rule aware
231 | pluralize method. Rdb now patches the backend to include
232 | I18n::Backend::Pluralization as well as Redmines lazy locale load to
233 | load the pluralization rules.
234 | - Update locales
235 |
236 | ## 2.3.1
237 |
238 | ### Changes
239 |
240 | - Fix version number
241 |
242 | ## 2.3.0
243 |
244 | ### Changes
245 |
246 | - New translations
247 |
248 | ## 2.2.0
249 |
250 | ### Changes
251 |
252 | - Add support for subprojects
253 |
254 | ## 2.1.0
255 |
256 | ### Changes
257 |
258 | - Include groups to assignee filter (#9)
259 | - Add swimlane for each team member instead of "others" (#18)
260 | - Several bug fixes
261 |
262 | ## 2.0 (2.0.dev)
263 |
264 | ### Changes
265 |
266 | - New release for Redmine 2.1+
267 | - Improve grouping, swimlanes, filters
268 | - New drop-down menus
269 | - Quick issue editing (progress & assignee only)
270 | - Workflow based column drag'n'drop
271 |
272 | ## 1.4
273 |
274 | ### Changes
275 |
276 | - Initial release for Redmine 1.4
277 | - Simple task board
278 | - Simple filter and grouping options
279 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 | # Gems required by redmine_dashboard
5 |
6 | send :ruby, RUBY_VERSION if ENV['CI']
7 |
8 | gem 'rake'
9 | gem 'slim-rails'
10 |
11 | group :test do
12 | gem 'database_cleaner-active_record', '~> 2.0'
13 |
14 | gem 'rspec', '~> 3.10'
15 | gem 'rspec-rails'
16 |
17 | # Redmine already defines capybara and puma, which required to run
18 | # browser tests.
19 | end
20 |
21 | # If rubocop is already defined, the Gemfile is loaded through Redmine's
22 | # own Gemfile as a plugin. In that case our local development gems are
23 | # not needed (and actually conflicting), therefore we skip them.
24 | if @dependencies.none? {|d| d.name == 'rubocop' }
25 | group :development do
26 | gem 'rubocop', '~> 1.81.0'
27 | gem 'rubocop-performance', '~> 1.26.0'
28 | gem 'rubocop-rails', '~> 2.33.0'
29 | gem 'slim_lint', '~> 0.33.0'
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Redmine Dashboard 2
2 |
3 | [](https://github.com/jgraichen/redmine_dashboard/releases/latest)
4 | [](https://github.com/jgraichen/redmine_dashboard/actions)
5 | [](http://www.redmine.org/plugins/redmine-dashboard)
6 | [](https://twitter.com/RmDashboard)
7 |
8 | This [Redmine](http://redmine.org) plugin adds an issue dashboard that supports drag and drop for issues and various filters.
9 |
10 | **Redmine Dashboard 2** is compatible and tested with Redmine 6.0, 5.1, 5.0, and Ruby 3.3, 3.2, 3.1, and 3.0.
11 |
12 | 
13 |
14 | ## Features List
15 |
16 | * Drag-n-drop of issues
17 | * Configurable columns
18 | * Grouping & Filtering
19 | * Group folding
20 | * Hierarchical parent issue view
21 | * Include subproject issues
22 | * Quick edit of assignee and progress
23 |
24 | Rate plugin at [redmine.org](http://www.redmine.org/plugins/redmine-dashboard).
25 |
26 | ## Questions? Stories?
27 |
28 | Please ask your questions, or tell us your stories or experience on [GitHub Discussions](https://github.com/jgraichen/redmine_dashboard/discussions).
29 |
30 | ## Install
31 |
32 | 1. Download the [latest release](https://github.com/jgraichen/redmine_dashboard/releases).
33 | 2. Extract archive to `<redmine>/plugins`. Make **sure** the plugin directory is called `<redmine>/plugins/redmine_dashboard/` ([#11](https://github.com/jgraichen/redmine_dashboard/issues/11)).
34 | 3. Install required dependencies by running `bundle install --without development test` in your Redmine directory. **Note**: Bitnami and other appliance are not officially supported and may need additional option e.g. `--path vendor/bundle` ([#58](https://github.com/jgraichen/redmine_dashboard/issues/58)).
35 | 4. A database migration is not needed. Restart Redmine.
36 |
37 | ### Configure Redmine
38 |
39 | 1. Add the dashboard module to your project (`Settings > Modules`).
40 | 2. Configure dashboard permissions to your user roles (`Administration > Roles and permissions`). Users won't see a Dashboard tab without having the `View Dashboard` permission.
41 |
42 | ### Upgrade
43 |
44 | 1. Remove old plugin directory.
45 | 2. Follow installation steps for new release.
46 |
47 | ## Contribute
48 |
49 | I appreciate any help and like Pull Requests. The `main` branch is the current stable branch for v2. The next version, Redmine Dashboard 3, a complete rewrite had been under development on the `develop` branch. Due to limited available time the project is in maintenance only mode but open to new contributors.
50 |
51 | I gladly accept new translations or language additions for any version of Redmine Dashboard. I would prefer new translations via [Transifex](https://www.transifex.com/projects/p/redmine-dashboard/), but you can also send a Pull Request. Feel free to request new languages or to join the team directly on Transifex.
52 |
53 | ## License
54 |
55 | Redmine dashboard is licensed under the Apache License, Version 2.0.
56 | See LICENSE for more information.
57 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'pathname'
4 | require 'yaml'
5 |
6 | require 'rspec/core/rake_task'
7 |
8 | task default: [:spec]
9 |
10 | RSpec::Core::RakeTask.new(:spec) do |t|
11 | t.pattern = ENV.fetch('SPEC', 'spec/**/*_spec.rb')
12 | t.ruby_opts = '-Ispec'
13 | t.rspec_opts = '--color --backtrace'
14 | t.rspec_opts << " --seed #{ENV['SEED']}" if ENV['SEED']
15 | end
16 |
17 | namespace :i18n do
18 | desc 'Fix locale files after tx pull'
19 | task :fix do
20 | Pathname.glob('config/locales/*.yml').each do |file|
21 | data = YAML.safe_load(file.read)
22 |
23 | # Transifex exports data inside the YAML using underscored
24 | # identifiers (e.g. pt_BR), but Redmine needs dashed identifiers
25 | # (pt-bR). We need convert each top-level key to use dashes.
26 | #
27 | # We cannot modify a hash while iterating using `#each_key`.
28 | # Therefore, we must us `keys.each` here.
29 | data.keys.each do |key|
30 | if key.include?('_')
31 | data[key.tr('_', '-')] = data.delete(key)
32 | end
33 | end
34 |
35 | fix_pluralizations = lambda do |locale_data|
36 | # Redmine does not contain pluralization rules, but many
37 | # languages only need an `other` key according to Unicode. Tools
38 | # like transifex expect proper pluralization rules and do not
39 | # export a `one` key when only an `other` key is needed, such as
40 | # for Japanese.
41 | #
42 | # This method searches the locale data for any nested dictionary
43 | # that looks like a pluralized section (contains only `one`,
44 | # `few`, `other` `many`, `zero` keys). If found, an `one` key
45 | # will be added with the `other` value.
46 | locale_data.each_value do |value|
47 | next unless value.is_a?(Hash)
48 |
49 | if (value.keys - %w[one few other many zero]).empty?
50 | value['one'] ||= value['other'] if value.key?('other')
51 | else
52 | fix_pluralizations.call(value)
53 | end
54 | end
55 | end
56 |
57 | fix_pluralizations.call(data)
58 |
59 | file.write(YAML.dump(data, line_width: -1))
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/app/controllers/rdb_dashboard_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbDashboardController < ApplicationController
4 | menu_item :dashboard
5 | before_action :find_project, :authorize
6 | before_action :setup_board, except: :index
7 | before_action :find_issue, only: %i[move update]
8 | before_action :authorize_edit, only: %i[move update]
9 | after_action :save_board_options
10 |
11 | def index
12 | return redirect_to rdb_taskboard_url if params[:controller] == 'rdb_dashboard'
13 |
14 | setup_board
15 | end
16 |
17 | def filter
18 | render action: 'index'
19 | end
20 |
21 | def update
22 | render_404
23 | end
24 |
25 | def move
26 | render_404
27 | end
28 |
29 | private
30 |
31 | def board_type
32 | nil
33 | end
34 |
35 | def board
36 | board_type.new(@project, options_for(board_type.name), params)
37 | end
38 |
39 | def setup_board
40 | return render_404 unless (@board = board)
41 |
42 | @board.setup(params)
43 | @board.update(params)
44 | @board.build
45 | @board
46 | end
47 |
48 | def save_board_options
49 | save_options_for(@board.options, board_type.name) if @board
50 | end
51 |
52 | def authorize_edit
53 | return true if @issue&.attributes_editable?
54 |
55 | raise Unauthorized
56 | end
57 |
58 | def find_project
59 | @project = Project.find params[:id]
60 | end
61 |
62 | def find_issue
63 | flash_error :rdb_flash_missing_lock_version and return false unless params[:lock_version]
64 |
65 | @issue = Issue.find params[:issue]
66 |
67 | if @issue.lock_version != params[:lock_version].to_i
68 | flash_error :rdb_flash_stale_object, update: true, issue: @issue.subject
69 | return false
70 | end
71 |
72 | @issue.lock_version = params[:lock_version].to_i
73 | @issue
74 | end
75 |
76 | def flash_error(sym, **options)
77 | flash.now[:rdb_error] = I18n.t(sym, **options).html_safe
78 | Rails.logger.info "Render Rdb flash error: #{sym}"
79 | if options[:update]
80 | render('index', formats: :js)
81 | else
82 | render('error', formats: :js)
83 | end
84 | end
85 |
86 | def options_for(board)
87 | User.current.pref["rdb_#{@project.id}_#{board}"] ||
88 | session["dashboard_#{@project.id}_#{User.current.id}_#{board}"] || {}
89 | end
90 |
91 | def save_options_for(options, board)
92 | User.current.pref["rdb_#{@project.id}_#{board}"] = options
93 | User.current.pref.save
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/app/controllers/rdb_taskboard_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbTaskboardController < RdbDashboardController
4 | menu_item :dashboard
5 |
6 | def board_type
7 | RdbTaskboard
8 | end
9 |
10 | def move
11 | return flash_error(:rdb_flash_invalid_request) unless (column = @board.columns[params[:column].to_s])
12 |
13 | # Get all status the user is allowed to assign and that are in the target column
14 | @statuses = @issue.new_statuses_allowed_to(User.current) & column.statuses
15 |
16 | if @statuses.empty?
17 | return flash_error :rdb_flash_illegal_workflow_action,
18 | issue: @issue.subject, source: @issue.status.name, target: column.title
19 | end
20 |
21 | # Show dialog if more than one status are available
22 | return render 'rdb_dashboard/taskboard/column_dialog' if @statuses.many?
23 |
24 | params[:status] = @statuses.first.id
25 | update
26 | end
27 |
28 | def update
29 | @issue.init_journal(User.current, params[:notes] || nil)
30 |
31 | @issue.done_ratio = params[:done_ratio].to_i if params[:done_ratio]
32 | @issue.assigned_to_id = nil if params[:unassigne_me] && @issue.assigned_to_id == User.current.id
33 | @issue.assigned_to_id = User.current.id if params[:assigne_me]
34 |
35 | if params[:status]
36 | status = IssueStatus.find params[:status].to_i
37 | if @issue.new_statuses_allowed_to(User.current).include?(status)
38 | @issue.status = status
39 | @issue.assigned_to_id = User.current.id if @board.options[:change_assignee]
40 | else
41 | return flash_error :rdb_flash_illegal_workflow_action,
42 | issue: @issue.subject, source: @issue.status.name, target: @status.name
43 | end
44 | end
45 |
46 | Issue.transaction do
47 | call_hook(
48 | :controller_issues_edit_before_save,
49 | {
50 | params: {},
51 | issue: @issue,
52 | journal: @issue.current_journal
53 | },
54 | )
55 |
56 | if @issue.save
57 | call_hook(
58 | :controller_issues_edit_after_save,
59 | {
60 | params: {},
61 | issue: @issue,
62 | journal: @issue.current_journal
63 | },
64 | )
65 | else
66 | raise ActiveRecord::Rollback
67 | end
68 | end
69 |
70 | render 'index'
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/app/helpers/rdb_dashboard_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RdbDashboardHelper
4 | def render_rdb_menu(id, title, options = {}, &container)
5 | options[:class] ||= ''
6 | options[:class] += ' rdb-menu-right' if options[:right]
7 | options[:class] += ' rdb-small' if options[:small]
8 |
9 | slim_tag :div, class: "rdb-menu rdb-menu-#{id} #{options[:class]}" do
10 | if options[:anchor]
11 | link = options[:anchor].call.to_s.html_safe
12 | else
13 | link = link_to(title, '#', class: 'rdb-menu-link')
14 | end
15 |
16 | if options[:header] && %i[h1 h2 h3 h4 h5].include?(options[:header].to_sym)
17 | slim_tag(options[:header], link)
18 | else
19 | concat link
20 | end
21 |
22 | slim_tag :div, class: "rdb-container #{'rdb-container-right' if options[:right]}" do
23 | slim_tag :div, class: "rdb-container-wrapper #{'rdb-icons' if options[:icons]}" do
24 | if options[:inlet]
25 | slim_tag(:div, class: 'rdb-container-inlet', &container)
26 | else
27 | yield
28 | end
29 | end
30 | end
31 | end
32 | end
33 |
34 | def render_rdb_menu_list(items = nil, options = {})
35 | if items.is_a?(Hash)
36 | options = items
37 | items = nil
38 | end
39 |
40 | slim_tag :div, class: "rdb-list #{'rdb-async' if options[:async]} #{options[:class]}" do
41 | slim_tag :h3, options[:title] if options[:title]
42 | slim_tag options[:list_tag] || :ul, class: options[:list_class] do
43 | if items
44 | items.each do |item|
45 | slim_tag :li do
46 | yield item
47 | end
48 | end
49 | else
50 | yield
51 | end
52 | end
53 | end
54 | end
55 |
56 | def render_rdb_menu_list_item(options = {}, &block)
57 | slim_tag(:li, class: options[:async] ? 'rdb-async' : '', &block)
58 | end
59 |
60 | def rdb_checkbox_link_to(*args)
61 | options = args.extract_options!
62 | options[:class] ||= ''
63 | options[:class] += ' rdb-checkbox-link'
64 | options[:class] += ' rdb-checkbox-link-enabled' if options[:enabled]
65 | options[:class] += ' rdb-checkbox-link-disabled' if !options[:enabled] && options[:show_disabled]
66 | options[:class].strip!
67 |
68 | options.delete :enabled
69 | options.delete :show_disabled
70 |
71 | args << options
72 |
73 | link_to(*args)
74 | end
75 |
76 | def rdb_update_path(issue, options = {})
77 | send(:"rdb_#{@board.id}_update_path", {
78 | issue: issue.id,
79 | lock_version: issue.lock_version
80 | }.merge(options),)
81 | end
82 |
83 | def rdb_move_path(issue, options = {})
84 | send(:"rdb_#{@board.id}_move_path", {
85 | issue: issue.id,
86 | lock_version: issue.lock_version
87 | }.merge(options),)
88 | end
89 |
90 | def rdb_filter_path(options = {})
91 | send(:"rdb_#{@board.id}_filter_path", options)
92 | end
93 |
94 | def rdb_board_path(options = {})
95 | send(:"rdb_#{@board.id}_path", options)
96 | end
97 |
98 | private
99 |
100 | def slim_tag(name, content = nil, **attrs)
101 | concat tag(name, attrs, true, true)
102 | concat(content) if content.present?
103 | yield if block_given?
104 | concat "</#{name}>".html_safe
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/app/models/rdb_assignee_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbAssigneeFilter < RdbFilter
4 | def initialize
5 | super(:assignee)
6 | end
7 |
8 | def scope(scope)
9 | case value
10 | when :me then scope.where(assigned_to_id: User.current.id)
11 | when :none then scope.where(assigned_to_id: nil)
12 | when :all then scope
13 | else scope.where(assigned_to_id: value)
14 | end
15 | end
16 |
17 | def apply_to_child_issues?
18 | true
19 | end
20 |
21 | def default_values
22 | [RdbDashboard.defaults[:assignee] || :me]
23 | end
24 |
25 | def update(params)
26 | return unless (assignee = params[:assignee])
27 |
28 | Rails.logger.warn "CHANGE ASSIGNE: #{assignee}"
29 |
30 | if (assignee == 'all') || (assignee == 'me') || (assignee == 'none')
31 | self.value = assignee.to_sym
32 | elsif board.assignees.where(id: assignee.to_i).any?
33 | self.value = assignee.to_i
34 | end
35 |
36 | Rails.logger.warn "CHANGE ASSIGNE: #{assignee} => #{values.inspect}"
37 | end
38 |
39 | def title
40 | case value
41 | when :all then I18n.t(:rdb_filter_assignee_all)
42 | when :me then I18n.t(:rdb_filter_assignee_me)
43 | when :none then I18n.t(:rdb_filter_assignee_none)
44 | else
45 | values.map {|id| board.assignees.find(id) }.map(&:name).join(', ')
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/models/rdb_category_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbCategoryFilter < RdbFilter
4 | def initialize
5 | super(:category)
6 | end
7 |
8 | def scope(scope)
9 | return scope if all?
10 |
11 | scope.where category_id: values
12 | end
13 |
14 | def all?
15 | values.count >= board.issue_categories.count or board.issue_categories.empty?
16 | end
17 |
18 | def default_values
19 | board.issue_categories.pluck(:id)
20 | end
21 |
22 | def valid_value?(value)
23 | default_values.include? value.to_i
24 | end
25 |
26 | def update(params)
27 | return unless (category = params[:category])
28 |
29 | if category == 'all'
30 | self.values = board.issue_categories.pluck(:id)
31 | else
32 | id = category.to_i
33 |
34 | if params[:only]
35 | self.value = id
36 | elsif values.include?(id) && values.count > 2
37 | values.delete id
38 | elsif valid_value?(id)
39 | values << id
40 | end
41 | end
42 | end
43 |
44 | def title
45 | return I18n.t(:rdb_filter_category_all) if all?
46 | return I18n.t(:rdb_filter_category_multiple) if values.many?
47 |
48 | board.issue_categories.find_by(id: value).try(:name)
49 | end
50 |
51 | def enabled?(id)
52 | return true if value == :all
53 |
54 | values.include? id
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/app/models/rdb_column.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbColumn
4 | attr_accessor :board
5 | attr_reader :name, :options, :id, :statuses
6 |
7 | def initialize(id, name, statuses, options = {})
8 | @id = id.to_s
9 | @name = name
10 | @statuses = statuses.is_a?(Array) ? statuses : [statuses]
11 | @options = options
12 | end
13 |
14 | def scope(issue_scope)
15 | issue_scope.where status_id: statuses.map(&:id)
16 | end
17 |
18 | def issues
19 | @issues ||= board.issues_for(self)
20 | end
21 |
22 | def percentage
23 | all_issue_count = board.issues.count
24 | all_issue_count > 0 ? ((issues.count.to_f / all_issue_count) * 100).round(4) : 0
25 | end
26 |
27 | def title
28 | name.is_a?(Symbol) ? I18n.t(name) : name.to_s
29 | end
30 |
31 | def compact?
32 | !!options[:compact]
33 | end
34 |
35 | def visible?
36 | !options[:hide]
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/models/rdb_dashboard.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbDashboard
4 | attr_reader :project, :options
5 |
6 | VIEW_MODES = %i[card compact].freeze
7 |
8 | def initialize(project, opts, params = nil)
9 | @project = project
10 | @options = {filters: {}}
11 |
12 | options.merge! self.class.defaults
13 | options.merge! opts
14 |
15 | if params[:include_subprojects]
16 | options[:include_subprojects] = (params[:include_subprojects] == 'true')
17 | end
18 |
19 | # Init board in sub class
20 | init if respond_to? :init
21 |
22 | filters.each_value do |filter|
23 | filter.values = options[:filters][filter.id] if options[:filters][filter.id]
24 | end
25 | end
26 |
27 | def setup(params)
28 | # Update issue view mode
29 | options[:view] = params[:view].to_sym if params[:view] && RdbDashboard::VIEW_MODES.include?(params[:view].to_sym)
30 | end
31 |
32 | def update(params)
33 | if params[:reset]
34 | filters.each_value do |filter|
35 | filter.values = filter.default_values
36 | end
37 | options[:filters] = {}
38 | else
39 | filters.each_value do |filter|
40 | filter.update params if params
41 | options[:filters][filter.id] = filter.values
42 | end
43 | end
44 | end
45 |
46 | def issue_view
47 | options[:view]
48 | end
49 |
50 | def projects
51 | @projects ||= Project.where(
52 | project.project_condition(options[:include_subprojects]),
53 | )
54 | end
55 |
56 | def project_ids
57 | @project_ids ||= projects.pluck(:id)
58 | end
59 |
60 | def issues
61 | filter Issue
62 | .where(project_id: project_ids)
63 | .includes(:assigned_to, :time_entries, :tracker, :status, :priority, :fixed_version)
64 | end
65 |
66 | def versions
67 | @versions ||= begin
68 | version_ids = project.shared_versions.pluck(:id)
69 |
70 | if options[:include_subprojects]
71 | version_ids += project.rolled_up_versions.pluck(:id)
72 | end
73 |
74 | Version.where(id: version_ids.uniq).sorted
75 | end
76 | end
77 |
78 | def issue_categories
79 | @issue_categories ||= IssueCategory.where(project_id: project_ids).distinct
80 | end
81 |
82 | def trackers
83 | @trackers ||= Tracker.where(
84 | id: Tracker.joins(:projects).where(projects: {id: project_ids}).distinct,
85 | ).sorted
86 | end
87 |
88 | def assignees
89 | @assignees ||= Principal.where(
90 | id: Principal.active.joins(:memberships).where(members: {project_id: project_ids}).distinct,
91 | ).sorted
92 | end
93 |
94 | def filter(issues)
95 | issues = filters.inject(issues) {|issues, filter| filter[1].scope issues }
96 | filters.inject(issues) {|issues, filter| filter[1].filter issues }
97 | end
98 |
99 | def abbreviation(project_id)
100 | project_id ||= project.id
101 |
102 | @abbreviations ||= []
103 | @abbreviations[project_id] ||= begin
104 | abbreviation = '#'
105 | Project.find(project_id).custom_field_values.each do |f|
106 | if f.to_s.blank? && f.custom_field.read_attribute(:name).casecmp('abbreviation').zero?
107 | abbreviation = "#{f}-"
108 | end
109 | end
110 | abbreviation
111 | end
112 | end
113 |
114 | def issue_id(issue)
115 | if issue.respond_to?(:issue_id)
116 | issue.issue_id
117 | else
118 | abbreviation(issue.project_id) + issue.id.to_s
119 | end
120 | end
121 |
122 | def id
123 | self.class.board_type
124 | end
125 |
126 | def name
127 | I18n.t :"rdb_#{id}"
128 | end
129 |
130 | def add_filter(filter)
131 | filter.board = self
132 | filters[filter.id.to_s] = filter
133 | end
134 |
135 | def add_group(group)
136 | group.board = self
137 | group_list << group
138 | groups[group.id.to_s] = group
139 | end
140 |
141 | def editable?(str = nil)
142 | @editable ||= !User.current.allowed_to?(:edit_issues, project).nil?
143 | if str
144 | @editable ? str : false
145 | else
146 | @editable
147 | end
148 | end
149 |
150 | def filters
151 | @filters ||= ActiveSupport::HashWithIndifferentAccess.new
152 | end
153 |
154 | def groups
155 | @groups ||= ActiveSupport::HashWithIndifferentAccess.new
156 | end
157 |
158 | def group_list
159 | @group_list ||= []
160 | end
161 |
162 | class << self
163 | def board_type
164 | @board_type ||= name.downcase.to_s.gsub(/^rdb/, '').to_sym
165 | end
166 |
167 | def defaults
168 | @defaults ||= load_defaults
169 | end
170 |
171 | def load_defaults
172 | config = YAML.load_file File.expand_path('../../config/default.yml', __dir__)
173 |
174 | {
175 | view: check_opts(config, 'view', :card, :compact),
176 | include_subprojects: check_opts(config, 'include_subprojects', false, true),
177 | assignee: check_opts(config, 'assignee', :me, :all),
178 | version: check_opts(config, 'version', :latest, :all),
179 | hide_done: check_opts(config, 'hide_done', false, true),
180 | change_assignee: check_opts(config, 'change_assignee', false, true)
181 | }
182 | end
183 |
184 | def check_opts(options, name, *values)
185 | value = options.fetch(name) { return values.first }
186 | values.each do |val|
187 | return val if val == value || val.to_s == value
188 | end
189 |
190 | values.first
191 | end
192 | end
193 | end
194 |
--------------------------------------------------------------------------------
/app/models/rdb_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbFilter
4 | attr_accessor :board
5 | attr_reader :id
6 |
7 | def initialize(id)
8 | @id = id.to_sym
9 | end
10 |
11 | def default_options
12 | {}
13 | end
14 |
15 | def values
16 | @values ||= default_values
17 | end
18 |
19 | def default_values
20 | []
21 | end
22 |
23 | def value
24 | values.first
25 | end
26 |
27 | def value=(value)
28 | self.values = value ? [value] : []
29 | end
30 |
31 | def values=(values)
32 | values = [values] unless values.is_a?(Array)
33 | @values = values.select {|value| valid_value? value }
34 | @values = default_values if @values.empty? && !allow_no_values?
35 | end
36 |
37 | def valid_value?(_value)
38 | true
39 | end
40 |
41 | def allow_no_values?
42 | false
43 | end
44 |
45 | def title
46 | values.join
47 | end
48 |
49 | def to_options
50 | []
51 | end
52 |
53 | def scope(scope)
54 | scope
55 | end
56 |
57 | def filter(issues)
58 | issues
59 | end
60 |
61 | def apply_to_child_issues?
62 | false
63 | end
64 |
65 | def update(params); end
66 | end
67 |
--------------------------------------------------------------------------------
/app/models/rdb_group.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbGroup
4 | attr_accessor :board
5 | attr_reader :name, :options, :id
6 |
7 | def initialize(id, name, options = {})
8 | @id = id.to_s
9 | @name = name
10 |
11 | @options = default_options
12 | @options[:accept] = options[:accept] if options[:accept].respond_to? :call
13 | end
14 |
15 | def default_options
16 | {}
17 | end
18 |
19 | def accept?(issue)
20 | return true if options[:accept].nil?
21 |
22 | options[:accept].call(issue)
23 | end
24 |
25 | def title
26 | name.is_a?(Symbol) ? I18n.t(name) : name.to_s
27 | end
28 |
29 | def accepted_issues(source = nil)
30 | @accepted_issues ||= filter((source || board).issues)
31 | end
32 |
33 | def accepted_issue_ids
34 | @accepted_issue_ids ||= accepted_issues.map(&:id)
35 | end
36 |
37 | def filter(issues)
38 | issues.select {|i| accept? i }
39 | end
40 |
41 | def visible?
42 | @visible ||= catch(:visible) do
43 | board.columns.each_value do |column|
44 | next if !column.visible? || column.compact?
45 |
46 | throw :visible, true if filter(column.issues).any?
47 | end
48 | false
49 | end
50 | end
51 |
52 | def issue_count
53 | filter(@board.issues).count.to_i
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/app/models/rdb_taskboard.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbTaskboard < RdbDashboard
4 | def init
5 | # Init filters
6 | add_filter RdbAssigneeFilter.new
7 | add_filter RdbVersionFilter.new if versions.any?
8 | add_filter RdbTrackerFilter.new
9 | add_filter RdbCategoryFilter.new if issue_categories.any?
10 | end
11 |
12 | def setup(params)
13 | super
14 |
15 | if %w[tracker priority assignee category version parent none project].include? params[:group]
16 | options[:group] = params[:group].to_sym
17 | end
18 |
19 | if params[:hide_done]
20 | options[:hide_done] = (params[:hide_done] == 'true')
21 | end
22 |
23 | if params[:change_assignee]
24 | options[:change_assignee] = (params[:change_assignee] == 'true')
25 | end
26 |
27 | if (id = params[:hide_column])
28 | options[:hide_columns] ||= []
29 | options[:hide_columns].include?(id) ? options[:hide_columns].delete(id) : (options[:hide_columns] << id)
30 | end
31 | end
32 |
33 | def statuses
34 | ids = WorkflowTransition
35 | .where(tracker_id: filters[:tracker].values)
36 | .distinct
37 | .pluck(:old_status_id, :new_status_id)
38 | .flatten
39 | .uniq
40 |
41 | IssueStatus.where(id: ids).sorted
42 | end
43 |
44 | def build
45 | # Init columns
46 | options[:hide_columns] ||= []
47 |
48 | done_statuses = statuses.select do |status|
49 | next true if status.is_closed?
50 |
51 | add_column RdbColumn.new(
52 | "s#{status.id}",
53 | status.name,
54 | status,
55 | hide: options[:hide_columns].include?("s#{status.id}"),
56 | )
57 |
58 | false
59 | end
60 |
61 | if done_statuses.one?
62 | status = done_statuses.first
63 | add_column RdbColumn.new(
64 | "s#{status.id}",
65 | status.name,
66 | status,
67 | compact: options[:hide_done],
68 | hide: options[:hide_columns].include?("s#{status.id}"),
69 | )
70 | elsif done_statuses.any?
71 | add_column RdbColumn.new(
72 | 'sX',
73 | :rdb_column_done,
74 | done_statuses,
75 | compact: options[:hide_done],
76 | hide: options[:hide_columns].include?('sX'),
77 | )
78 | end
79 |
80 | # Init groups
81 | case options[:group]
82 | when :tracker
83 | trackers.each do |tracker|
84 | add_group RdbGroup.new(
85 | "tracker-#{tracker.id}",
86 | tracker.name,
87 | accept: proc {|issue| issue.tracker == tracker },
88 | )
89 | end
90 |
91 | when :priority
92 | IssuePriority.reorder(position: :desc).each do |p|
93 | add_group RdbGroup.new(
94 | "priority-#{p.position}",
95 | p.name,
96 | accept: proc {|issue| issue.priority_id == p.id },
97 | )
98 | end
99 |
100 | when :assignee
101 | add_group RdbGroup.new(
102 | :assigne_me,
103 | :rdb_filter_assignee_me,
104 | accept: proc {|issue| issue.assigned_to_id == User.current.id },
105 | )
106 | add_group RdbGroup.new(
107 | :assigne_none,
108 | :rdb_filter_assignee_none,
109 | accept: proc {|issue| issue.assigned_to_id.nil? },
110 | )
111 | assignees.sort_by(&:name).each do |principal|
112 | next if principal.id == User.current.id
113 |
114 | add_group RdbGroup.new(
115 | "assignee-#{id}",
116 | principal.name,
117 | accept: proc {|issue| !issue.assigned_to_id.nil? && issue.assigned_to_id == principal.id },
118 | )
119 | end
120 |
121 | when :category
122 | issue_categories.each do |category|
123 | add_group RdbGroup.new(
124 | "category-#{category.id}",
125 | category.name,
126 | accept: proc {|issue| issue.category_id == category.id },
127 | )
128 | end
129 | add_group RdbGroup.new(
130 | :category_none,
131 | :rdb_unassigned,
132 | accept: proc {|issue| issue.category.nil? },
133 | )
134 |
135 | when :version
136 | versions.each do |version|
137 | add_group RdbGroup.new(
138 | "version-#{version.id}",
139 | version.name,
140 | accept: proc {|issue| issue.fixed_version_id == version.id },
141 | )
142 | end
143 | add_group RdbGroup.new(
144 | :version_none,
145 | :rdb_unassigned,
146 | accept: proc {|issue| issue.fixed_version.nil? },
147 | )
148 |
149 | when :project
150 | projects.each do |project|
151 | add_group RdbGroup.new(
152 | "project-#{project.id}",
153 | project.name,
154 | accept: proc {|issue| issue.project_id == project.id },
155 | )
156 | end
157 |
158 | when :parent
159 | issues.where(id: issues.pluck(:parent_id).uniq).uniq.each do |issue|
160 | add_group RdbGroup.new(
161 | "issue-#{issue.id}",
162 | issue.subject,
163 | accept: proc {|sub_issue| sub_issue.parent_id == issue.id },
164 | )
165 | end
166 | add_group RdbGroup.new(
167 | 'issue-others',
168 | :rdb_no_parent,
169 | accept: proc {|issue| issue.parent.nil? },
170 | )
171 | end
172 |
173 | add_group RdbGroup.new(:all, :rdb_all_issues) if groups.empty?
174 | end
175 |
176 | # -------------------------------------------------------
177 | # Helpers
178 |
179 | def issues_for(column)
180 | filter column.scope(issues)
181 | end
182 |
183 | def columns
184 | @columns ||= ActiveSupport::HashWithIndifferentAccess.new
185 | end
186 |
187 | def column_list
188 | @column_list ||= []
189 | end
190 |
191 | def add_column(column)
192 | column.board = self
193 | column_list << column
194 | columns[column.id.to_s] = column
195 | end
196 |
197 | def visible_columns
198 | column_list.select(&:visible?)
199 | end
200 |
201 | def drop_on(issue)
202 | if User.current.admin?
203 | return column_list.reject {|c| c.statuses.include? issue.status }.map(&:id).join(' ')
204 | end
205 |
206 | return false unless issue&.attributes_editable?(User.current)
207 |
208 | statuses = issue.new_statuses_allowed_to(User.current)
209 | statuses.delete issue.status
210 | column_list.select do |c|
211 | (statuses & c.statuses).any?
212 | end.reject {|c| c.statuses.include? issue.status }.map(&:id).uniq.join(' ')
213 | end
214 | end
215 |
--------------------------------------------------------------------------------
/app/models/rdb_tracker_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbTrackerFilter < RdbFilter
4 | def initialize
5 | super(:tracker)
6 | end
7 |
8 | def scope(scope)
9 | return scope if all?
10 |
11 | scope.where tracker_id: values
12 | end
13 |
14 | def all?
15 | values.count == board.trackers.count
16 | end
17 |
18 | def apply_to_child_issues?
19 | true
20 | end
21 |
22 | def default_values
23 | board.trackers.pluck(:id)
24 | end
25 |
26 | def update(params)
27 | return unless (tracker = params[:tracker])
28 |
29 | if tracker == 'all'
30 | self.values = board.trackers.pluck(:id)
31 | return
32 | end
33 |
34 | id = tracker.to_i
35 | return if board.trackers.where(id: id).empty?
36 |
37 | if params[:only]
38 | self.value = id
39 | elsif values.include? id
40 | values.delete id if values.count > 2
41 | else
42 | values << id
43 | end
44 | end
45 |
46 | def title
47 | return I18n.t(:rdb_filter_tracker_all) if all?
48 | return I18n.t(:rdb_filter_tracker_multiple) if values.many?
49 |
50 | board.trackers.find(value).name
51 | end
52 |
53 | def enabled?(id)
54 | return true if value == :all
55 |
56 | values.include? id
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/app/models/rdb_version_filter.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class RdbVersionFilter < RdbFilter
4 | def initialize
5 | super(:version)
6 | end
7 |
8 | def scope(issues)
9 | case value
10 | when :all then issues
11 | when :unassigned then issues.where fixed_version_id: nil
12 | else issues.where fixed_version_id: value
13 | end
14 | end
15 |
16 | def valid_value?(value)
17 | return true if (value == :all) || value.nil?
18 | return false unless value.respond_to?(:to_i)
19 |
20 | board.versions.where(id: value.to_i).any?
21 | end
22 |
23 | def default_values
24 | if RdbDashboard.defaults[:version] == :latest
25 | version = board.versions
26 | .where(status: %i[open locked])
27 | .where.not(effective_date: nil)
28 | .order('effective_date ASC')
29 | .first
30 | return [version.id] unless version.nil?
31 |
32 | version = board.versions
33 | .where(status: %i[open locked])
34 | .order('name ASC')
35 | .first
36 | return [version.id] unless version.nil?
37 | end
38 |
39 | [:all]
40 | end
41 |
42 | def update(params)
43 | return unless (version = params[:version])
44 |
45 | case version
46 | when 'all'
47 | self.value = :all
48 | when 'unassigned'
49 | self.values = [nil]
50 | else
51 | self.value = version.to_i
52 | end
53 | end
54 |
55 | def title
56 | if value == :all
57 | I18n.t(:rdb_filter_version_all)
58 | elsif value.nil?
59 | I18n.t(:rdb_filter_version_unassigned)
60 | else
61 | values.map {|id| board.versions.find(id) }.map(&:name).join(', ')
62 | end
63 | end
64 |
65 | def versions
66 | board.versions.where(status: %i[open locked])
67 | end
68 |
69 | def done_versions
70 | board.versions.where(status: :closed)
71 | end
72 | end
73 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/_footer.html.slim:
--------------------------------------------------------------------------------
1 | #rdb-footer
2 | #rdb-copyright
3 | a href="https://github.com/jgraichen/redmine_dashboard"
4 | | Redmine Dashboard 2
5 | = " - v#{Redmine::Plugin.find(:redmine_dashboard).version}"
6 |
7 | #rdb-legend
8 | div
9 | p = t :rdb_legend_priorities
10 | - IssuePriority.all.sort_by(&:position).each do |priority|
11 | span class="rdb-priority rdb-priority-#{priority.position}"
12 | = priority
13 |
14 | div
15 | p = t :rdb_legend_warnings
16 | span.rdb-overdue
17 | span
18 | = t :rdb_issue_overdue
19 | .rdb-clear
20 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/_taskboard.html.slim:
--------------------------------------------------------------------------------
1 | #rdb-taskboard
2 | header#rdb-header
3 | = render partial: 'rdb_dashboard/taskboard/header'
4 | .rdb-clear
5 |
6 | #rdb-board
7 | #rdb-board-container
8 | = render partial: 'rdb_dashboard/taskboard/column_names'
9 | = render partial: 'rdb_dashboard/taskboard/overall_progress'
10 | = render partial: 'rdb_dashboard/taskboard/groups'
11 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/configure.html.slim:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/app/views/rdb_dashboard/configure.html.slim
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/error.js.erb:
--------------------------------------------------------------------------------
1 | <% if flash[:rdb_error] %>
2 | Rdb.rdbError("<%= escape_javascript(flash[:rdb_error]) %>");
3 | <% end %>
4 | Rdb.rdbDADShowIssue();
5 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/index.html.slim:
--------------------------------------------------------------------------------
1 | - content_for :header_tags do
2 | = stylesheet_link_tag 'dashboard.css', plugin: :redmine_dashboard
3 | = stylesheet_link_tag 'dashboard.ui.css', plugin: :redmine_dashboard
4 | = stylesheet_link_tag 'dashboard.issues.css', plugin: :redmine_dashboard
5 | = stylesheet_link_tag 'dashboard.taskboard.css', plugin: :redmine_dashboard
6 | = javascript_include_tag 'jquery.autoellipsis.js', plugin: :redmine_dashboard
7 | = javascript_include_tag 'jquery.total-storage.js', plugin: :redmine_dashboard
8 | = javascript_include_tag 'dashboard.js', plugin: :redmine_dashboard
9 | = javascript_include_tag 'dashboard.ui.js', plugin: :redmine_dashboard
10 | = javascript_include_tag 'dashboard.taskboard.js', plugin: :redmine_dashboard
11 |
12 | #rdb data={'rdb-base': rdb_board_path}
13 | #rdb-wrapper
14 | #rdb-header
15 | #rdb-board
16 | #rdb-loading
17 |
18 | #rdb-errors
19 | #rdb-dialogs
20 | = render partial: 'footer'
21 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/index.js.erb:
--------------------------------------------------------------------------------
1 | $('#rdb-wrapper').html("<%= escape_javascript(render partial: @board.id.to_s) %>");
2 | Rdb.rdbInit();
3 | <% if flash[:rdb_error] %>
4 | Rdb.rdbError("<%= escape_javascript(flash[:rdb_error]) %>");
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_card.html.slim:
--------------------------------------------------------------------------------
1 | / ISSUE CARD
2 | .rdb-card
3 | div class="rdb-priority rdb-priority-#{issue.priority.position}"
4 | header.rdb-card-header
5 | .rdb-card-title
6 | = render partial: 'rdb_dashboard/issues/issue_menu', locals: {issue: issue}
7 | div
8 |
9 | .rdb-card-progress
10 | .rdb-card-progress-bar style="width: #{issue.done_ratio}%"
11 |
12 | .rdb-card-content
13 | .rdb-card-subject.rdb-property-subject = issue.subject
14 | = render partial: 'rdb_dashboard/issues/issue_properties', locals: {issue: issue}
15 | / ISSUE CARD END
16 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_compact.html.slim:
--------------------------------------------------------------------------------
1 | / ISSUE COMPACT
2 | .rdb-compact
3 | .rdb-priority class="rdb-priority-#{issue.priority.position}"
4 |
5 | .rdb-compact-progress
6 | .rdb-compact-progress-bar style="width: #{issue.done_ratio}%"
7 | header.rdb-compact-header
8 | .rdb-compact-title
9 | = render partial: 'rdb_dashboard/issues/issue_menu', locals: {issue: issue}
10 | .rdb-compact-header-data
11 | = render partial: 'rdb_dashboard/issues/issue_properties', locals: {issue: issue}
12 |
13 | .rdb-compact-content
14 | .rdb-compact-subject.rdb-property-subject = issue.subject
15 | / ISSUE COMPACT END
16 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_issue.html.slim:
--------------------------------------------------------------------------------
1 | .rdb-issue(
2 | class="rdb-issue-#{@board.issue_view} #{@board.editable?('rdb-issue-drag')} #{issue.overdue? ? 'rdb-overdue' : ''}"
3 | data={'rdb-drop-on': @board.drop_on(issue), 'rdb-drop-group': group.id, 'rdb-issue-id': issue.id, 'rdb-lock-version': issue.lock_version}
4 | )
5 | = render partial: "rdb_dashboard/issues/#{@board.issue_view}", locals: {issue: issue}
6 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_issue_menu.html.slim:
--------------------------------------------------------------------------------
1 | - render_rdb_menu :issue, @board.issue_id(issue), small: true do
2 | - render_rdb_menu_list title: t(:rdb_issue_menu_redmine_issue) do
3 | - render_rdb_menu_list_item do
4 | = link_to t(:rdb_issue_menu_show), issue_path(issue)
5 | - if @board.editable?
6 | - render_rdb_menu_list_item do
7 | = link_to t(:rdb_issue_menu_edit), edit_issue_path(issue)
8 | - if issue.assigned_to_id == User.current.id
9 | - render_rdb_menu_list_item async: true do
10 | = link_to t(:rdb_issue_menu_unassign_me), rdb_update_path(issue, unassigne_me: true)
11 | - else
12 | - render_rdb_menu_list_item async: true do
13 | = link_to t(:rdb_issue_menu_assign_me), rdb_update_path(issue, assigne_me: true)
14 |
15 | - if @board.editable? && (issue.assigned_to_id == User.current.id || User.current.admin?)
16 | - render_rdb_menu_list title: t(:rdb_issue_menu_progress_title), async: true, list_tag: :div do
17 | ul.rdb-issue-menu-progress
18 | - [0, 20, 40].each do |value|
19 | li = link_to t(:rdb_issue_menu_progress, count: value), rdb_update_path(issue, done_ratio: value)
20 | ul.rdb-issue-menu-progress
21 | - [60, 80, 100].each do |value|
22 | li = link_to t(:rdb_issue_menu_progress, count: value), rdb_update_path(issue, done_ratio: value)
23 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_issue_properties.html.slim:
--------------------------------------------------------------------------------
1 | .rdb-property.rdb-property-tracker = issue.tracker
2 | - if @board.versions.any?
3 | - if issue.fixed_version
4 | .rdb-property.rdb-property-version = issue.fixed_version.name
5 | - else
6 | .rdb-property.rdb-property-version.rdb-disabled = t(:rdb_filter_version_unassigned)
7 | - if issue.assigned_to
8 | .rdb-property.rdb-property-assignee = issue.assigned_to.name
9 | - else
10 | .rdb-property.rdb-property-assignee.rdb-disabled = t(:rdb_unassigned)
11 | - if @board.issue_categories.any?
12 | - if issue.category
13 | .rdb-property.rdb-property-category = issue.category.name
14 | - else
15 | .rdb-property.rdb-property-category.rdb-disabled = t(:rdb_unassigned)
16 | - if User.current.allowed_to?(:view_time_entries, @project)
17 | .rdb-property.rdb-property-time class=(issue.estimated_hours.nil? && issue.time_entries.empty? ? 'rdb-disabled' : '')
18 | = t(:rdb_property_time, estimated: issue.estimated_hours.to_f.round(2), actual: issue.time_entries.map(&:hours).reduce(&:+).to_f.round(2)).html_safe
19 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/issues/_parent.html.slim:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/app/views/rdb_dashboard/issues/_parent.html.slim
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_column.html.slim:
--------------------------------------------------------------------------------
1 | - if issues.any? && @board.groups.count > 1
2 | .rdb-column-head = t(:rdb_x_issues, count: issues.count)
3 | div class="rdb-grid-#{@board.issue_view}"
4 | - grouped_issues = issues.group_by {|i| i.priority.position }
5 | - grouped_issues.keys.sort.reverse.each do |priority|
6 | - grouped_issues[priority].sort_by(&:id).each do |issue|
7 | = render partial: 'rdb_dashboard/issues/issue', locals: {issue: issue, group: group}
8 |
9 | / Render multple empty diff to fill-up the "grid" if
10 | / very few issues are present in a column. Otherwise
11 | / the column will not add CSS grid columns in the group
12 | / if the overall column gets wide enought.
13 | - 5.times do
14 | div
15 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_column_dialog.html.slim:
--------------------------------------------------------------------------------
1 | div
2 | .rdb-card.rdb-card-dialog
3 | .rdb-priority class="rdb-priority-#{@issue.priority.position}"
4 | header.rdb-card-header
5 | span.rdb-card-title
6 | = link_to "#{@board.abbreviation(@issue.project_id)}#{@issue.id}", @issue
7 |
8 | .rdb-card-progress
9 | .rdb-card-progress-bar style="width: #{@issue.done_ratio}%"
10 |
11 | .rdb-card-content
12 | .rdb-card-subject.rdb-property-subject = @issue.subject
13 |
14 | .rdb-card-dialog-box.rdb-async
15 | label = t(:rdb_dialog_update_issue_status)
16 | ul
17 | - @statuses.each do |status|
18 | li = link_to status.name, rdb_update_path(@issue, status: status.id)
19 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_column_names.html.slim:
--------------------------------------------------------------------------------
1 | ul.rdb-headers
2 | - @board.column_list.each do |column|
3 | - if column.visible?
4 | li.rdb-column class=(column.compact? ? 'rdb-column-compact' : '')
5 | h3 = column.title
6 | - if column.issues.count > 0
7 | span = t :rdb_x_issues, count: column.issues.count
8 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_columns.html.slim:
--------------------------------------------------------------------------------
1 | ul.rdb-columns
2 | - @board.visible_columns.each do |column|
3 | li.rdb-column class=(column.compact? ? 'rdb-column-compact' : '') data={'rdb-column-id': column.id, 'rdb-drop-group': group.id}
4 | - unless column.compact?
5 | = render partial: 'rdb_dashboard/taskboard/column', locals: {column: column, issues: group.filter(column.issues), group: group}
6 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_group.html.slim:
--------------------------------------------------------------------------------
1 | .rdb-group data-rdb-group-id=group.id
2 | .rdb-group-header
3 | a.rdb-evt-group-toggle
4 | h4 = group.title
5 | span = t(:rdb_x_issues, count: group.issue_count)
6 | = render partial: 'rdb_dashboard/taskboard/columns', locals: {group: group}
7 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_groups.html.slim:
--------------------------------------------------------------------------------
1 | .rdb-groups
2 | - if @board.groups.count == 1
3 | .rdb-group data-rdb-group-id=@board.group_list.first.id
4 | = render partial: 'rdb_dashboard/taskboard/columns', locals: {group: @board.group_list.first}
5 | - else
6 | - show_all = @board.group_list.select(&:visible?).none?
7 | - @board.group_list.each do |group|
8 | - if show_all || group.visible?
9 | = render partial: 'rdb_dashboard/taskboard/group', locals: {group: group}
10 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_header.html.slim:
--------------------------------------------------------------------------------
1 | .rdb-board
2 | = link_to '', rdb_board_path, class: 'rdb-async', id: 'rdb-refresh'
3 |
4 | - render_rdb_menu :board, @board.name, header: :h2 do
5 | - render_rdb_menu_list [:taskboard] do |val|
6 | = link_to t("rdb_#{val}"), rdb_path(board: val)
7 |
8 | - if @board.versions.any?
9 | .rdb-filter.rdb-async
10 | - render_rdb_menu :versions, @board.filters[:version].title do
11 | - render_rdb_menu_list [:all, :unassigned] do |val|
12 | = link_to t("rdb_filter_version_#{val}"), rdb_filter_path(version: val)
13 | - render_rdb_menu_list @board.filters[:version].versions do |val|
14 | = link_to val.name, rdb_filter_path(version: val.id)
15 |
16 | .rdb-filter.rdb-async
17 | - render_rdb_menu :tracker, @board.filters[:tracker].title, icons: true do
18 | - render_rdb_menu_list [:all] do |val|
19 | = link_to t("rdb_filter_tracker_#{val}"), rdb_filter_path(tracker: val)
20 | - render_rdb_menu_list @board.trackers do |val|
21 | span.rdb-multicheck
22 | = rdb_checkbox_link_to '', rdb_filter_path(tracker: val.id), enabled: @board.filters[:tracker].enabled?(val.id), show_disabled: true
23 | = link_to val.name, rdb_filter_path(tracker: val.id, only: true)
24 |
25 | - if @board.issue_categories.any?
26 | .rdb-filter.rdb-async
27 | - render_rdb_menu :categories, @board.filters[:category].title, icons: true do
28 | - render_rdb_menu_list [:all] do |val|
29 | = link_to t("rdb_filter_category_#{val}"), rdb_filter_path(category: val)
30 | - render_rdb_menu_list @board.issue_categories do |val|
31 | span.rdb-multicheck
32 | = rdb_checkbox_link_to '', rdb_filter_path(category: val.id), enabled: @board.filters[:category].enabled?(val.id), show_disabled: true
33 | = link_to val.name, rdb_filter_path(category: val.id, only: true)
34 |
35 | .rdb-filter.rdb-async
36 | - render_rdb_menu :assignee, @board.filters[:assignee].title do
37 | - render_rdb_menu_list [:all, :me, :none] do |val|
38 | = link_to t("rdb_filter_assignee_#{val}"), rdb_filter_path(assignee: val)
39 | - render_rdb_menu_list @board.assignees do |val|
40 | - if User === val
41 | = link_to val.name, rdb_filter_path(assignee: val.id)
42 | - render_rdb_menu_list @board.assignees do |val|
43 | - if Group === val
44 | = link_to val.name, rdb_filter_path(assignee: val.id)
45 |
46 | .rdb-option.rdb-async
47 | - render_rdb_menu :options, t(:rdb_options), right: true, icons: true do
48 | - render_rdb_menu_list do
49 | - render_rdb_menu_list_item do
50 | = rdb_checkbox_link_to t(:rdb_options_change_assignee), rdb_board_path(change_assignee: !@board.options[:change_assignee]),
51 | enabled: @board.options[:change_assignee], title: t(:rdb_options_change_assignee_info)
52 | = rdb_checkbox_link_to t(:rdb_options_hide_done), rdb_board_path(hide_done: !@board.options[:hide_done]),
53 | enabled: @board.options[:hide_done], title: t(:rdb_options_hide_done_info)
54 | = rdb_checkbox_link_to t(:rdb_options_include_subprojects), rdb_board_path(include_subprojects: (@board.project_ids.size > 1) ? 'false' : 'true'),
55 | enabled: (@board.project_ids.size > 1)
56 | - render_rdb_menu_list @board.column_list, title: t(:rdb_options_columns) do |column|
57 | = rdb_checkbox_link_to column.title, rdb_board_path(hide_column: column.id), enabled: @board.columns[column.id].visible?
58 | - render_rdb_menu_list do
59 | - render_rdb_menu_list_item do
60 | = link_to t(:rdb_options_fullscreen), 'javascript:Rdb.rdbToggleFullscreen();', id: "rdb-option-fullscreen"
61 | - render_rdb_menu_list_item do
62 | = link_to t(:rdb_options_reset), rdb_filter_path(reset: 1), id: "rdb-reset", title: t(:rdb_options_reset_info)
63 |
64 | .rdb-option.rdb-async
65 | - render_rdb_menu :view, t(:rdb_options_view), right: true do
66 | - render_rdb_menu_list RdbDashboard::VIEW_MODES, title: t(:rdb_options_issue_view) do |view|
67 | = rdb_checkbox_link_to t(:"rdb_options_issue_view_#{view}"), rdb_board_path(view: view), enabled: @board.options[:view] == view
68 | - render_rdb_menu_list [:none, :tracker, :category, :priority, :assignee, :version, :parent, :project], title: t(:rdb_options_group) do |group|
69 | = rdb_checkbox_link_to t(:"rdb_group_#{group}"), rdb_board_path(group: group), enabled: @board.options[:group] == group
70 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/_overall_progress.html.slim:
--------------------------------------------------------------------------------
1 | div.rdb-overall-progress
2 | - @board.column_list[0..-2].each do |column|
3 | a.rdb-overall-progress class="rdb-column-#{column.id}" style="width: #{column.percentage}%"
4 |
--------------------------------------------------------------------------------
/app/views/rdb_dashboard/taskboard/column_dialog.js.erb:
--------------------------------------------------------------------------------
1 | $().rdbDialog("<%= escape_javascript t(:rdb_dialog_update_issue_title) %>", "<%= escape_javascript(render :partial => 'rdb_dashboard/taskboard/column_dialog') %>");
2 |
--------------------------------------------------------------------------------
/assets/javascripts/dashboard.js:
--------------------------------------------------------------------------------
1 | (function(global, $) {
2 |
3 | global.Rdb = {};
4 | var rdbInits = [];
5 |
6 | /* extend */
7 | String.prototype.startsWith = function (string) {
8 | return(this.indexOf(string) === 0);
9 | };
10 |
11 | $.fn.rdbAny = function(selector) {
12 | return $(this).length > 0;
13 | };
14 |
15 | $.fn.rdbEmpty = function(selector) {
16 | return $(this).length == 0;
17 | };
18 |
19 | $.fn.rdbFindUp = function(selector) {
20 | var el = $(this);
21 | if(el.is(selector))
22 | return $(this);
23 | return el.parents(selector);
24 | };
25 |
26 | Rdb.rdbInit = function(fn) {
27 | $.fn.rdbInit.call(Rdb.rdbBase(), fn);
28 | }
29 |
30 | $.fn.rdbInit = function(fn) {
31 | if(fn) {
32 | rdbInits.push(fn);
33 | } else {
34 | for(var i in rdbInits) {
35 | rdbInits[i].call(this);
36 | }
37 | }
38 | };
39 |
40 | $.fn.rdbIssue = function() {
41 | return $(this).rdbFindUp('[data-rdb-issue-id]');
42 | };
43 |
44 | $.fn.rdbIssueId = function() {
45 | return $(this).rdbIssue().data('rdb-issue-id');
46 | };
47 |
48 | $.fn.rdbIssueLockVersion = function() {
49 | return $(this).rdbIssue().data('rdb-lock-version');
50 | };
51 |
52 | Rdb.rdbError = function(message) {
53 | var box = $('#rdb-errors');
54 | var msg = $('<div class="rdb-error" />').html(message).hide();
55 |
56 | msg.append('<a class="close">❌</a>')
57 |
58 | msg.find('a.close').click(function(e) {
59 | e.preventDefault();
60 | msg.fadeOut(function() {
61 | msg.remove();
62 | });
63 | });
64 |
65 | msg.appendTo(box).fadeIn(function() {
66 | setTimeout(function() {
67 | msg.fadeOut(function() {
68 | msg.remove();
69 | });
70 | }, 12000);
71 | });
72 | };
73 |
74 | $.fn.rdbVisible = function() {
75 | var el = $(this);
76 | var docTop = $(window).scrollTop();
77 | var docBottom = docTop + $(window).height();
78 |
79 | var top = $(el).offset().top;
80 | var bottom = top + $(el).height();
81 |
82 | return ((bottom <= docBottom) && (top >= docTop));
83 | };
84 |
85 | Rdb.rdbStorageAdd = function(id, value) {
86 | var storage = $.totalStorage('rdb-' + id);
87 | if(!storage) storage = new Array;
88 | storage.push(value);
89 | $.totalStorage('rdb-' + id, storage);
90 | return true;
91 | };
92 |
93 | Rdb.rdbStorageRemove = function(id, value) {
94 | var storage = $.totalStorage('rdb-' + id);
95 | if(!storage) return false;
96 | var i = -1;
97 | while((i = storage.indexOf(value)) >= 0) {
98 | storage.splice(i, 1)
99 | }
100 | $.totalStorage('rdb-' + id, storage);
101 | return true;
102 | };
103 |
104 | Rdb.rdbStorageHas = function(id, value) {
105 | var storage = $.totalStorage('rdb-' + id);
106 | if(!storage) return false;
107 | for(var i in storage) {
108 | if(storage[i] == value) {
109 | return true;
110 | }
111 | }
112 | return false;
113 | };
114 |
115 | Rdb.rdbBase = function() {
116 | return $('#rdb');
117 | };
118 |
119 | Rdb.rdbBaseURL = function() {
120 | return Rdb.rdbBase().data('rdb-base');
121 | };
122 |
123 | /*
124 | * Ajax Filter / Options
125 | */
126 | $(document).click(function(e) {
127 | var link = $(e.target).rdbFindUp('a').first();
128 | if(link.rdbFindUp('.rdb-async').rdbAny() && link.attr('href') != '#' && !link.is('.rdb-sync') && !link.attr('href').startsWith('javascript:')) {
129 | Rdb.rdbMenuClose();
130 | Rdb.rdbCloseDialog();
131 | e.preventDefault();
132 | $.getScript(
133 | link.attr('href')
134 | ).fail(function(jqxhr, settings, exception) {
135 | Rdb.rdbError('<b>Ajax Error</b>: ' + exception);
136 | });
137 | }
138 | });
139 |
140 | /* Issue subject text ellipsis */
141 | $(document).ready(function () {
142 | var resizeActions = function() {
143 | $('.rdb-property-subject').ellipsis();
144 |
145 | var box = $('#rdb-errors');
146 | var board = $('#rdb-board');
147 | if($('#rdb-footer').rdbVisible()) {
148 | box.css({ 'position': 'absolute', 'bottom': '30px' });
149 | } else {
150 | box.css({ 'position': 'fixed', 'bottom': '30px' });
151 | }
152 | };
153 |
154 | Rdb.rdbInit(resizeActions);
155 | $(window).resize(resizeActions);
156 | });
157 |
158 | /* load board on startup */
159 | $(document).ready(function () {
160 | $.getScript('?');
161 | });
162 |
163 | })(window, jQuery);
164 |
--------------------------------------------------------------------------------
/assets/javascripts/dashboard.taskboard.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | $.fn.rdbColumn = function() {
4 | return $(this).rdbFindUp('[data-rdb-column-id]');
5 | };
6 |
7 | $.fn.rdbColumnId = function() {
8 | return $(this).rdbColumn().data('rdb-column-id');
9 | };
10 |
11 | $.fn.rdbGroup = function() {
12 | return $(this).rdbFindUp('[data-rdb-group-id]');
13 | };
14 |
15 | $.fn.rdbGroupId = function() {
16 | return $(this).rdbGroup().data('rdb-group-id');
17 | };
18 |
19 | /* ====================================================
20 | ** Drag and drop
21 | */
22 |
23 | var currentIssue;
24 |
25 | Rdb.rdbDADShowIssue = function() {
26 | if(currentIssue) currentIssue.css({ visibility: 'visible', opacity: 1 });
27 | };
28 |
29 | Rdb.rdbInitDAD = function () {
30 | var el = Rdb.rdbBase();
31 | var baseURL = Rdb.rdbBaseURL();
32 |
33 | el.find(".rdb-issue-drag").each(function() {
34 | var issue = $(this);
35 |
36 | issue.draggable({
37 | scroll: false,
38 | revert: true,
39 | // containment: '#rdb-board',
40 | distance: 20,
41 | cancel: 'a,.rdb-menu',
42 | start: function() {
43 | Rdb.rdbMenuClose();
44 | issue.addClass('rdb-issue-dragged');
45 | },
46 | stop: function() {
47 | issue.removeClass('rdb-issue-dragged');
48 | }
49 | });
50 | });
51 |
52 | el.find(".rdb-column").each(function() {
53 | var column = $(this);
54 | var coluid = column.rdbColumnId();
55 | var cgroup = column.data('rdb-drop-group');
56 | if(coluid) {
57 | column.droppable({
58 | accept: function(draggable) {
59 | var issue = draggable.rdbIssue();
60 | var dropon = issue.data('rdb-drop-on') || '';
61 | return issue.data('rdb-drop-group') == cgroup && dropon.indexOf(coluid) >= 0;
62 | }, //'[data-rdb-drop-on*="' + accept + '"]',
63 | activeClass: "rdb-column-drop-active",
64 | hoverClass: "rdb-column-drop-hover",
65 | tolerance: "pointer",
66 | drop: function(event, ui) {
67 | var issue = $(ui.draggable).rdbIssue();
68 | var lock = issue.rdbIssueLockVersion();
69 | var issueId = issue.rdbIssueId();
70 | var groupId = issue.rdbGroupId();
71 |
72 | if(issueId && issue.rdbColumnId() != coluid) {
73 | currentIssue = issue;
74 | currentIssue.css({ visibility: 'hidden', opacity: 0 });
75 | $.getScript(
76 | baseURL + '/move?issue=' + issueId + '&lock_version=' + lock + '&column=' + coluid + '&group=' + groupId)
77 | .fail(function(jqxhr, settings, exception) {
78 | Rdb.rdbDADShowIssue();
79 | Rdb.rdbError('<b>Error</b>: ' + exception);
80 | });
81 | }
82 | }
83 | });
84 | }
85 | });
86 | };
87 |
88 | Rdb.rdbDestroyDAD = function () {
89 | Rdb.rdbBase().find(".rdb-issue-drag").draggable('destroy');
90 | };
91 |
92 | Rdb.rdbInit(function() {
93 | Rdb.rdbInitDAD();
94 | });
95 |
96 | /* ====================================================
97 | ** Collapse groups
98 | */
99 |
100 | $(document).click(function (e) {
101 | var link = $(e.target).rdbFindUp('a').first();
102 | var group = link.rdbGroup();
103 | if(link.rdbAny() && group.rdbAny() && link.parents().is('.rdb-group-header')) {
104 | e.preventDefault();
105 | if(group.hasClass('rdb-collapsed')) {
106 | Rdb.rdbStorageRemove('collapsed-groups', group.rdbGroupId());
107 | group.removeClass('rdb-collapsed');
108 | } else {
109 | Rdb.rdbStorageAdd('collapsed-groups', group.rdbGroupId());
110 | group.addClass('rdb-collapsed');
111 | }
112 | }
113 | });
114 |
115 | Rdb.rdbInit(function() {
116 | $('.rdb-group').each(function() {
117 | var group = $(this);
118 | if(Rdb.rdbStorageHas('collapsed-groups', group.rdbGroupId())) {
119 | group.addClass('rdb-collapsed');
120 | }
121 | });
122 | });
123 |
124 | })(jQuery);;
125 |
--------------------------------------------------------------------------------
/assets/javascripts/dashboard.ui.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | $.fn.rdbMenu = function() {
4 | $(this).rdbFindUp('.rdb-menu');
5 | }
6 |
7 | $.fn.rdbMenuShow = function() {
8 | $(this).addClass('rdb-menu-active');
9 | }
10 |
11 | $.fn.rdbMenuHide = function() {
12 | $(this).removeClass('rdb-menu-active');
13 | }
14 |
15 | /* =====================================================
16 | ** Dashboard Drop-Down Menus
17 | */
18 | var lastMenu;
19 |
20 | Rdb.rdbMenuClose = function() {
21 | if(lastMenu != null) {
22 | lastMenu.rdbMenuHide();
23 | lastMenu = null;
24 | }
25 | }
26 |
27 | $(document).click(function(e) {
28 | var link = $(e.target).rdbFindUp('a.rdb-menu-link');
29 | if(link.rdbAny() && link.parents('.rdb-menu-container').rdbEmpty() ) {
30 | e.preventDefault();
31 | var menu = link.parents('.rdb-menu').first();
32 | if(menu.rdbAny()) {
33 | if(menu.is(lastMenu)) {
34 | lastMenu.rdbMenuHide();
35 | lastMenu = null;
36 | } else {
37 | if(lastMenu) lastMenu.rdbMenuHide();
38 | lastMenu = menu;
39 | lastMenu.rdbMenuShow();
40 | }
41 | }
42 | }
43 |
44 | if(lastMenu != null && $(e.target).rdbFindUp('.rdb-menu').length == 0) {
45 | lastMenu.rdbMenuHide();
46 | lastMenu = null;
47 | }
48 | });
49 |
50 | /* =====================================================
51 | ** Dashboard Dialog
52 | */
53 |
54 | $.fn.rdbDialog = function(title, html) {
55 | var dialog = $('<div class="rdb-dialog" />').html(html).dialog({
56 | title: title,
57 | modal: true,
58 | draggable: false,
59 | resizable: false,
60 | dialogClass: 'alert',
61 | close: function() { Rdb.rdbDADShowIssue(); }
62 | });
63 | };
64 |
65 | Rdb.rdbCloseDialog = function() {
66 | $('.rdb-dialog').remove();
67 | };
68 |
69 | /* =====================================================
70 | ** Dashboard Fullscreen
71 | */
72 |
73 | Rdb.rdbIsFullscreen = function() {
74 | return Rdb.rdbStorageHas('fullscreen', 'fullscreen');
75 | };
76 |
77 | Rdb.rdbLoadFullscreen = function() {
78 | if(Rdb.rdbIsFullscreen()) {
79 | Rdb.rdbShowFullscreen();
80 | } else {
81 | Rdb.rdbHideFullscreen();
82 | }
83 | };
84 |
85 | Rdb.rdbToggleFullscreen = function() {
86 | Rdb.rdbMenuClose();
87 | if(Rdb.rdbIsFullscreen()) {
88 | Rdb.rdbHideFullscreen();
89 | } else {
90 | Rdb.rdbShowFullscreen();
91 | }
92 | };
93 |
94 | Rdb.rdbShowFullscreen = function() {
95 | Rdb.rdbStorageAdd('fullscreen', 'fullscreen');
96 | Rdb.rdbBase().addClass('rdb-fullscreen');
97 | };
98 |
99 | Rdb.rdbHideFullscreen = function() {
100 | Rdb.rdbStorageRemove('fullscreen', 'fullscreen');
101 | Rdb.rdbBase().removeClass('rdb-fullscreen');
102 | };
103 |
104 | Rdb.rdbInit(Rdb.rdbLoadFullscreen);
105 |
106 | })(jQuery);
107 |
--------------------------------------------------------------------------------
/assets/javascripts/jquery.autoellipsis.js:
--------------------------------------------------------------------------------
1 | /*!
2 |
3 | Copyright (c) 2011 Peter van der Spek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 | */
24 |
25 |
26 | (function($) {
27 |
28 | /**
29 | * Hash containing mapping of selectors to settings hashes for target selectors that should be live updated.
30 | *
31 | * @type {Object.<string, Object>}
32 | * @private
33 | */
34 | var liveUpdatingTargetSelectors = {};
35 |
36 | /**
37 | * Interval ID for live updater. Contains interval ID when the live updater interval is active, or is undefined
38 | * otherwise.
39 | *
40 | * @type {number}
41 | * @private
42 | */
43 | var liveUpdaterIntervalId;
44 |
45 | /**
46 | * Boolean indicating whether the live updater is running.
47 | *
48 | * @type {boolean}
49 | * @private
50 | */
51 | var liveUpdaterRunning = false;
52 |
53 | /**
54 | * Set of default settings.
55 | *
56 | * @type {Object.<string, string>}
57 | * @private
58 | */
59 | var defaultSettings = {
60 | ellipsis: '…',
61 | setTitle: 'never',
62 | live: false
63 | };
64 |
65 | /**
66 | * Perform ellipsis on selected elements.
67 | *
68 | * @param {string} selector the inner selector of elements that ellipsis may work on. Inner elements not referred to by this
69 | * selector are left untouched.
70 | * @param {Object.<string, string>=} options optional options to override default settings.
71 | * @return {jQuery} the current jQuery object for chaining purposes.
72 | * @this {jQuery} the current jQuery object.
73 | */
74 | $.fn.ellipsis = function(selector, options) {
75 | var subjectElements, settings;
76 |
77 | subjectElements = $(this);
78 |
79 | // Check for options argument only.
80 | if (typeof selector !== 'string') {
81 | options = selector;
82 | selector = undefined;
83 | }
84 |
85 | // Create the settings from the given options and the default settings.
86 | settings = $.extend({}, defaultSettings, options);
87 |
88 | // If selector is not set, work on immediate children (default behaviour).
89 | settings.selector = selector;
90 |
91 | // Do ellipsis on each subject element.
92 | subjectElements.each(function() {
93 | var elem = $(this);
94 |
95 | // Do ellipsis on subject element.
96 | ellipsisOnElement(elem, settings);
97 | });
98 |
99 | // If live option is enabled, add subject elements to live updater. Otherwise remove from live updater.
100 | if (settings.live) {
101 | addToLiveUpdater(subjectElements.selector, settings);
102 |
103 | } else {
104 | removeFromLiveUpdater(subjectElements.selector);
105 | }
106 |
107 | // Return jQuery object for chaining.
108 | return this;
109 | };
110 |
111 |
112 | /**
113 | * Perform ellipsis on the given container.
114 | *
115 | * @param {jQuery} containerElement jQuery object containing one DOM element to perform ellipsis on.
116 | * @param {Object.<string, string>} settings the settings for this ellipsis operation.
117 | * @private
118 | */
119 | function ellipsisOnElement(containerElement, settings) {
120 | var containerData = containerElement.data('jqae');
121 | if (!containerData) containerData = {};
122 |
123 | // Check if wrapper div was already created and bound to the container element.
124 | var wrapperElement = containerData.wrapperElement;
125 |
126 | // If not, create wrapper element.
127 | if (!wrapperElement) {
128 | wrapperElement = containerElement.wrapInner('<div/>').find('>div');
129 |
130 | // Wrapper div should not add extra size.
131 | wrapperElement.css({
132 | margin: 0,
133 | padding: 0,
134 | border: 0
135 | });
136 | }
137 |
138 | // Check if the original wrapper element content was already bound to the wrapper element.
139 | var wrapperElementData = wrapperElement.data('jqae');
140 | if (!wrapperElementData) wrapperElementData = {};
141 |
142 | var wrapperOriginalContent = wrapperElementData.originalContent;
143 |
144 | // If so, clone the original content, re-bind the original wrapper content to the clone, and replace the
145 | // wrapper with the clone.
146 | if (wrapperOriginalContent) {
147 | wrapperElement = wrapperElementData.originalContent.clone(true)
148 | .data('jqae', {originalContent: wrapperOriginalContent}).replaceAll(wrapperElement);
149 |
150 | } else {
151 | // Otherwise, clone the current wrapper element and bind it as original content to the wrapper element.
152 |
153 | wrapperElement.data('jqae', {originalContent: wrapperElement.clone(true)});
154 | }
155 |
156 | // Bind the wrapper element and current container width and height to the container element. Current container
157 | // width and height are stored to detect changes to the container size.
158 | containerElement.data('jqae', {
159 | wrapperElement: wrapperElement,
160 | containerWidth: containerElement.width(),
161 | containerHeight: containerElement.height()
162 | });
163 |
164 | // Calculate with current container element height.
165 | var containerElementHeight = containerElement.height();
166 |
167 | // Calculate wrapper offset.
168 | var wrapperOffset = (parseInt(containerElement.css('padding-top'), 10) || 0) + (parseInt(containerElement.css('border-top-width'), 10) || 0) - (wrapperElement.offset().top - containerElement.offset().top);
169 |
170 | // Normally the ellipsis characters are applied to the last non-empty text-node in the selected element. If the
171 | // selected element becomes empty during ellipsis iteration, the ellipsis characters cannot be applied to that
172 | // selected element, and must be deferred to the previous selected element. This parameter keeps track of that.
173 | var deferAppendEllipsis = false;
174 |
175 | // Loop through all selected elements in reverse order.
176 | var selectedElements = wrapperElement;
177 | if (settings.selector) selectedElements = $(wrapperElement.find(settings.selector).get().reverse());
178 |
179 | selectedElements.each(function() {
180 | var selectedElement = $(this),
181 | originalText = selectedElement.text(),
182 | ellipsisApplied = false;
183 |
184 | // Check if we can safely remove the selected element. This saves a lot of unnecessary iterations.
185 | if (wrapperElement.innerHeight() - selectedElement.innerHeight() > containerElementHeight + wrapperOffset) {
186 | selectedElement.remove();
187 |
188 | } else {
189 | // Reverse recursively remove empty elements, until the element that contains a non-empty text-node.
190 | removeLastEmptyElements(selectedElement);
191 |
192 | // If the selected element has not become empty, start ellipsis iterations on the selected element.
193 | if (selectedElement.contents().length) {
194 |
195 | // If a deffered ellipsis is still pending, apply it now to the last text-node.
196 | if (deferAppendEllipsis) {
197 | getLastTextNode(selectedElement).get(0).nodeValue += settings.ellipsis;
198 | deferAppendEllipsis = false;
199 | }
200 |
201 | // Iterate until wrapper element height is less than or equal to the original container element
202 | // height plus possible wrapperOffset.
203 | while (wrapperElement.innerHeight() > containerElementHeight + wrapperOffset) {
204 | // Apply ellipsis on last text node, by removing one word.
205 | ellipsisApplied = ellipsisOnLastTextNode(selectedElement);
206 |
207 | // If ellipsis was succesfully applied, remove any remaining empty last elements and append the
208 | // ellipsis characters.
209 | if (ellipsisApplied) {
210 | removeLastEmptyElements(selectedElement);
211 |
212 | // If the selected element is not empty, append the ellipsis characters.
213 | if (selectedElement.contents().length) {
214 | getLastTextNode(selectedElement).get(0).nodeValue += settings.ellipsis;
215 |
216 | } else {
217 | // If the selected element has become empty, defer the appending of the ellipsis characters
218 | // to the previous selected element.
219 | deferAppendEllipsis = true;
220 | selectedElement.remove();
221 | break;
222 | }
223 |
224 | } else {
225 | // If ellipsis could not be applied, defer the appending of the ellipsis characters to the
226 | // previous selected element.
227 | deferAppendEllipsis = true;
228 | selectedElement.remove();
229 | break;
230 | }
231 | }
232 |
233 | // If the "setTitle" property is set to "onEllipsis" and the ellipsis has been applied, or if the
234 | // property is set to "always", the add the "title" attribute with the original text. Else remove the
235 | // "title" attribute. When the "setTitle" property is set to "never" we do not touch the "title"
236 | // attribute.
237 | if (((settings.setTitle == 'onEllipsis') && ellipsisApplied) || (settings.setTitle == 'always')) {
238 | selectedElement.attr('title', originalText);
239 |
240 | } else if (settings.setTitle != 'never') {
241 | selectedElement.removeAttr('title');
242 | }
243 | }
244 | }
245 | });
246 | }
247 |
248 | /**
249 | * Performs ellipsis on the last text node of the given element. Ellipsis is done by removing a full word.
250 | *
251 | * @param {jQuery} element jQuery object containing a single DOM element.
252 | * @return {boolean} true when ellipsis has been done, false otherwise.
253 | * @private
254 | */
255 | function ellipsisOnLastTextNode(element) {
256 | var lastTextNode = getLastTextNode(element);
257 |
258 | // If the last text node is found, do ellipsis on that node.
259 | if (lastTextNode.length) {
260 | var text = lastTextNode.get(0).nodeValue;
261 |
262 | // Find last space character, and remove text from there. If no space is found the full remaining text is
263 | // removed.
264 | var pos = text.lastIndexOf(' ');
265 | if (pos > -1) {
266 | text = $.trim(text.substring(0, pos));
267 | lastTextNode.get(0).nodeValue = text;
268 |
269 | } else {
270 | lastTextNode.get(0).nodeValue = '';
271 | }
272 |
273 | return true;
274 | }
275 |
276 | return false;
277 | }
278 |
279 | /**
280 | * Get last text node of the given element.
281 | *
282 | * @param {jQuery} element jQuery object containing a single element.
283 | * @return {jQuery} jQuery object containing a single text node.
284 | * @private
285 | */
286 | function getLastTextNode(element) {
287 | if (element.contents().length) {
288 |
289 | // Get last child node.
290 | var contents = element.contents();
291 | var lastNode = contents.eq(contents.length - 1);
292 |
293 | // If last node is a text node, return it.
294 | if (lastNode.filter(textNodeFilter).length) {
295 | return lastNode;
296 |
297 | } else {
298 | // Else it is an element node, and we recurse into it.
299 |
300 | return getLastTextNode(lastNode);
301 | }
302 |
303 | } else {
304 | // If there is no last child node, we append an empty text node and return that. Normally this should not
305 | // happen, as we test for emptiness before calling getLastTextNode.
306 |
307 | element.append('');
308 | var contents = element.contents();
309 | return contents.eq(contents.length - 1);
310 | }
311 | }
312 |
313 | /**
314 | * Remove last empty elements. This is done recursively until the last element contains a non-empty text node.
315 | *
316 | * @param {jQuery} element jQuery object containing a single element.
317 | * @return {boolean} true when elements have been removed, false otherwise.
318 | * @private
319 | */
320 | function removeLastEmptyElements(element) {
321 | if (element.contents().length) {
322 |
323 | // Get last child node.
324 | var contents = element.contents();
325 | var lastNode = contents.eq(contents.length - 1);
326 |
327 | // If last child node is a text node, check for emptiness.
328 | if (lastNode.filter(textNodeFilter).length) {
329 | var text = lastNode.get(0).nodeValue;
330 | text = $.trim(text);
331 |
332 | if (text == '') {
333 | // If empty, remove the text node.
334 | lastNode.remove();
335 |
336 | return true;
337 |
338 | } else {
339 | return false;
340 | }
341 |
342 | } else {
343 | // If the last child node is an element node, remove the last empty child nodes on that node.
344 | while (removeLastEmptyElements(lastNode)) {
345 | }
346 |
347 | // If the last child node contains no more child nodes, remove the last child node.
348 | if (lastNode.contents().length) {
349 | return false;
350 |
351 | } else {
352 | lastNode.remove();
353 |
354 | return true;
355 | }
356 | }
357 | }
358 |
359 | return false;
360 | }
361 |
362 | /**
363 | * Filter for testing on text nodes.
364 | *
365 | * @return {boolean} true when this node is a text node, false otherwise.
366 | * @this {Node}
367 | * @private
368 | */
369 | function textNodeFilter() {
370 | return this.nodeType === 3;
371 | }
372 |
373 | /**
374 | * Add target selector to hash of target selectors. If this is the first target selector added, start the live
375 | * updater.
376 | *
377 | * @param {string} targetSelector the target selector to run the live updater for.
378 | * @param {Object.<string, string>} settings the settings to apply on this target selector.
379 | * @private
380 | */
381 | function addToLiveUpdater(targetSelector, settings) {
382 | // Store target selector with its settings.
383 | liveUpdatingTargetSelectors[targetSelector] = settings;
384 |
385 | // If the live updater has not yet been started, start it now.
386 | if (!liveUpdaterIntervalId) {
387 | liveUpdaterIntervalId = window.setInterval(function() {
388 | doLiveUpdater();
389 | }, 200);
390 | }
391 | }
392 |
393 | /**
394 | * Remove the target selector from the hash of target selectors. It this is the last remaining target selector
395 | * being removed, stop the live updater.
396 | *
397 | * @param {string} targetSelector the target selector to stop running the live updater for.
398 | * @private
399 | */
400 | function removeFromLiveUpdater(targetSelector) {
401 | // If the hash contains the target selector, remove it.
402 | if (liveUpdatingTargetSelectors[targetSelector]) {
403 | delete liveUpdatingTargetSelectors[targetSelector];
404 |
405 | // If no more target selectors are in the hash, stop the live updater.
406 | if (!liveUpdatingTargetSelectors.length) {
407 | if (liveUpdaterIntervalId) {
408 | window.clearInterval(liveUpdaterIntervalId);
409 | liveUpdaterIntervalId = undefined;
410 | }
411 | }
412 | }
413 | };
414 |
415 | /**
416 | * Run the live updater. The live updater is periodically run to check if its monitored target selectors require
417 | * re-applying of the ellipsis.
418 | *
419 | * @private
420 | */
421 | function doLiveUpdater() {
422 | // If the live updater is already running, skip this time. We only want one instance running at a time.
423 | if (!liveUpdaterRunning) {
424 | liveUpdaterRunning = true;
425 |
426 | // Loop through target selectors.
427 | for (var targetSelector in liveUpdatingTargetSelectors) {
428 | $(targetSelector).each(function() {
429 | var containerElement, containerData;
430 |
431 | containerElement = $(this);
432 | containerData = containerElement.data('jqae');
433 |
434 | // If container element dimensions have changed, or the container element is new, run ellipsis on
435 | // that container element.
436 | if ((containerData.containerWidth != containerElement.width()) ||
437 | (containerData.containerHeight != containerElement.height())) {
438 | ellipsisOnElement(containerElement, liveUpdatingTargetSelectors[targetSelector]);
439 | }
440 | });
441 | }
442 |
443 | liveUpdaterRunning = false;
444 | }
445 | };
446 |
447 | })(jQuery);
448 |
--------------------------------------------------------------------------------
/assets/javascripts/jquery.total-storage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TotalStorage
3 | *
4 | * Copyright (c) 2012 Jared Novack & Upstatement (upstatement.com)
5 | * Dual licensed under the MIT and GPL licenses:
6 | * http://www.opensource.org/licenses/mit-license.php
7 | * http://www.gnu.org/licenses/gpl.html
8 | *
9 | * Total Storage is the conceptual the love child of jStorage by Andris Reinman,
10 | * and Cookie by Klaus Hartl -- though this is not connected to either project.
11 | */
12 |
13 | /**
14 | * Create a local storage parameter
15 | *
16 | == What makes it TOTAL Storage? ==
17 |
18 | * The browser doesn't support local storage it will fall-back to cookies! (Using the
19 | wonderful $.cookie plugin).
20 | * Send it strings, numbers even complex object arrays! TotalStorage does not care.
21 | Your efforts to defeat it will prove futile.
22 | * Simple as shit. jStorage and some other very well-written plugins provide a bevy of
23 | options for expiration, security and so forth. Frequently this is more power than you
24 | need and vulnerable to confusion if you're just want it to work (JWITW)
25 |
26 | * @desc Set the value of a key to a string
27 | * @example $.totalStorage('the_key', 'the_value');
28 | * @desc Set the value of a key to a number
29 | * @example $.totalStorage('the_key', 800.2);
30 | * @desc Set the value of a key to a complex Array
31 | * @example var myArray = new Array();
32 | * myArray.push({name:'Jared', company:'Upstatement', zip:63124});
33 | myArray.push({name:'McGruff', company:'Police', zip:60652};
34 | $.totalStorage('people', myArray);
35 | //to return:
36 | $.totalStorage('people');
37 | *
38 | * @name $.totalStorage
39 | * @cat Plugins/Cookie
40 | * @author Jared Novack/jared@upstatement.com
41 | * @version 1.1
42 | * @url http://upstatement.com/blog/2012/01/jquery-local-storage-done-right-and-easy/
43 | */
44 |
45 | ;(function($){
46 |
47 | /* Variables I'll need throghout */
48 |
49 | var ls = window.localStorage;
50 | var supported;
51 | if (typeof ls == 'undefined' || typeof window.JSON == 'undefined'){
52 | supported = false;
53 | } else {
54 | supported = true;
55 | }
56 | // supported = false;
57 | // console.log('supported = ' + supported);
58 | /* Make the methods public */
59 |
60 | $.totalStorage = function(key, value, options){
61 | return $.totalStorage.impl.init(key, value);
62 | }
63 |
64 | $.totalStorage.setItem = function(key, value){
65 | return $.totalStorage.impl.setItem(key, value);
66 | }
67 |
68 | $.totalStorage.getItem = function(key){
69 | return $.totalStorage.impl.getItem(key);
70 | }
71 |
72 | $.totalStorage.getAll = function(){
73 | return $.totalStorage.impl.getAll();
74 | }
75 |
76 | /* Object to hold all methods: public and private */
77 |
78 | $.totalStorage.impl = {
79 |
80 | init: function(key, value){
81 | if (typeof value != 'undefined') {
82 | return this.setItem(key, value);
83 | } else {
84 | return this.getItem(key);
85 | }
86 | },
87 |
88 | setItem: function(key, value){
89 | if (!supported){
90 | try {
91 | $.cookie(key, value);
92 | return value;
93 | } catch(e){
94 | console.log('Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality');
95 | }
96 | }
97 | var saver = JSON.stringify(value);
98 | ls.setItem(key, saver);
99 | return this.parseResult(saver);
100 | },
101 |
102 | getItem: function(key){
103 | if (!supported){
104 | try {
105 | return this.parseResult($.cookie(key));
106 | } catch(e){
107 | return null;
108 | }
109 | }
110 | return this.parseResult(ls.getItem(key));
111 | },
112 |
113 | getAll: function(){
114 | var items = new Array();
115 | if (!supported){
116 | try {
117 | var pairs = document.cookie.split(";");
118 | for (var i = 0; i<pairs.length; i++){
119 | var pair = pairs[i].split('=');
120 | var key = pair[0];
121 | items.push({key:key, value:this.parseResult($.cookie(key))});
122 | }
123 | } catch(e){
124 | return null;
125 | }
126 | } else {
127 | for (var i in ls){
128 | if (i.length){
129 | items.push({key:i, value:this.parseResult(ls.getItem(i))});
130 | }
131 | }
132 | }
133 | return items;
134 | },
135 |
136 | parseResult: function(res){
137 | var ret;
138 | try {
139 | ret = JSON.parse(res);
140 | if (ret == 'true'){
141 | ret = true;
142 | }
143 | if (ret == 'false'){
144 | ret = false;
145 | }
146 | if (parseFloat(ret) == ret){
147 | ret = parseFloat(ret);
148 | }
149 | } catch(e){}
150 | return ret;
151 | }
152 | }
153 |
154 | })(jQuery);
155 |
--------------------------------------------------------------------------------
/assets/stylesheets/dashboard.css:
--------------------------------------------------------------------------------
1 | /*
2 | ** Redmine Dashboard
3 | */
4 |
5 | /* =========================================================
6 | ** Dashboard Origin
7 | */
8 |
9 | /* override */
10 | body,
11 | html {
12 | height: 100%;
13 | }
14 |
15 | /* override */
16 | html > body #content {
17 | padding: 0 !important;
18 | background-color: #eee;
19 | }
20 |
21 | #rdb {
22 | background-color: #eee;
23 | position: relative;
24 | }
25 |
26 | #rdb.rdb-fullscreen {
27 | position: absolute;
28 | top: 0;
29 | left: 0;
30 | right: 0;
31 | min-height: 100%;
32 | z-index: 9999999995;
33 | }
34 |
35 | #rdb.rdb-fullscreen #rdb-header {
36 | position: fixed;
37 | top: 0;
38 | left: 0;
39 | right: 0;
40 | z-index: 9999999999;
41 | }
42 |
43 | #rdb.rdb-fullscreen {
44 | padding-top: 45px;
45 | }
46 |
47 | #rdb-board {
48 | clear: both;
49 |
50 | margin: 10px 15px;
51 | padding: 8px 2px 2px;
52 |
53 | background-color: #fff;
54 | border: 1px solid #ccc;
55 | border-radius: 8px;
56 | }
57 |
58 | #rdb-board-container {
59 | /* padding: 1px;
60 | padding-bottom: 1px;
61 | */
62 | }
63 |
64 | #rdb-loading {
65 | padding: 40px;
66 | text-align: center;
67 | min-height: 200px;
68 | }
69 |
70 | .rdb-clear {
71 | clear: both;
72 | }
73 |
74 | /* =========================================================
75 | ** Dashboard Header
76 | */
77 |
78 | #rdb-header {
79 | padding: 7px 30px 7px 11px;
80 | background-color: #fff;
81 | border-bottom: 1px solid #ccc;
82 | }
83 |
84 | #rdb-header .rdb-board,
85 | #rdb-header .rdb-filter,
86 | #rdb-header .rdb-option {
87 | float: left;
88 | margin: 0 4px;
89 | }
90 |
91 | #rdb-header .rdb-menu-link {
92 | padding: 4px 18px 4px 6px;
93 | border-radius: 2px;
94 | }
95 |
96 | #rdb-header .rdb-filter,
97 | #rdb-header .rdb-option {
98 | margin-top: 8px;
99 | }
100 |
101 | #rdb-header .rdb-option {
102 | float: right;
103 | }
104 |
105 | #rdb-header .rdb-board .rdb-container {
106 | margin-top: 4px;
107 | }
108 |
109 | #rdb-header .rdb-board h1,
110 | #rdb-header .rdb-board h2,
111 | #rdb-header .rdb-board h3,
112 | #rdb-header .rdb-board h4,
113 | #rdb-header .rdb-board h5 {
114 | margin: 0;
115 | padding: 0;
116 | white-space: nowrap;
117 | }
118 | #rdb-header .rdb-board h2 {
119 | font-size: 20px;
120 | }
121 |
122 | a#rdb-refresh {
123 | float: left;
124 | padding: 4px;
125 | margin-right: 5px;
126 | display: block;
127 | width: 30px;
128 | height: 17px;
129 | background-image: url(../../../images/reload.png);
130 | background-repeat: no-repeat;
131 | background-position: 50% 40%;
132 | border-radius: 2px;
133 | border: 1px solid #fff;
134 | }
135 | a#rdb-refresh:hover {
136 | background-color: #eee;
137 | border: 1px solid #ccc;
138 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
139 | }
140 | a#rdb-refresh:active {
141 | background-color: #eee;
142 | border: 1px solid #aaa;
143 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.3) inset;
144 | }
145 |
146 | a#rdb-reset {
147 | background-image: url(../../../images/cancel.png);
148 | background-repeat: no-repeat;
149 | background-position: 6px 60%;
150 | }
151 |
152 | /* =========================================================
153 | ** Error message
154 | */
155 |
156 | #rdb-errors {
157 | position: fixed;
158 | bottom: 10%;
159 | left: 35%;
160 | right: 35%;
161 | }
162 | #rdb-errors .rdb-error {
163 | margin: 5px 0;
164 | padding: 10px 20px;
165 |
166 | position: relative;
167 | overflow: hidden;
168 |
169 | color: #b94a48;
170 | background-color: #f2dede;
171 | border: 1px solid #c58080;
172 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
173 | border-radius: 2px;
174 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
175 | }
176 | #rdb-errors .rdb-error a.close {
177 | position: absolute;
178 | top: 0;
179 | right: 8px;
180 |
181 | font-size: 25px;
182 | color: #aaa;
183 | }
184 | #rdb-errors .rdb-error a.close:hover {
185 | color: #777;
186 | text-decoration: none;
187 | cursor: pointer;
188 | }
189 |
190 | /* =========================================================
191 | ** Legend / Footer
192 | */
193 |
194 | #rdb-legend {
195 | float: left;
196 | }
197 | #rdb-legend div {
198 | padding: 0 0 4px 0;
199 | }
200 | #rdb-legend p {
201 | float: left;
202 | min-width: 70px;
203 | margin: 0;
204 | padding: 0;
205 | font-weight: bold;
206 | /*text-align: right;*/
207 | padding-right: 10px;
208 | }
209 | #rdb-legend span {
210 | padding: 0 10px 0 3px;
211 | }
212 | #rdb-legend .rdb-overdue {
213 | padding: 0;
214 | }
215 | #rdb-legend .rdb-overdue span {
216 | border: 1px solid red;
217 | padding: 0;
218 | }
219 |
220 | #rdb-footer {
221 | padding: 5px 30px 10px;
222 | margin: 0;
223 | font-size: 0.9em;
224 | }
225 |
226 | #rdb-copyright {
227 | float: right;
228 | color: #999;
229 | }
230 |
--------------------------------------------------------------------------------
/assets/stylesheets/dashboard.issues.css:
--------------------------------------------------------------------------------
1 | /*
2 | ** Redmine.rdb-issue - Issues
3 | */
4 |
5 | .rdb-grid-card {
6 | margin: 3px;
7 |
8 | display: grid;
9 | grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
10 | gap: 3px;
11 | }
12 |
13 | .rdb-issue-dragged {
14 | z-index: 10;
15 | opacity: 0.9;
16 | cursor: move;
17 | }
18 |
19 | /* ========================================================
20 | ** Issue Priorities
21 | */
22 |
23 | .rdb-priority {
24 | border-left: 5px solid black;
25 | }
26 |
27 | .rdb-priority-1 {
28 | border-color: #aaa;
29 | }
30 | .rdb-priority-2 {
31 | border-color: #6b6;
32 | }
33 | .rdb-priority-3 {
34 | border-color: orange;
35 | }
36 | .rdb-priority-4 {
37 | border-color: red;
38 | }
39 | .rdb-priority-5 {
40 | border-color: black;
41 | }
42 |
43 | /* ========================================================
44 | ** Issue Card View
45 | */
46 |
47 | .rdb-card {
48 | border: 1px solid #ccc;
49 | background: #ffffdd;
50 | border-radius: 2px;
51 |
52 | position: relative;
53 | }
54 |
55 | .rdb-overdue .rdb-card {
56 | border-color: red;
57 | }
58 |
59 | .rdb-card-title {
60 | float: left;
61 | }
62 | .rdb-card-header-data {
63 | padding-top: 2px;
64 | }
65 |
66 | .rdb-card-title > a {
67 | padding-right: 4px;
68 | }
69 | .rdb-card-title a.rdb-menu-link,
70 | .rdb-card-title > a {
71 | display: inline-block;
72 | padding-top: 2px;
73 | padding-bottom: 2px;
74 | font-size: 1em;
75 | border-right: 1px solid #ddd;
76 | }
77 | .rdb-card-title a.rdb-menu-link:hover,
78 | .rdb-card-title > a:hover {
79 | background-color: #eee;
80 | }
81 |
82 | .rdb-card-header a.rdb-menu-link,
83 | .rdb-compact-header a.rdb-menu-link,
84 | .rdb-card-title > a {
85 | padding-left: 16px;
86 | background-image: url("img/cog.png");
87 | background-repeat: no-repeat;
88 | background-position: 4px 50%;
89 | }
90 |
91 | .rdb-card-header .rdb-container,
92 | .rdb-compact-header .rdb-container {
93 | margin-top: 4px;
94 | }
95 |
96 | .rdb-card-content {
97 | padding: 2px 4px 2px 5px;
98 | }
99 |
100 | .rdb-card-subject {
101 | display: block;
102 | margin: 0 0 0.25em 0;
103 | height: 4.6em;
104 |
105 | text-overflow: ellipsis;
106 | overflow: hidden;
107 | }
108 |
109 | .rdb-card-progress,
110 | .rdb-compact-progress {
111 | background-color: #ddd;
112 | margin: 0 0;
113 | clear: both;
114 | }
115 |
116 | .rdb-card-progress-bar,
117 | .rdb-compact-progress-bar {
118 | background-color: green;
119 | height: 2px;
120 | }
121 |
122 | /* ========================================================
123 | ** Issue Properties
124 | */
125 |
126 | .rdb-property {
127 | display: block;
128 | padding: 1px 0 0 14px;
129 | background: transparent none no-repeat 0 70%;
130 | font-size: 0.95em;
131 | }
132 | .rdb-property.rdb-disabled {
133 | color: #aaa;
134 | }
135 |
136 | .rdb-property-tracker {
137 | background-image: url("img/ticket.png");
138 | }
139 | .rdb-property-assignee {
140 | background-image: url("img/user.png");
141 | }
142 | .rdb-property-category {
143 | background-image: url("img/brick.png");
144 | }
145 | .rdb-property-version {
146 | background-image: url("img/package.png");
147 | }
148 | .rdb-property-time {
149 | background-image: url("img/time.png");
150 | font-size: 70%;
151 | }
152 | .rdb-property-time span {
153 | font-size: 140%;
154 | }
155 |
156 | /* ========================================================
157 | ** Issue Compact View
158 | */
159 |
160 | .rdb-compact {
161 | position: relative;
162 | margin-bottom: 2px;
163 |
164 | border: 1px solid #ccc;
165 | background: #ffffdd;
166 | border-radius: 2px;
167 | }
168 |
169 | .rdb-overdue .rdb-compact {
170 | border-color: red;
171 | }
172 |
173 | .rdb-compact-header {
174 | padding: 0 2px 0 0;
175 | font-size: 1em;
176 | height: 1.5em;
177 | }
178 |
179 | .rdb-compact-header .rdb-compact-title a.rdb-menu-link {
180 | padding-top: 2px;
181 | padding-bottom: 2px;
182 | padding-left: 18px;
183 | background-position: 4px 50%;
184 | display: block;
185 | border-bottom-right-radius: 8px;
186 | }
187 |
188 | .rdb-card-header .rdb-card-header-data,
189 | .rdb-compact-header .rdb-compact-header-data {
190 | height: 1.5em;
191 | font-size: 0.8em;
192 | overflow: hidden;
193 | }
194 | .rdb-compact-header .rdb-property {
195 | display: inline;
196 | color: #888;
197 | margin: 0 6px 0 0;
198 | }
199 | .rdb-compact-header .rdb-property:last-child {
200 | margin: 0;
201 | }
202 | .rdb-compact-header .rdb-property.rdb-disabled {
203 | display: none;
204 | }
205 |
206 | .rdb-compact-title {
207 | display: block;
208 | min-width: 60px;
209 | float: left;
210 | }
211 |
212 | .rdb-compact-content {
213 | padding: 2px 4px 4px 5px;
214 | font-size: 0.95em;
215 | }
216 | .rdb-compact-subject {
217 | overflow: hidden;
218 | text-overflow: ellipsis;
219 |
220 | display: -webkit-box;
221 | -webkit-line-clamp: 1;
222 | -webkit-box-orient: vertical;
223 | }
224 | .rdb-compact-property {
225 | color: #999;
226 | display: inline;
227 | display: inline-block;
228 | background-position: 2px 70%;
229 | }
230 | .rdb-compact-progress-bar {
231 | height: 2px;
232 | }
233 |
234 | /* ========================================================
235 | ** Issue Menu
236 | */
237 |
238 | .rdb-issue-menu-progress {
239 | margin: 0;
240 | padding: 0;
241 | width: 100%;
242 | list-style: none;
243 | display: table;
244 | table-layout: fixed;
245 | }
246 | .rdb-issue-menu-progress:last-child {
247 | padding-bottom: 3px;
248 | }
249 | .rdb-issue-menu-progress li {
250 | display: table-cell;
251 | text-align: center;
252 | }
253 |
254 | .rdb-issue-menu-progress li a {
255 | display: block;
256 | padding: 4px 8px;
257 | }
258 |
259 | .rdb-issue-menu-progress li a:hover {
260 | background-color: #eee;
261 | }
262 |
--------------------------------------------------------------------------------
/assets/stylesheets/dashboard.taskboard.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Redmine Dashboard - Taskboard
3 | */
4 |
5 | /* ========================================================
6 | * Taskboard Header
7 | */
8 |
9 | .rdb-columns,
10 | .rdb-headers {
11 | margin: 0;
12 | padding: 0;
13 | width: 100%;
14 |
15 | display: table;
16 | table-layout: fixed;
17 | }
18 |
19 | .rdb-columns .rdb-column,
20 | .rdb-headers .rdb-column {
21 | display: table-cell;
22 | vertical-align: top;
23 | }
24 |
25 | .rdb-headers {
26 | margin-bottom: 1px;
27 | }
28 |
29 | .rdb-headers .rdb-column {
30 | border-bottom: 1px solid #aaa;
31 | padding: 0 0 4px;
32 | }
33 |
34 | .rdb-headers .rdb-column h3 {
35 | margin: 0;
36 | padding: 0 0 0 10px;
37 | display: inline;
38 | }
39 |
40 | .rdb-headers .rdb-column span {
41 | font-size: 0.8em;
42 | margin-left: 10px;
43 | color: #999;
44 | }
45 |
46 | .rdb-columns {
47 | margin: 0;
48 | }
49 | .rdb-columns:first-child .rdb-column {
50 | padding-top: 8px;
51 | }
52 |
53 | .rdb-columns .rdb-column {
54 | border-left: 2px solid #f5f5f5;
55 | padding: 0 3px 25px 1px;
56 | transition: all 0.3s ease-in-out;
57 | }
58 |
59 | .rdb-column-compact {
60 | width: 170px;
61 | }
62 |
63 | .rdb-columns .rdb-column:first-child {
64 | border-left: none;
65 | }
66 |
67 | .rdb-column-head {
68 | font-size: 0.7em;
69 | color: #999;
70 | padding: 3px 10px 0;
71 | }
72 |
73 | .rdb-column-drop-active {
74 | background-color: #e8f7ff;
75 | transition: all 0.3s ease-in-out;
76 | }
77 |
78 | .rdb-column-drop-hover {
79 | background-color: #ddfade;
80 | transition: all 0.3s ease-in-out;
81 | }
82 |
83 | /* ========================================================
84 | ** Groups / Group Headers
85 | */
86 |
87 | .rdb-group:last-child .rdb-columns .rdb-column {
88 | padding-bottom: 70px;
89 | }
90 |
91 | .rdb-group-header {
92 | overflow: hidden;
93 | *zoom: 1;
94 |
95 | border: 1px solid #ccc;
96 | border-radius: 2px;
97 | margin: 0 -5px;
98 | background-color: #f9f9f9;
99 | }
100 | .rdb-group:first-child .rdb-group-header {
101 | margin-top: 10px;
102 | }
103 |
104 | .rdb-group-header h4 {
105 | display: inline;
106 | border: none;
107 | padding: 0;
108 | margin: 0;
109 | }
110 |
111 | .rdb-group-header a {
112 | position: relative;
113 | padding: 0 12px 0 20px;
114 | cursor: pointer;
115 | display: block;
116 | }
117 | .rdb-group-header a:hover {
118 | text-decoration: none;
119 | border: none;
120 | background-color: #eee;
121 | }
122 |
123 | .rdb-group-header a::before {
124 | position: absolute;
125 | top: 45%;
126 | left: 6px;
127 | display: inline-block;
128 | border-right: 4px solid transparent;
129 | border-left: 4px solid transparent;
130 | border-top: 4px solid #888;
131 | border-bottom: 4px solid transparent;
132 | content: "";
133 | }
134 | .rdb-collapsed .rdb-group-header {
135 | margin-bottom: 10px;
136 | }
137 | .rdb-collapsed:last-child .rdb-group-header {
138 | margin-bottom: 40px;
139 | }
140 | .rdb-collapsed .rdb-group-header a::before {
141 | margin-top: -3px;
142 | left: 8px;
143 | border-left-color: #888;
144 | border-top-color: transparent;
145 | }
146 | .rdb-collapsed .rdb-columns {
147 | visibility: none;
148 | display: none;
149 | }
150 |
151 | .rdb-group-header span {
152 | margin-left: 5px;
153 | font-size: 0.8em;
154 | color: #999;
155 | }
156 |
157 | /* ========================================================
158 | ** Overall progress
159 | */
160 |
161 | .rdb-overall-progress {
162 | height: 8px;
163 | background-color: #599917;
164 | overflow: hidden;
165 | *zoom: 1;
166 |
167 | margin-bottom: 2px;
168 | }
169 |
170 | .rdb-overall-progress a {
171 | height: 8px;
172 |
173 | float: left;
174 | display: block;
175 | background-color: #f2da30;
176 | }
177 |
178 | .rdb-overall-progress a:first-child {
179 | background-color: #c00;
180 | }
181 |
182 | /* ========================================================
183 | ** Taskboard Column Dialog
184 | */
185 |
186 | .rdb-card-dialog {
187 | width: 100%;
188 | margin-top: 10px;
189 | margin-bottom: 8px;
190 | font-size: 12px;
191 | }
192 |
193 | .rdb-card-dialog .rdb-card-subject {
194 | height: auto;
195 | min-height: 4em;
196 | max-height: none;
197 | margin-top: 2px;
198 | }
199 |
200 | .rdb-card-dialog .rdb-priority {
201 | height: auto;
202 | padding-bottom: 1px;
203 | }
204 |
205 | .rdb-card-dialog-box {
206 | margin: 4px;
207 | border: 1px solid #ccc;
208 | background-color: #fff;
209 | padding: 10px;
210 | }
211 |
212 | .rdb-card-dialog-box ul {
213 | list-style: none;
214 | margin: 5px 0;
215 | padding: 0;
216 | }
217 |
218 | .rdb-card-dialog-box ul li a {
219 | display: block;
220 | padding: 5px 10px;
221 | border-radius: 2px;
222 | }
223 |
224 | .rdb-card-dialog-box ul li a:hover {
225 | background-color: #eee;
226 | }
227 |
--------------------------------------------------------------------------------
/assets/stylesheets/dashboard.ui.css:
--------------------------------------------------------------------------------
1 | /*
2 | ** Redmine Dashboard UI
3 | */
4 |
5 | /* =========================================================
6 | ** Dashboard Drop-Down-Menus
7 | */
8 |
9 | /* menu item */
10 | .rdb-menu {
11 | position: relative;
12 | display: inline-block;
13 | }
14 |
15 | .rdb-menu a.rdb-menu-link {
16 | padding-right: 18px;
17 | }
18 |
19 | .rdb-menu h1 a.rdb-menu-link,
20 | .rdb-menu h2 a.rdb-menu-link,
21 | .rdb-menu h3 a.rdb-menu-link,
22 | .rdb-menu h4 a.rdb-menu-link,
23 | .rdb-menu h5 a.rdb-menu-link {
24 | padding-top: 1px;
25 | padding-bottom: 1px;
26 | }
27 |
28 | .rdb-menu a.rdb-menu-link::before {
29 | position: absolute;
30 | top: 45%;
31 | right: 6px;
32 | display: inline-block;
33 | border-right: 4px solid transparent;
34 | border-left: 4px solid transparent;
35 | border-top: 4px solid #888;
36 | content: "";
37 | }
38 |
39 | .rdb-menu a.rdb-menu-link:hover,
40 | .rdb-menu-active a.rdb-menu-link {
41 | background-color: #eee;
42 | }
43 |
44 | /* =========================================================
45 | ** Dashboard UI Container
46 | */
47 |
48 | /* menu container */
49 | .rdb-container {
50 | position: absolute;
51 | left: -99999px;
52 |
53 | z-index: 1000;
54 | display: none;
55 | visibility: hidden;
56 |
57 | margin: 7px 0 0 0;
58 | padding: 0;
59 |
60 | border: 1px solid #ccc;
61 | background-color: #fff;
62 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
63 | border-radius: 2px;
64 | }
65 |
66 | .rdb-container-wrapper {
67 | overflow: auto;
68 |
69 | min-width: 160px;
70 | max-height: 400px;
71 | padding: 0 1px;
72 | }
73 | .rdb-container-wrapper::-webkit-scrollbar {
74 | width: 5px;
75 | background-color: #eee;
76 | }
77 | .rdb-container-wrapper::-webkit-scrollbar-thumb {
78 | background-color: #bbb;
79 | }
80 |
81 | .rdb-container::before {
82 | position: absolute;
83 | top: -7px;
84 | left: 9px;
85 | display: inline-block;
86 | border-right: 7px solid transparent;
87 | border-bottom: 7px solid #ccc;
88 | border-left: 7px solid transparent;
89 | border-bottom-color: rgba(0, 0, 0, 0.2);
90 | content: "";
91 | }
92 | .rdb-container::after {
93 | position: absolute;
94 | top: -6px;
95 | left: 10px;
96 | display: inline-block;
97 | border-right: 6px solid transparent;
98 | border-bottom: 6px solid white;
99 | border-left: 6px solid transparent;
100 | content: "";
101 | }
102 |
103 | .rdb-container-right::before {
104 | left: auto;
105 | right: 6px;
106 | }
107 | .rdb-container-right::after {
108 | left: auto;
109 | right: 7px;
110 | }
111 |
112 | .rdb-container-inlet {
113 | padding: 4px 8px;
114 | }
115 |
116 | .rdb-menu-active .rdb-container,
117 | .rdb-container.rdb-visible {
118 | display: block;
119 | visibility: visible;
120 | left: -3px;
121 | }
122 |
123 | .rdb-menu-active .rdb-container-right,
124 | .rdb-container-right.rdb-visible {
125 | display: block;
126 | visibility: visible;
127 | left: auto;
128 | right: -4px;
129 | }
130 |
131 | .rdb-container h3 {
132 | margin: 0;
133 | padding: 1px 6px 1px;
134 | font-size: 0.9em;
135 | font-weight: normal;
136 | }
137 |
138 | .rdb-container h3 ~ ul {
139 | padding-top: 0;
140 | }
141 |
142 | /* =========================================================
143 | ** Dashboard Container List
144 | */
145 |
146 | .rdb-container .rdb-list {
147 | border-bottom: 1px solid #ccc;
148 | }
149 | .rdb-container .rdb-list:last-child {
150 | border-bottom: none;
151 | }
152 |
153 | .rdb-small .rdb-list {
154 | font-size: 0.9em;
155 | }
156 |
157 | .rdb-list > ul {
158 | list-style: none;
159 | padding: 4px 0 2px;
160 | margin: 0;
161 | }
162 |
163 | .rdb-small .rdb-list > ul {
164 | padding-top: 2px;
165 | padding-bottom: 2px;
166 | }
167 |
168 | .rdb-list > ul > li > a,
169 | .rdb-list .rdb-multicheck a {
170 | display: block;
171 | padding: 6px 10px 6px 10px;
172 | margin: 0 0 1px 0;
173 | }
174 |
175 | .rdb-small .rdb-list > ul > li > a,
176 | .rdb-small .rdb-multicheck a {
177 | padding-top: 4px;
178 | padding-bottom: 4px;
179 | }
180 |
181 | .rdb-icons .rdb-list > ul > li > a {
182 | padding-left: 28px;
183 | }
184 |
185 | .rdb-list > ul > li > a:hover,
186 | .rdb-list .rdb-multicheck a:hover,
187 | .rdb-list .rdb-multicheck a.rdb-checkbox-link:hover ~ a {
188 | background-color: #eee;
189 | }
190 | .rdb-list .rdb-multicheck {
191 | display: table;
192 | width: 100%;
193 | }
194 | .rdb-list .rdb-multicheck a {
195 | padding-left: 5px;
196 | padding-right: 5px;
197 | display: table-cell;
198 | }
199 |
200 | .rdb-list .rdb-multicheck a.rdb-checkbox-link {
201 | padding-right: 0;
202 | padding-left: 24px;
203 | margin-right: 4px;
204 | /*float: left;*/
205 | width: 1px;
206 | height: 1em;
207 | background-position: 4px 50%;
208 | }
209 |
210 | /* =========================================================
211 | ** Dashboard Checkbox Links
212 | */
213 |
214 | .rdb-checkbox-link {
215 | padding-left: 20px;
216 | background-image: none;
217 | background-repeat: no-repeat;
218 | background-position: 2px 50%;
219 | }
220 |
221 | .rdb-checkbox-link.rdb-checkbox-link-enabled {
222 | background-image: url(../../../images/true.png);
223 | }
224 |
225 | .rdb-checkbox-link.rdb-checkbox-link-disabled {
226 | background-image: url(img/disabled_true.png);
227 | }
228 |
229 | .rdb-list > ul > li > a.rdb-checkbox-link {
230 | padding-left: 28px;
231 | background-position: 6px 50%;
232 | }
233 |
--------------------------------------------------------------------------------
/assets/stylesheets/img/brick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/brick.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/cog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/cog.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/disabled_true.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/disabled_true.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/menu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/menu.gif
--------------------------------------------------------------------------------
/assets/stylesheets/img/package.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/package.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/ticket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/ticket.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/time.png
--------------------------------------------------------------------------------
/assets/stylesheets/img/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgraichen/redmine_dashboard/5717094cf13a41288c50f2b81c2cf925174ece12/assets/stylesheets/img/user.png
--------------------------------------------------------------------------------
/config/default.yml:
--------------------------------------------------------------------------------
1 | # Configure default settings for dashboards here. They
2 | # apply to every dashboard on all projects.
3 | #
4 | # Remember to restart redmine to apply changed settings.
5 | # Also logout and login again as dashboard options are
6 | # also stored in the user session (cookie).
7 |
8 | # Default view mode.
9 | # Allowed values are: `card` or `compact`.
10 | view: card
11 |
12 | # Should subproject be included by default.
13 | # Allowed values are: `false` or `true`.
14 | include_subprojects: false
15 |
16 | # Default setting for assignee filter.
17 | # Allowed values are: `me` or `all`.
18 | assignee: me
19 |
20 | # Default setting for version filter.
21 | # Allowed values are: `latest` or `all`.
22 | version: latest
23 |
24 | # Should done issues be hidden by default
25 | # Allowed values are: `false` or `true`.
26 | hide_done: false
27 |
28 | # Should "Change assignee" option be enabled by default
29 | # Allowed values are: `false` or `true`
30 | change_assignee: false
31 |
--------------------------------------------------------------------------------
/config/locales/bg.yml:
--------------------------------------------------------------------------------
1 | ---
2 | bg:
3 | project_module_dashboard: Табло
4 | permission_view_dashboards: Преглед на таблата
5 | permission_configure_dashboards: Конфигурация на таблата
6 | menu_label_dashboard: Табло
7 | rdb_filter_version_all: Всички версии
8 | rdb_filter_assignee_none: Неназначен
9 | rdb_filter_assignee_others: Други
10 | rdb_options: Настройки
11 | rdb_options_hide_done: Скрий завършените задачи
12 | rdb_options_reset: Премахни филтър
13 | rdb_options_configure: Настройки
14 | rdb_options_group: Групиране на задачи
15 | rdb_group_category: Категория
16 | rdb_group_assignee: Назначен на
17 | rdb_group_tracker: Тракер
18 | rdb_group_priority: Приоритет
19 | rdb_group_version: Версия
20 | rdb_group_parent: Родителска задача
21 | rdb_group_project: Проект
22 | rdb_options_fullscreen: Цял екран
23 | rdb_legend_priorities: Приоритети
24 | rdb_column_done: Готово
25 | rdb_others: Други
26 | rdb_all_issues: Всички задачи
27 | rdb_no_parent: Без родителски задачи
28 | rdb_issue_menu_redmine_issue: Redmine задача
29 | rdb_issue_menu_edit: Редакция
30 | rdb_flash_invalid_request: "<p>Невалидна заявка.</p>"
31 |
--------------------------------------------------------------------------------
/config/locales/ca.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ca:
3 | project_module_dashboard: Taulers
4 | permission_view_dashboards: Mostra els taulers
5 | permission_configure_dashboards: Configura els taulers
6 | menu_label_dashboard: Tauler
7 | rdb_taskboard: Assumptes
8 | rdb_planningboard: 'Planificació '
9 | rdb_filter_version_all: Totes les versions
10 | rdb_filter_version_unassigned: Sense assignar
11 | rdb_filter_tracker_all: Tots els rastrejadors
12 | rdb_filter_tracker_multiple: Diversos rastrejadors
13 | rdb_filter_category_all: Totes les categories
14 | rdb_filter_category_multiple: Diverses categories
15 | rdb_filter_assignee_all: Tots els assignats
16 | rdb_filter_assignee_me: Els meus assumptes
17 | rdb_filter_assignee_none: Sense assignar
18 | rdb_filter_assignee_others: Altres
19 | rdb_options: Opcions
20 | rdb_options_change_assignee: 'Canvia l''assignació '
21 | rdb_options_change_assignee_info: Canvia l'assignació a l'usuari actual quan l'assumpte es mou
22 | rdb_options_hide_done: Amaga els assumptes tancats
23 | rdb_options_hide_done_info: Amaga assumptes tancats i redueix la columna "fet"
24 | rdb_options_reset: Restableix filtre
25 | rdb_options_reset_info: Restableix tots els filtres
26 | rdb_options_configure: Configura
27 | rdb_options_include_subprojects: Inclou subprojects
28 | rdb_options_view: Veure
29 | rdb_options_issue_view: Vista d'assumpte
30 | rdb_options_issue_view_card: Targeta
31 | rdb_options_issue_view_compact: Compacte
32 | rdb_options_group: Agrupació d'assumptes
33 | rdb_group_none: Sense agrupar
34 | rdb_group_category: Categoria
35 | rdb_group_assignee: Assignat
36 | rdb_group_tracker: Rastrejador
37 | rdb_group_priority: Prioritat
38 | rdb_group_version: 'Versió '
39 | rdb_group_parent: Assumpte pare
40 | rdb_group_project: Projecte
41 | rdb_options_board_view: Vista del tauler
42 | rdb_options_board_view_compact: 'Clàssic '
43 | rdb_options_board_view_outline: Esquema
44 | rdb_options_columns: Columnes del tauler
45 | rdb_options_fullscreen: Pantalla sencera
46 | rdb_legend_priorities: Prioritats
47 | rdb_legend_warnings: Avisos
48 | rdb_issue_overdue: Endarrerit
49 | rdb_property_time: <span title="Spend time">%{actual}</span> / <span title="Estimated time">%{estimated}</span>
50 | rdb_not_available: N/D
51 | rdb_column_done: Fet
52 | rdb_x_issues:
53 | one: 1 Assumpte
54 | other: "%{count} Assumptes"
55 | rdb_dialog_update_issue_title: Actualitza l'assumpte
56 | rdb_dialog_update_issue_status: 'Escull un nou estat per l''assumpte '
57 | rdb_other_issues: Altres assumptes <span>(%{count})</span>
58 | rdb_unassigned: Sense assignar
59 | rdb_others: Altres
60 | rdb_all_issues: Tots els assumptes
61 | rdb_no_parent: Sense assumpte pare
62 | rdb_issue_menu_progress_title: 'Posa al dia l''actualització '
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Assumpte de Redmine
65 | rdb_issue_menu_show: Veure
66 | rdb_issue_menu_edit: Edita
67 | rdb_issue_menu_assign_me: Assigna-m'ho
68 | rdb_issue_menu_unassign_me: Desassigna-m'ho
69 | rdb_flash_illegal_workflow_action: "<p>No tens permís per realitzar l'acció següent:</p><p>Moure<b>%{issue}</b> des de <b>%{source}</b> a <b>%{target}</b>.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>No s'ha trobat el bloqueig de la versió </b>:No es pot actualitzar l'assumpte sense el bloqueig de la versió. Torna-ho a provar"
71 | rdb_flash_stale_object: "<p>S'ha intentat actualitzar un assumpte antic: <b>%{issue}</b>. Comprova el canvis i torna-ho a intentar."
72 | rdb_flash_invalid_request: "<p> Sol·licitud no vàlida.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/cs.yml:
--------------------------------------------------------------------------------
1 | ---
2 | cs:
3 | project_module_dashboard: Nástěnka
4 | permission_view_dashboards: Zobrazit nástěnky
5 | permission_configure_dashboards: Nastavit nástěnky
6 | menu_label_dashboard: Nástěnka
7 | rdb_taskboard: Nástěnka úkolů
8 | rdb_planningboard: Plánovací nástěnka
9 | rdb_filter_version_all: Všechny verze
10 | rdb_filter_version_unassigned: Nepřiřazené
11 | rdb_filter_tracker_all: Všechny fronty
12 | rdb_filter_tracker_multiple: Více front
13 | rdb_filter_category_all: Všechny kategorie
14 | rdb_filter_category_multiple: Více kategorií
15 | rdb_filter_assignee_all: Přiřazené všem
16 | rdb_filter_assignee_me: Mé úkoly
17 | rdb_filter_assignee_none: Nepřiřazené
18 | rdb_filter_assignee_others: Ostatní
19 | rdb_options: Možnosti
20 | rdb_options_change_assignee: Změnit přiřazeného uživatele
21 | rdb_options_change_assignee_info: Při přesunu úkolu jej přiřadit aktuálně přihlášenému uživateli.
22 | rdb_options_hide_done: Skrýt uzavřené úkoly
23 | rdb_options_hide_done_info: Skrýt uzavřené úkoly a zmenšit sloupec hotových úkolů.
24 | rdb_options_reset: Zrušit filtry
25 | rdb_options_reset_info: Obnoví všechny filtry do výchozího nastavení.
26 | rdb_options_configure: Konfigurovat
27 | rdb_options_include_subprojects: Zahrnout podprojekty
28 | rdb_options_view: Zobrazení
29 | rdb_options_issue_view: Zobrazení úkolů
30 | rdb_options_issue_view_card: Karty
31 | rdb_options_issue_view_compact: Kompaktní
32 | rdb_options_group: Seskupit úkoly podle
33 | rdb_group_none: Neseskupovat
34 | rdb_group_category: Kategorie
35 | rdb_group_assignee: Přiřazeno
36 | rdb_group_tracker: Fronty
37 | rdb_group_priority: Priority
38 | rdb_group_version: Verze
39 | rdb_group_parent: Rodičovského úkolu
40 | rdb_group_project: Projektu
41 | rdb_options_board_view: Zobrazení nástěnky
42 | rdb_options_board_view_compact: Klasické
43 | rdb_options_board_view_outline: Hrubý přehled
44 | rdb_options_columns: Sloupce nástěnky
45 | rdb_options_fullscreen: Celá obrazovka
46 | rdb_legend_priorities: Priority
47 | rdb_legend_warnings: Varování
48 | rdb_issue_overdue: Zpožděné
49 | rdb_property_time: <span title="Strávený čas">%{actual}</span> / <span title="Odhadovaný čas">%{estimated}</span>
50 | rdb_not_available: Nedostupné
51 | rdb_column_done: Hotové
52 | rdb_x_issues:
53 | one: 1 úkol
54 | few: "%{count} úkoly"
55 | many: "%{count} úkolů"
56 | other: "%{count} úkolů"
57 | rdb_dialog_update_issue_title: Upravit úkol
58 | rdb_dialog_update_issue_status: Zvolte nový stav úkolu
59 | rdb_other_issues: Ostatní úkoly <span>(%{count})</span>
60 | rdb_unassigned: Nepřiřazeno
61 | rdb_others: Ostatní
62 | rdb_all_issues: Všechny úkoly
63 | rdb_no_parent: Bez nadřazeného úkolu
64 | rdb_issue_menu_progress_title: Aktualizovat % hotovo
65 | rdb_issue_menu_progress: "%{count}%"
66 | rdb_issue_menu_redmine_issue: Úkol
67 | rdb_issue_menu_show: Zobrazit
68 | rdb_issue_menu_edit: Upravit
69 | rdb_issue_menu_assign_me: Přiřadit mně
70 | rdb_issue_menu_unassign_me: Zrušit přiřazení mně
71 | rdb_flash_illegal_workflow_action: "<p>Nemáte oprávnění provést následující akci v průběhu prací:</p><p>Přesunout <b>%{issue}</b> z <b>%{source}</b> na <b>%{target}</b>.</p>"
72 | rdb_flash_missing_lock_version: "<p><b>Chybějící uzamčená verze</b>: Nelze aktualizovat úkol bez uzamčené verze. Opakujte akci."
73 | rdb_flash_stale_object: "<p>Pokus o odeslání starých změn úkolu <b>%{issue}</b>. Úkol byl nejspíše upraven jiným uživatelem. Zkontrolujte změny a opakujte akci."
74 | rdb_flash_invalid_request: "<p>Neplatný požadavek.</p>"
75 |
--------------------------------------------------------------------------------
/config/locales/de.yml:
--------------------------------------------------------------------------------
1 | ---
2 | de:
3 | project_module_dashboard: Dashboard
4 | permission_view_dashboards: Dashboards anschauen
5 | permission_configure_dashboards: Dashboards konfigurieren
6 | menu_label_dashboard: Dashboard
7 | rdb_taskboard: Task Board
8 | rdb_planningboard: Planning Board
9 | rdb_filter_version_all: Alle Versionen
10 | rdb_filter_version_unassigned: Keine Version
11 | rdb_filter_tracker_all: Alle Trackers
12 | rdb_filter_tracker_multiple: Mehrere Trackers
13 | rdb_filter_category_all: Alle Kategorien
14 | rdb_filter_category_multiple: Mehrere Kategorien
15 | rdb_filter_assignee_all: Alle Tickets
16 | rdb_filter_assignee_me: Meine Tickets
17 | rdb_filter_assignee_none: Nicht zugewiesen
18 | rdb_filter_assignee_others: Andere
19 | rdb_options: Optionen
20 | rdb_options_change_assignee: Ändere Verantwortlichen
21 | rdb_options_change_assignee_info: Ändere Verantwortlichen auf aktuellen Benutzer wenn ein Ticket verschoben wird.
22 | rdb_options_hide_done: Verberge fertige Tickets
23 | rdb_options_hide_done_info: Verbirgt fertige Tickets und verkleinert Fertig-Spalte.
24 | rdb_options_reset: Filter zurücksetzen
25 | rdb_options_reset_info: Setzt alle Filter auf Standardeinstellungen zurück.
26 | rdb_options_configure: Konfigurieren
27 | rdb_options_include_subprojects: Unterprojekte einbeziehen
28 | rdb_options_view: Ansicht
29 | rdb_options_issue_view: Ticket-Ansicht
30 | rdb_options_issue_view_card: Karte
31 | rdb_options_issue_view_compact: Kompakt
32 | rdb_options_group: Gruppierung
33 | rdb_group_none: Keine Gruppierung
34 | rdb_group_category: Kategorie
35 | rdb_group_assignee: Verantwortlicher
36 | rdb_group_tracker: Tracker
37 | rdb_group_priority: Priorität
38 | rdb_group_version: Version
39 | rdb_group_parent: Übergeordnete Aufgabe
40 | rdb_group_project: Projekt
41 | rdb_options_board_view: Board Ansicht
42 | rdb_options_board_view_compact: Klassisch (Kompakt)
43 | rdb_options_board_view_outline: Outline
44 | rdb_options_columns: Spalten
45 | rdb_options_fullscreen: Vollbildschirm
46 | rdb_legend_priorities: Prioritäten
47 | rdb_legend_warnings: Warnungen
48 | rdb_issue_overdue: Überfällig
49 | rdb_property_time: <span title="Verbrauchte Zeit">%{actual}</span> / <span title="Geschätzte Zeit">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Fertig
52 | rdb_x_issues:
53 | one: 1 Ticket
54 | other: "%{count} Tickets"
55 | rdb_dialog_update_issue_title: Ticket aktualisieren
56 | rdb_dialog_update_issue_status: Wähle neuen Ticket-Status
57 | rdb_other_issues: Andere Tickets <span>(%{count})</span>
58 | rdb_unassigned: Nicht zugewiesen
59 | rdb_others: Andere
60 | rdb_all_issues: Alle Tickets
61 | rdb_no_parent: Keine übergeordneten Aufgaben
62 | rdb_issue_menu_progress_title: Fortschritt ändern
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine Ticket
65 | rdb_issue_menu_show: Anzeigen
66 | rdb_issue_menu_edit: Bearbeiten
67 | rdb_issue_menu_assign_me: Mich zuweisen
68 | rdb_issue_menu_unassign_me: Zuweisung entfernen
69 | rdb_flash_illegal_workflow_action: "<p>Diese Änderung verstößt gegen den festgelegten Arbeitsfluss:</p><p><b>%{issue}</b> von <b>%{source}</b> nach <b>%{target}</b> ändern.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>Lock-Version fehlt</b>: Ticket kann nicht aktualisiert werden. Bitte nochmal probieren."
71 | rdb_flash_stale_object: "<p>Ticket wurde verändert: <b>%{issue}</b>. Überprüfe die Änderungen und probiere es erneut."
72 | rdb_flash_invalid_request: "<p>Ungültige Anfrage.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | ---
2 | en:
3 | project_module_dashboard: Dashboard
4 | permission_view_dashboards: View Dashboards
5 | permission_configure_dashboards: Configure Dashboards
6 | menu_label_dashboard: Dashboard
7 | rdb_taskboard: Task Board
8 | rdb_planningboard: Planning Board
9 | rdb_filter_version_all: All Versions
10 | rdb_filter_version_unassigned: Unassigned
11 | rdb_filter_tracker_all: All Trackers
12 | rdb_filter_tracker_multiple: Multiple Trackers
13 | rdb_filter_category_all: All Categories
14 | rdb_filter_category_multiple: Multiple Categories
15 | rdb_filter_assignee_all: All Assignees
16 | rdb_filter_assignee_me: My Issues
17 | rdb_filter_assignee_none: Unassigned
18 | rdb_filter_assignee_others: Others
19 | rdb_options: Options
20 | rdb_options_change_assignee: Change assignee
21 | rdb_options_change_assignee_info: Change assignee to current user when issue is moved.
22 | rdb_options_hide_done: Hide closed issues
23 | rdb_options_hide_done_info: Hide closed issues and makes done column smaller.
24 | rdb_options_reset: Reset filter
25 | rdb_options_reset_info: Reset all filters back to default
26 | rdb_options_configure: Configure
27 | rdb_options_include_subprojects: Include subprojects
28 | rdb_options_view: View
29 | rdb_options_issue_view: Issue View
30 | rdb_options_issue_view_card: Card
31 | rdb_options_issue_view_compact: Compact
32 | rdb_options_group: Issue Grouping
33 | rdb_group_none: No Grouping
34 | rdb_group_category: Category
35 | rdb_group_assignee: Assignee
36 | rdb_group_tracker: Tracker
37 | rdb_group_priority: Priority
38 | rdb_group_version: Version
39 | rdb_group_parent: Parent Task
40 | rdb_group_project: Project
41 | rdb_options_board_view: Board View
42 | rdb_options_board_view_compact: Classic
43 | rdb_options_board_view_outline: Outline
44 | rdb_options_columns: Board Columns
45 | rdb_options_fullscreen: Full Screen
46 | rdb_legend_priorities: Priorities
47 | rdb_legend_warnings: Warnings
48 | rdb_issue_overdue: Overdue
49 | rdb_property_time: <span title="Spend time">%{actual}</span> / <span title="Estimated time">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Done
52 | rdb_x_issues:
53 | one: 1 Issue
54 | other: "%{count} Issues"
55 | rdb_dialog_update_issue_title: Update Issue
56 | rdb_dialog_update_issue_status: Choose new issue status
57 | rdb_other_issues: Other Issues <span>(%{count})</span>
58 | rdb_unassigned: Unassigned
59 | rdb_others: Others
60 | rdb_all_issues: All Issues
61 | rdb_no_parent: No parent tasks
62 | rdb_issue_menu_progress_title: Update progress
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine Issue
65 | rdb_issue_menu_show: Show
66 | rdb_issue_menu_edit: Edit
67 | rdb_issue_menu_assign_me: Assign me
68 | rdb_issue_menu_unassign_me: Unassign me
69 | rdb_flash_illegal_workflow_action: "<p>You are not allowed to perform this workflow action:</p><p>Move <b>%{issue}</b> from <b>%{source}</b> to <b>%{target}</b>.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>Missing lock version</b>: Cannot update issue without lock version. Try again."
71 | rdb_flash_stale_object: "<p>Attempted to update a stale issue: <b>%{issue}</b>. Check changes and try again."
72 | rdb_flash_invalid_request: "<p>Invalid request.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/es.yml:
--------------------------------------------------------------------------------
1 | ---
2 | es:
3 | project_module_dashboard: Tablero
4 | permission_view_dashboards: Ver tableros
5 | permission_configure_dashboards: Configurar tableros
6 | menu_label_dashboard: Tablero
7 | rdb_taskboard: Tareas
8 | rdb_planningboard: Planificación
9 | rdb_filter_version_all: Todas las versiones
10 | rdb_filter_version_unassigned: Sin asignar
11 | rdb_filter_tracker_all: Todos los rastreadores
12 | rdb_filter_tracker_multiple: Varios rastreadores
13 | rdb_filter_category_all: Todas las categorías
14 | rdb_filter_category_multiple: Múltiples categorías
15 | rdb_filter_assignee_all: Todas las asignadas
16 | rdb_filter_assignee_me: Mis tareas
17 | rdb_filter_assignee_none: Sin asignar
18 | rdb_filter_assignee_others: Otros
19 | rdb_options: Opciones
20 | rdb_options_change_assignee: Cambiar asignación
21 | rdb_options_change_assignee_info: Cambiar asignación al usuario actual cuando la tarea se mueva
22 | rdb_options_hide_done: Ocultar tareas cerradas
23 | rdb_options_hide_done_info: Oculta tareas cerradas y disminuye la columna "hecho"
24 | rdb_options_reset: Restablecer filtro
25 | rdb_options_reset_info: Restablecer todos los filtros
26 | rdb_options_configure: Configurar
27 | rdb_options_include_subprojects: Incluir proyectos hijo
28 | rdb_options_view: Ver
29 | rdb_options_issue_view: Vista de tarea
30 | rdb_options_issue_view_card: Tarjeta
31 | rdb_options_issue_view_compact: Compacta
32 | rdb_options_group: Agrupación de tareas
33 | rdb_group_none: Sin agrupar
34 | rdb_group_category: Categoría
35 | rdb_group_assignee: Asignación
36 | rdb_group_tracker: Rastreador
37 | rdb_group_priority: Prioridad
38 | rdb_group_version: Versión
39 | rdb_group_parent: Tarea padre
40 | rdb_group_project: Proyecto
41 | rdb_options_board_view: Vista de tablero
42 | rdb_options_board_view_compact: Clásico
43 | rdb_options_board_view_outline: Esquema
44 | rdb_options_columns: Columnas de tablero
45 | rdb_options_fullscreen: Pantalla completa
46 | rdb_legend_priorities: Prioridades
47 | rdb_legend_warnings: Advertencias
48 | rdb_issue_overdue: Atrasado
49 | rdb_property_time: <span title="Spend time">%{actual}</span> / <span title="Estimated time">%{estimated}</span>
50 | rdb_not_available: N/D
51 | rdb_column_done: Hecho
52 | rdb_x_issues:
53 | one: 1 Tarea
54 | many: "%{count} Tareas"
55 | other: "%{count} Tareas"
56 | rdb_dialog_update_issue_title: Actualizar tarea
57 | rdb_dialog_update_issue_status: Escoger nuevo estado de la tarea
58 | rdb_other_issues: Otras tareas <span>(%{count})</span>
59 | rdb_unassigned: Sin asignar
60 | rdb_others: Otros
61 | rdb_all_issues: Todas las tareas
62 | rdb_no_parent: Sin tareas padre
63 | rdb_issue_menu_progress_title: Progreso de actualización
64 | rdb_issue_menu_progress: "%{count}%"
65 | rdb_issue_menu_redmine_issue: Tarea de Redmine
66 | rdb_issue_menu_show: Ver
67 | rdb_issue_menu_edit: Editar
68 | rdb_issue_menu_assign_me: Asignar a mi
69 | rdb_issue_menu_unassign_me: Desasignar
70 | rdb_flash_illegal_workflow_action: "<p>No tienes permiso para realizar la siguiente acción:</p><p>Mover <b>%{issue}</b> de <b>%{source}</b> a <b>%{target}</b>.</p>"
71 | rdb_flash_missing_lock_version: "<p><b>Bloqueo de la versión no encontrado</b>: No puedes actualizar la tarea sin bloquear la versión. Inténtalo de nuevo"
72 | rdb_flash_stale_object: "<p>Se ha intentado actualizar un tarea antigua: <b>%{issue}</b>. Comprueba los cambios e inténtalo de nuevo."
73 | rdb_flash_invalid_request: "<p>Solicitud no válida.</p>"
74 |
--------------------------------------------------------------------------------
/config/locales/fr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | fr:
3 | project_module_dashboard: Tableau de bord
4 | permission_view_dashboards: Voir les tableaux de bord
5 | permission_configure_dashboards: Configurer les tableaux de bord
6 | menu_label_dashboard: Tableau de bord
7 | rdb_taskboard: Tableau des tâches
8 | rdb_planningboard: Tableau du planning
9 | rdb_filter_version_all: Toutes les versions
10 | rdb_filter_version_unassigned: Non assigné
11 | rdb_filter_tracker_all: Tous les trackers
12 | rdb_filter_tracker_multiple: Trackers multiples
13 | rdb_filter_category_all: Toutes les catégories
14 | rdb_filter_category_multiple: Catégories multiples
15 | rdb_filter_assignee_all: Tous les membres assignés
16 | rdb_filter_assignee_me: Mes demandes
17 | rdb_filter_assignee_none: Non assigné
18 | rdb_filter_assignee_others: Autres
19 | rdb_options: Options
20 | rdb_options_change_assignee: Changer l'assignation
21 | rdb_options_change_assignee_info: Assigner à l'utilisateur courant lorsque la demande est déplacée.
22 | rdb_options_hide_done: Masquer les demandes fermées
23 | rdb_options_hide_done_info: Masquer les demandes fermées et réduire la colonne Terminé
24 | rdb_options_reset: Réinitialiser le filtre
25 | rdb_options_reset_info: Réinitialiser tous les filtres
26 | rdb_options_configure: Configurer
27 | rdb_options_include_subprojects: Inclure les sous-projets
28 | rdb_options_view: Affichage
29 | rdb_options_issue_view: Affichage des demandes
30 | rdb_options_issue_view_card: Carte
31 | rdb_options_issue_view_compact: Compact
32 | rdb_options_group: Regrouper par demande
33 | rdb_group_none: Pas de regroupement
34 | rdb_group_category: Catégorie
35 | rdb_group_assignee: 'Assigné à '
36 | rdb_group_tracker: Tracker
37 | rdb_group_priority: Priorité
38 | rdb_group_version: Version
39 | rdb_group_parent: Tâche parente
40 | rdb_group_project: Projet
41 | rdb_options_board_view: Affichage du tableau
42 | rdb_options_board_view_compact: Classique
43 | rdb_options_board_view_outline: Contour
44 | rdb_options_columns: Colonnes du tableau
45 | rdb_options_fullscreen: Plein écran
46 | rdb_legend_priorities: Priorités
47 | rdb_legend_warnings: Avertissements
48 | rdb_issue_overdue: En retard
49 | rdb_property_time: <span title="Temps passé">%{actual}</span> / <span title="Temps estimé">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Terminé
52 | rdb_x_issues:
53 | one: 1 demande
54 | many: "%{count} demandes"
55 | other: "%{count} demandes"
56 | rdb_dialog_update_issue_title: Mettre à jour la demande
57 | rdb_dialog_update_issue_status: Choisir le statut des nouvelles demandes
58 | rdb_other_issues: Autres demandes <span>(%{count})</span>
59 | rdb_unassigned: Non assignée
60 | rdb_others: Autres
61 | rdb_all_issues: Toutes les demandes
62 | rdb_no_parent: Pas de tâche parente
63 | rdb_issue_menu_progress_title: Mettre à jour la progression
64 | rdb_issue_menu_progress: "%{count}%"
65 | rdb_issue_menu_redmine_issue: Demande Redmine
66 | rdb_issue_menu_show: Voir
67 | rdb_issue_menu_edit: Editer
68 | rdb_issue_menu_assign_me: Assigner à moi
69 | rdb_issue_menu_unassign_me: Ne plus assigner à moi
70 | rdb_flash_illegal_workflow_action: "<p>Vous n'êtes pas autorisé à effectuer cette action du workflow :</p><p>Déplacer <b>%{issue}</b> de <b>%{source}</b> à <b>%{target}</b>.</p>"
71 | rdb_flash_missing_lock_version: "<p><b>Version de verrou manquante</b> : impossible de mettre à jour la demande sans version de verrou. Veuillez réessayer."
72 | rdb_flash_stale_object: "<p>Tentative de mise à jour d'une demande dépassée : <b>%{issue}</b>. Vérifiez les changements puis réessayez."
73 | rdb_flash_invalid_request: "<p>Requête non valide.</p>"
74 |
--------------------------------------------------------------------------------
/config/locales/it.yml:
--------------------------------------------------------------------------------
1 | ---
2 | it:
3 | project_module_dashboard: Dashboard
4 | permission_view_dashboards: Visualizza Dashboards
5 | permission_configure_dashboards: Configura Dashboards
6 | menu_label_dashboard: Dashboard
7 | rdb_taskboard: Quadro attività
8 | rdb_planningboard: Quadro pianificazione
9 | rdb_filter_version_all: Tutte le versioni
10 | rdb_filter_version_unassigned: Non assegnato
11 | rdb_filter_tracker_all: Tutti i Trackers
12 | rdb_filter_tracker_multiple: Trackers Multipli
13 | rdb_filter_category_all: Tutte le Categorie
14 | rdb_filter_category_multiple: Categorie Multiple
15 | rdb_filter_assignee_all: Tutti gli Assegnatari
16 | rdb_filter_assignee_me: Le mie Segnalazioni
17 | rdb_filter_assignee_none: Non assegnato
18 | rdb_filter_assignee_others: Altri
19 | rdb_options: Opzioni
20 | rdb_options_change_assignee: Cambio assegnatario
21 | rdb_options_change_assignee_info: Cambia assegnatario in utente attuale allo spostamento della segnalazione.
22 | rdb_options_hide_done: Nascondi le segnalazioni chiuse
23 | rdb_options_hide_done_info: Nascondi le segnalazioni chiuse e riduci le colonne del completato.
24 | rdb_options_reset: Reset filtro
25 | rdb_options_reset_info: Reset di tutti i filtri ai valori di default
26 | rdb_options_configure: Configura
27 | rdb_options_include_subprojects: Includi i sottoprogetti
28 | rdb_options_view: Vista
29 | rdb_options_issue_view: Vista delle segnalazioni
30 | rdb_options_issue_view_card: Card
31 | rdb_options_issue_view_compact: Compatta
32 | rdb_options_group: Raggruppamento segnalazioni
33 | rdb_group_none: Nessun raggruppamento
34 | rdb_group_category: Categoria
35 | rdb_group_assignee: Assegnatario
36 | rdb_group_tracker: Tracker
37 | rdb_group_priority: Priorita'
38 | rdb_group_version: Versione
39 | rdb_group_parent: Task padre
40 | rdb_group_project: Progetto
41 | rdb_options_board_view: Board View
42 | rdb_options_board_view_compact: Classic
43 | rdb_options_board_view_outline: Outline
44 | rdb_options_columns: Board Columns
45 | rdb_options_fullscreen: Full Screen
46 | rdb_legend_priorities: Priorita'
47 | rdb_legend_warnings: Avvisi
48 | rdb_issue_overdue: Superato
49 | rdb_property_time: <span title="Tempo impiegato">%{actual}</span> / <span title="Tempo stimato">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Fatto
52 | rdb_x_issues:
53 | one: 1 segnalazione
54 | many: "%{count} segnalazioni"
55 | other: "%{count} segnalazioni"
56 | rdb_dialog_update_issue_title: Aggiorna la segnalazione
57 | rdb_dialog_update_issue_status: Scegli lo stato della nuova segnalazione
58 | rdb_other_issues: Altre segnalazioni <span>(%{count})</span>
59 | rdb_unassigned: Non assegnata
60 | rdb_others: Altri
61 | rdb_all_issues: Tutte le segnalazioni
62 | rdb_no_parent: Nessun task padre
63 | rdb_issue_menu_progress_title: Aggiorna avanzamento
64 | rdb_issue_menu_progress: "%{count}%"
65 | rdb_issue_menu_redmine_issue: Segnalazione Redmine
66 | rdb_issue_menu_show: Mostra
67 | rdb_issue_menu_edit: Edita
68 | rdb_issue_menu_assign_me: Assegnalo a me
69 | rdb_issue_menu_unassign_me: Rimuovi l'assegnazione
70 | rdb_flash_illegal_workflow_action: "<p>Non sei autorizzato a compiere quest'azione sul workflow:</p><p>Sposta <b>%{issue}</b> da <b>%{source}</b> a <b>%{target}</b>.</p>"
71 | rdb_flash_missing_lock_version: "<p><b>Versione lock mancante</b>: Impossibile aggiornare la segnalazione senza il lock della versione. Riprova."
72 | rdb_flash_stale_object: "<p>Tentativo di aggiornare una segnalazione scaduta: <b>%{issue}</b>. Controlla le modifiche e riprova."
73 | rdb_flash_invalid_request: "<p>Richiesta non valida.</p>"
74 |
--------------------------------------------------------------------------------
/config/locales/ja.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ja:
3 | project_module_dashboard: ダッシュボード
4 | permission_view_dashboards: ダッシュボードを表示
5 | permission_configure_dashboards: ダッシュボードの設定
6 | menu_label_dashboard: ダッシュボード
7 | rdb_taskboard: タスクボード
8 | rdb_planningboard: 計画ボード
9 | rdb_filter_version_all: すべてのバージョン
10 | rdb_filter_version_unassigned: 未割当
11 | rdb_filter_tracker_all: すべてのトラッカー
12 | rdb_filter_tracker_multiple: 複数のトラッカー
13 | rdb_filter_category_all: すべてのカテゴリ
14 | rdb_filter_category_multiple: 複数のカテゴリ
15 | rdb_filter_assignee_all: すべての担当
16 | rdb_filter_assignee_me: 自分
17 | rdb_filter_assignee_none: 未割当
18 | rdb_filter_assignee_others: 自分以外
19 | rdb_options: オプション
20 | rdb_options_change_assignee: 担当者を変更する
21 | rdb_options_change_assignee_info: ステータスを移動する際に担当者が自分に変更されます
22 | rdb_options_hide_done: 完了は表示しない
23 | rdb_options_hide_done_info: ステータスが完了しているチケットは表示しません
24 | rdb_options_reset: フィルタをリセット
25 | rdb_options_reset_info: デフォルトのフィルタにリセットします
26 | rdb_options_configure: 設定
27 | rdb_options_include_subprojects: 子プロジェクトを含めます
28 | rdb_options_view: 表示
29 | rdb_options_issue_view: ボードの表示形式
30 | rdb_options_issue_view_card: カード形式
31 | rdb_options_issue_view_compact: 簡易
32 | rdb_options_group: グルーピング
33 | rdb_group_none: グルーピングなし
34 | rdb_group_category: カテゴリ
35 | rdb_group_assignee: 担当
36 | rdb_group_tracker: トラッカー
37 | rdb_group_priority: 優先度
38 | rdb_group_version: バージョン
39 | rdb_group_parent: 親チケット
40 | rdb_group_project: プロジェクト
41 | rdb_options_board_view: ボードビュー
42 | rdb_options_board_view_compact: クラシック
43 | rdb_options_board_view_outline: アウトライン
44 | rdb_options_columns: ボードに表示するチケットのステータス
45 | rdb_options_fullscreen: 全画面表示
46 | rdb_legend_priorities: 優先度
47 | rdb_legend_warnings: 警告
48 | rdb_issue_overdue: 期日超過
49 | rdb_not_available: 利用不可
50 | rdb_column_done: 完了
51 | rdb_x_issues:
52 | other: "%{count} 件のチケット"
53 | one: "%{count} 件のチケット"
54 | rdb_dialog_update_issue_title: チケットの更新
55 | rdb_dialog_update_issue_status: チケットのステータスを選んでください
56 | rdb_other_issues: 他のチケット <span>(%{count})</span>
57 | rdb_unassigned: 未割当
58 | rdb_others: その他
59 | rdb_all_issues: すべてのチケット
60 | rdb_no_parent: 親のないチケット
61 | rdb_issue_menu_progress_title: 進捗率の変更
62 | rdb_issue_menu_progress: "%{count}%"
63 | rdb_issue_menu_redmine_issue: チケット
64 | rdb_issue_menu_show: チケットを表示
65 | rdb_issue_menu_edit: チケットの編集
66 | rdb_issue_menu_assign_me: 担当を自分にする
67 | rdb_issue_menu_unassign_me: 担当を未割当にする
68 | rdb_flash_illegal_workflow_action: "<p>このワークフローアクションを実行することはできません。</p><p><b>%{issue} </b><b>%{source}</b> から<b>%{target}</b>.</p>"
69 | rdb_flash_missing_lock_version: "<p><b>ロックされたバージョンがありません</b>:もう一度やり直してください"
70 | rdb_flash_stale_object: "<p>チケットはすでに更新されています: <b>%{issue}</b>. 変更を確認して再度更新してください"
71 | rdb_flash_invalid_request: "<p>リクエストは無効です</p>"
72 |
--------------------------------------------------------------------------------
/config/locales/ko.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ko:
3 | project_module_dashboard: 현황판
4 | permission_view_dashboards: 현황판 보기
5 | permission_configure_dashboards: 현황판 설정
6 | menu_label_dashboard: 현황판
7 | rdb_taskboard: 작업판
8 | rdb_planningboard: 계획판
9 | rdb_filter_version_all: 모든 버전
10 | rdb_filter_version_unassigned: 버전 없음
11 | rdb_filter_tracker_all: 모든 유형
12 | rdb_filter_tracker_multiple: 여러 유형
13 | rdb_filter_category_all: 모든 범주
14 | rdb_filter_category_multiple: 여러 범주
15 | rdb_filter_assignee_all: 모든 담당자
16 | rdb_filter_assignee_me: 내 일감
17 | rdb_filter_assignee_none: 담당자 없음
18 | rdb_filter_assignee_others: 다른 사람
19 | rdb_options: 옵션
20 | rdb_options_change_assignee: 담당자 변경
21 | rdb_options_change_assignee_info: 일감을 옮기면서 담당자를 자신으로 변경
22 | rdb_options_hide_done: 닫힌 일감 숨기기
23 | rdb_options_hide_done_info: 닫힌 일감을 숨기고 종료됨 영역을 작게 표시
24 | rdb_options_reset: 필터 재설정
25 | rdb_options_reset_info: 모든 필터를 재설정하여 기본상대로 돌가감
26 | rdb_options_configure: 설정
27 | rdb_options_include_subprojects: 하위 프로젝트 포함
28 | rdb_options_view: 보기
29 | rdb_options_issue_view: 이슈 보기
30 | rdb_options_issue_view_card: 카드
31 | rdb_options_issue_view_compact: 작게 보기
32 | rdb_options_group: 이슈 묶기
33 | rdb_group_none: 묶음 없음
34 | rdb_group_category: 범주
35 | rdb_group_assignee: 담당자
36 | rdb_group_tracker: 유형
37 | rdb_group_priority: 우선순위
38 | rdb_group_version: 버전
39 | rdb_group_parent: 상위 일감
40 | rdb_group_project: 프로젝트
41 | rdb_options_board_view: 현황판 보기
42 | rdb_options_board_view_compact: 클래식
43 | rdb_options_board_view_outline: 개괄
44 | rdb_options_columns: 현황판 열
45 | rdb_options_fullscreen: 전체화면
46 | rdb_legend_priorities: 우선순위
47 | rdb_legend_warnings: 경고
48 | rdb_issue_overdue: 기한 지남
49 | rdb_not_available: N/A
50 | rdb_column_done: 완료
51 | rdb_x_issues:
52 | other: "%{count} 일감"
53 | one: "%{count} 일감"
54 | rdb_dialog_update_issue_title: 일감 갱신
55 | rdb_dialog_update_issue_status: 일감의 새 상태 선택
56 | rdb_other_issues: 다른 일감들 <span>(%{count})</span>
57 | rdb_unassigned: 지정되지 않음
58 | rdb_others: 기타
59 | rdb_all_issues: 모든 일감
60 | rdb_no_parent: 상위 일감 없음
61 | rdb_issue_menu_progress_title: 진척률 조정
62 | rdb_issue_menu_progress: "%{count}%"
63 | rdb_issue_menu_redmine_issue: 일감
64 | rdb_issue_menu_show: 보기
65 | rdb_issue_menu_edit: 편집
66 | rdb_issue_menu_assign_me: 내가 맡기
67 | rdb_issue_menu_unassign_me: 일감 놓기
68 | rdb_flash_illegal_workflow_action: "<p>이 업무흐름이 허용되지 않습니다:</p><p><b>%{source}</b>에서 <b>%{target}</b>로 <b>%{issue}</b> 이동</p>"
69 | rdb_flash_invalid_request: "<p>잘못된 요청.</p>"
70 |
--------------------------------------------------------------------------------
/config/locales/mn.yml:
--------------------------------------------------------------------------------
1 | ---
2 | mn:
3 | project_module_dashboard: Хянах самбар
4 | permission_view_dashboards: Хянах самбар харах
5 | permission_configure_dashboards: Хянах самбар тохируулах
6 | menu_label_dashboard: Хянах самбар
7 | rdb_taskboard: Ажлын самбар
8 | rdb_planningboard: Төлөвлөгөөний самбар
9 | rdb_filter_version_all: Бүх хувилбар
10 | rdb_filter_version_unassigned: Хуваарилагдаагүй
11 | rdb_filter_tracker_all: Бүх трэкер
12 | rdb_filter_tracker_multiple: Хэд хэдэн трэкер
13 | rdb_filter_category_all: Бүх ангилал
14 | rdb_filter_category_multiple: Хэд хэдэн ангилал
15 | rdb_filter_assignee_all: Бүх хариуцагч
16 | rdb_filter_assignee_me: Миний ажлууд
17 | rdb_filter_assignee_none: Хуваарилагдаагүй
18 | rdb_filter_assignee_others: Бусад
19 | rdb_options: Нэмэлт тохируулга
20 | rdb_options_change_assignee: Хариуцагчийг солих
21 | rdb_options_change_assignee_info: Асуудлыг зөөхөд хариуцагчийг одоогийн хэрэглэгчээр солих
22 | rdb_options_hide_done: Хаагдсан асуудлуудыг нуух
23 | rdb_options_hide_done_info: Хаагдсан асуудлуудыг нууж гүйцэтгэлийн баганыг нарийсгах
24 | rdb_options_reset: Шүүлтийг арилгах
25 | rdb_options_reset_info: Бүх шүүлтийг анхны төлөвт оруулах
26 | rdb_options_configure: Тохируулах
27 | rdb_options_include_subprojects: Бүх дэд төслийг оруулах
28 | rdb_options_view: Харах
29 | rdb_options_issue_view: Асуудлыг харах
30 | rdb_options_issue_view_card: Карт
31 | rdb_options_issue_view_compact: Цомхон
32 | rdb_options_group: Асуудлыг бүлэглэх
33 | rdb_group_none: Бүлэглэхгүй
34 | rdb_group_category: Ангилал
35 | rdb_group_assignee: Хариуцагч
36 | rdb_group_tracker: Трэкер
37 | rdb_group_priority: Зэрэглэл
38 | rdb_group_version: Хувилбар
39 | rdb_group_parent: Эх ажил
40 | rdb_group_project: Төсөл
41 | rdb_options_board_view: Самбар харах
42 | rdb_options_board_view_compact: Сонгодог
43 | rdb_options_board_view_outline: Тойм
44 | rdb_options_columns: Самбарын баганууд
45 | rdb_options_fullscreen: Бүтэн дэлгэцээр
46 | rdb_legend_priorities: Зэрэглэлүүд
47 | rdb_legend_warnings: Анхааруулга
48 | rdb_issue_overdue: Хугацаа хэтэрсэн
49 | rdb_property_time: <span title="Зарцуулсан хугацаа">%{actual}</span> / <span title="Төлөвлөсөн хугацаа">%{estimated}</span>
50 | rdb_not_available: Хамааралгүй
51 | rdb_column_done: Гүйцэтгэл
52 | rdb_x_issues:
53 | one: 1 асуудал
54 | other: "%{count} асуудал"
55 | rdb_dialog_update_issue_title: Асуудлыг шинэчлэх
56 | rdb_dialog_update_issue_status: Шинэ асуудлын төлөвийг сонго
57 | rdb_other_issues: Бусад асуудлууд <span>(%{count})</span>
58 | rdb_unassigned: Хуваарилагдаагүй
59 | rdb_others: Бусад
60 | rdb_all_issues: Бүх асуудлууд
61 | rdb_no_parent: Эх ажил алга
62 | rdb_issue_menu_progress_title: Явц шинэчлэх
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine Issue
65 | rdb_issue_menu_show: Харуулах
66 | rdb_issue_menu_edit: Засварлах
67 | rdb_issue_menu_assign_me: Надад хариуцуул
68 | rdb_issue_menu_unassign_me: Хариуцагчаас намайг хас
69 | rdb_flash_illegal_workflow_action: "<p>Танд энэ үйлдлийг хийх эрх алга:</p><p><b>%{issue}</b>-г <b>%{source}</b>-с <b>%{target}</b> уруу зөөх.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>Түгжих хувилбар алга</b>: Түгжих хувилбаргүйгээр асуудлыг шинэчилж чадахгүй. Дахин оролд."
71 | rdb_flash_stale_object: "<p>Хоосон асуудлыг шинэчлэх гэж оролдсон байна: <b>%{issue}</b>. Өөрчлөлтөө шалгаад дахин оролд."
72 | rdb_flash_invalid_request: "<p>Хүсэлт буруу.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/nl.yml:
--------------------------------------------------------------------------------
1 | ---
2 | nl:
3 | project_module_dashboard: Dashboard
4 | permission_view_dashboards: Dashboards bekijken
5 | permission_configure_dashboards: Dashboards instellen
6 | menu_label_dashboard: Dashboard
7 | rdb_taskboard: Takenlijst
8 | rdb_planningboard: Planningschema
9 | rdb_filter_version_all: Alle versies
10 | rdb_filter_version_unassigned: Niet toegekend
11 | rdb_filter_tracker_all: Alle trackers
12 | rdb_filter_tracker_multiple: Meerdere trackers
13 | rdb_filter_category_all: Alle Categorieën
14 | rdb_filter_category_multiple: Meerdere Categorieën
15 | rdb_filter_assignee_all: Alle toegewezen personen
16 | rdb_filter_assignee_me: Mijn Issues
17 | rdb_filter_assignee_none: Niet toegewezen
18 | rdb_filter_assignee_others: Andere
19 | rdb_options: Opties
20 | rdb_options_change_assignee: Verander de aangewezen persoon
21 | rdb_options_change_assignee_info: Verander de aangewezen persoon wanneer het issue verplaatst wordt.
22 | rdb_options_hide_done: Verberg afgesloten issues
23 | rdb_options_hide_done_info: Verberg afgesloten issues en maak de klaar kolom smaller
24 | rdb_options_reset: Herstel filter
25 | rdb_options_reset_info: Herstel alle filters naar hun standaardwaarde
26 | rdb_options_configure: Instellen
27 | rdb_options_include_subprojects: Neem ondergeschikte projecten mee
28 | rdb_options_view: Bekijk
29 | rdb_options_issue_view: Issue beeld
30 | rdb_options_issue_view_card: Kaart
31 | rdb_options_issue_view_compact: Bondig
32 | rdb_options_group: Issue groepering
33 | rdb_group_none: Gene groepering
34 | rdb_group_category: Categorie
35 | rdb_group_assignee: Toegewezen persoon
36 | rdb_group_tracker: Tracker
37 | rdb_group_priority: Prioriteit
38 | rdb_group_version: Versie
39 | rdb_group_parent: Bovenliggende taak
40 | rdb_group_project: Project
41 | rdb_options_board_view: Overzichtsbeeld
42 | rdb_options_board_view_compact: Klassiek
43 | rdb_options_board_view_outline: Overzicht
44 | rdb_options_columns: Kolommen in het overzicht
45 | rdb_options_fullscreen: Schermvullend
46 | rdb_legend_priorities: Prioriteiten
47 | rdb_legend_warnings: Waarschuwingen
48 | rdb_issue_overdue: Te laat
49 | rdb_property_time: <span title="Spend time">%{actual}</span> / <span title="Estimated time">%{estimated}</span>
50 | rdb_not_available: Niet toepasbaar
51 | rdb_column_done: Klaar
52 | rdb_x_issues:
53 | one: 1 Issue
54 | other: "%{count} Issues"
55 | rdb_dialog_update_issue_title: Issue bijwerken
56 | rdb_dialog_update_issue_status: Kies een nieuwe Issue status
57 | rdb_other_issues: Andere Issues <span>(%{count})</span>
58 | rdb_unassigned: Niet toegewezen
59 | rdb_others: Andere
60 | rdb_all_issues: Alle Issues
61 | rdb_no_parent: Geen bovenliggende taak
62 | rdb_issue_menu_progress_title: De voortgang bewerken
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine Issue
65 | rdb_issue_menu_show: Toon
66 | rdb_issue_menu_edit: Bewerken
67 | rdb_issue_menu_assign_me: Aan mezelf toewijzen
68 | rdb_issue_menu_unassign_me: Niet meer aan mezelf toewijzen
69 | rdb_flash_illegal_workflow_action: "<p>U hebt geen toegang om deze werkproces actie uit te voeren :</p><p>Verplaats <b>%{issue}</b> van <b>%{source}</b> naar <b>%{target}</b>.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>Ontbrekende lock versie</b>: Kan het issue niet bijwerken zonder lock versie. Probeer het opnieuw."
71 | rdb_flash_stale_object: "<p>Geprobeerd om een vastgelopen issue bij te werken : <b>%{issue}</b>. Controleer de wijzigingen en probeer opnieuw."
72 | rdb_flash_invalid_request: "<p>Ongeldige vraag.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/pl.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pl:
3 | project_module_dashboard: Tablica
4 | permission_view_dashboards: Przeglądanie tablic
5 | permission_configure_dashboards: Konfigurowanie tablic
6 | menu_label_dashboard: Tablica
7 | rdb_taskboard: Tablica zadań
8 | rdb_planningboard: Tablica planowania
9 | rdb_filter_version_all: Wszystkie wersje
10 | rdb_filter_version_unassigned: Nieprzypisane
11 | rdb_filter_tracker_all: Wszystkie typy zagadnień
12 | rdb_filter_tracker_multiple: Wiele typów zagadnień
13 | rdb_filter_category_all: Wszystkie kategorie
14 | rdb_filter_category_multiple: Wiele kategorii
15 | rdb_filter_assignee_all: Wszyscy użytkownicy przypisani do zagadnień
16 | rdb_filter_assignee_me: Moje zagadnienia
17 | rdb_filter_assignee_none: Nieprzypisane
18 | rdb_filter_assignee_others: Inni
19 | rdb_options: Opcje
20 | rdb_options_change_assignee: Zmień przypisaną osobę
21 | rdb_options_change_assignee_info: Zmienia przypisaną do zagadnienia osobę na aktualnego użytkownika gdy zagadnienie jest przesuwane
22 | rdb_options_hide_done: Ukryj zamknięte zagadnienia
23 | rdb_options_hide_done_info: Ukrywa zamknięte zagadnienia i zmniejsza kolumnę wykonane
24 | rdb_options_reset: Resetuj filtr
25 | rdb_options_reset_info: Resetuje wszystkie filtry do ustawień domyślnych
26 | rdb_options_configure: Konfiguruj
27 | rdb_options_include_subprojects: Uwzględnij podprojekty
28 | rdb_options_view: Widok
29 | rdb_options_issue_view: Widok zagadnień
30 | rdb_options_issue_view_card: Karty
31 | rdb_options_issue_view_compact: Kompaktowy
32 | rdb_options_group: Grupowanie zagadnień
33 | rdb_group_none: Bez grupowania
34 | rdb_group_category: Kategoria
35 | rdb_group_assignee: Osoba przypisana do zagadnienia
36 | rdb_group_tracker: Typ zagadnienia
37 | rdb_group_priority: Priorytet
38 | rdb_group_version: Wersja
39 | rdb_group_parent: Zagadnienie nadrzędne
40 | rdb_group_project: Projekt
41 | rdb_options_board_view: Widok tablicy
42 | rdb_options_board_view_compact: Klasyczny
43 | rdb_options_board_view_outline: Zarys
44 | rdb_options_columns: Kolumny tablicy
45 | rdb_options_fullscreen: Pełny ekran
46 | rdb_legend_priorities: Priorytety
47 | rdb_legend_warnings: Ostrzeżenia
48 | rdb_issue_overdue: Opóźnienie
49 | rdb_property_time: '''<span title="Spędzony czas">%{actual}</span> / <span title="Szacowany czas">%{estimated}</span>'''
50 | rdb_not_available: N/A
51 | rdb_column_done: Zrobione
52 | rdb_x_issues:
53 | one: 1 zagadnienie
54 | few: "%{count} zagadnień"
55 | many: "%{count} zagadnień"
56 | other: "%{count} zagadnień"
57 | rdb_dialog_update_issue_title: Aktualizacja zagadnienia
58 | rdb_dialog_update_issue_status: Wybierz nowy status zagadnienia
59 | rdb_other_issues: Inne zagadnienia <span>(%{count})</span>
60 | rdb_unassigned: Nieprzypisane
61 | rdb_others: Inni
62 | rdb_all_issues: Wszystkie zagadnienia
63 | rdb_no_parent: Brak zagadnień nadrzędnych
64 | rdb_issue_menu_progress_title: Postęp aktualizacji
65 | rdb_issue_menu_progress: "%{count}%"
66 | rdb_issue_menu_redmine_issue: Zagadnienie Redmine
67 | rdb_issue_menu_show: Pokaż
68 | rdb_issue_menu_edit: Edycja
69 | rdb_issue_menu_assign_me: Przypisz do mnie
70 | rdb_issue_menu_unassign_me: Cofnij przypisanie do mnie
71 | rdb_flash_illegal_workflow_action: "<p>Nie masz uprawnień do wykonania tej akcji przepływu pracy:</p><p>Przesuń <b>%{issue}</b> z <b>%{source}</b> do <b>%{target}</b>.</p>"
72 | rdb_flash_missing_lock_version: "<p><b>Brak wersji docelowej</b>: Nie można zaktualizować zagadnienia bez wersji docelowej. Spróbuj ponownie."
73 | rdb_flash_stale_object: "<p>Próba aktualizacji przestarzałego zagadnienia: <b>%{issue}</b>. Sprawdź zmiany i spróbuj ponownie."
74 | rdb_flash_invalid_request: "<p>Niepoprawne żądanie.</p>"
75 |
--------------------------------------------------------------------------------
/config/locales/pt-BR.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pt-BR:
3 | project_module_dashboard: Dashboard
4 | permission_view_dashboards: Ver Dashboard
5 | permission_configure_dashboards: Configurar Dashboards
6 | menu_label_dashboard: Dashboard
7 | rdb_taskboard: Tarefas
8 | rdb_planningboard: Planejamento
9 | rdb_filter_version_all: Todas Versões
10 | rdb_filter_version_unassigned: Não atribuído
11 | rdb_filter_tracker_all: Todos Observadores
12 | rdb_filter_tracker_multiple: Múltiplos Observadores
13 | rdb_filter_category_all: Todas Categorias
14 | rdb_filter_category_multiple: Múltiplas Categorias
15 | rdb_filter_assignee_all: Todas Atribuições
16 | rdb_filter_assignee_me: Minhas tarefas
17 | rdb_filter_assignee_none: Não atribuído
18 | rdb_filter_assignee_others: Outros
19 | rdb_options: Opções
20 | rdb_options_change_assignee: Mudar atribuição
21 | rdb_options_change_assignee_info: Mudar atribuição para o usuário atual quando a tarefa for alterada
22 | rdb_options_hide_done: Esconder tarefas fechadas
23 | rdb_options_hide_done_info: Esconder tarefas fechadas e diminuir a coluna "feito"
24 | rdb_options_reset: Resetar filtro
25 | rdb_options_reset_info: Resetar todos filtros para o padrão
26 | rdb_options_configure: Configurar
27 | rdb_options_include_subprojects: Incluir sub-projetos
28 | rdb_options_view: Visualização
29 | rdb_options_issue_view: Visualização de tarefa
30 | rdb_options_issue_view_card: Cartão
31 | rdb_options_issue_view_compact: Compactar
32 | rdb_options_group: Agrupamento de tarefas
33 | rdb_group_none: Sem agrupamento
34 | rdb_group_category: Categoria
35 | rdb_group_assignee: Atribuído para
36 | rdb_group_tracker: Observador
37 | rdb_group_priority: Prioridade
38 | rdb_group_version: Versão
39 | rdb_group_parent: Tarefa pai
40 | rdb_group_project: Projeto
41 | rdb_options_board_view: Modo de visualização
42 | rdb_options_board_view_compact: Classico
43 | rdb_options_board_view_outline: Contorno
44 | rdb_options_columns: Colunas
45 | rdb_options_fullscreen: Tela cheia
46 | rdb_legend_priorities: Prioridades
47 | rdb_legend_warnings: Alertas
48 | rdb_issue_overdue: Atrasado
49 | rdb_property_time: <span title="Spend time">%{actual}</span> / <span title="Estimated time">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Feito
52 | rdb_x_issues:
53 | one: 1 Tarefa
54 | many: "%{count} Tarefas"
55 | other: "%{count} Tarefas"
56 | rdb_dialog_update_issue_title: Atualizar Tarefa
57 | rdb_dialog_update_issue_status: Escolher status da tarefa
58 | rdb_other_issues: Outras tarefas <span>(%{count})</span>
59 | rdb_unassigned: Não atribuidas
60 | rdb_others: Outras
61 | rdb_all_issues: Todas Tarefas
62 | rdb_no_parent: Sem tarefas pai
63 | rdb_issue_menu_progress_title: Progresso de atualização
64 | rdb_issue_menu_progress: "%{count}%"
65 | rdb_issue_menu_redmine_issue: Tarefa Redmine
66 | rdb_issue_menu_show: Mostrar
67 | rdb_issue_menu_edit: Editar
68 | rdb_issue_menu_assign_me: Atribuir para mim
69 | rdb_issue_menu_unassign_me: Retirar atribuição
70 | rdb_flash_illegal_workflow_action: "<p>Você não tem autorização para altera o fluxo:</p><p>Mover <b>%{issue}</b> de <b>%{source}</b> para <b>%{target}</b>.</p>"
71 | rdb_flash_missing_lock_version: "<p><b>Falta versão bloqueada</b>: Não é possível atualizar uma tarefa sem uma versão bloqueada. Tente novamente."
72 | rdb_flash_stale_object: "<p>Tentando atualizar uma tarefa antiga: <b>%{issue}</b>. Verifique as mudanças e tente novamente."
73 | rdb_flash_invalid_request: "<p>Requisição inválida.</p>"
74 |
--------------------------------------------------------------------------------
/config/locales/ru.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ru:
3 | project_module_dashboard: Панель задач
4 | permission_view_dashboards: Просмотр Панели
5 | permission_configure_dashboards: Управление панелями
6 | menu_label_dashboard: Панель задач
7 | rdb_taskboard: Панель задач
8 | rdb_planningboard: Доска планирования
9 | rdb_filter_version_all: Все версии
10 | rdb_filter_version_unassigned: Неназначенные
11 | rdb_filter_tracker_all: Все трекеры
12 | rdb_filter_tracker_multiple: Множественные трекеры
13 | rdb_filter_category_all: Все категории
14 | rdb_filter_category_multiple: Множественные категории
15 | rdb_filter_assignee_all: Все ответственные
16 | rdb_filter_assignee_me: Мои задачи
17 | rdb_filter_assignee_none: Не назначены
18 | rdb_filter_assignee_others: Другие
19 | rdb_options: Настройки
20 | rdb_options_change_assignee: Изменить назначение
21 | rdb_options_change_assignee_info: Назначать задачу текущему пользователю при перемещении
22 | rdb_options_hide_done: Скрыть закрытые задачи
23 | rdb_options_hide_done_info: Скрыть закрытые задачи и сделать колонку 'Выполнено' меньше
24 | rdb_options_reset: Очистить фильтр
25 | rdb_options_reset_info: Сбросить все фильтры по-умолчанию
26 | rdb_options_configure: Настроить
27 | rdb_options_include_subprojects: Включить под-проекты
28 | rdb_options_view: Вид
29 | rdb_options_issue_view: Вид задач
30 | rdb_options_issue_view_card: Карточки
31 | rdb_options_issue_view_compact: Компактный
32 | rdb_options_group: Группировка задач
33 | rdb_group_none: Без группировки
34 | rdb_group_category: Категория
35 | rdb_group_assignee: Назначена
36 | rdb_group_tracker: Трекер
37 | rdb_group_priority: Приоритет
38 | rdb_group_version: Версия
39 | rdb_group_parent: Основная задача
40 | rdb_group_project: Проект
41 | rdb_options_board_view: Вид панели
42 | rdb_options_board_view_compact: Классический
43 | rdb_options_board_view_outline: Контур
44 | rdb_options_columns: Колонки панели
45 | rdb_options_fullscreen: Полный экран
46 | rdb_legend_priorities: Приоритеты
47 | rdb_legend_warnings: Предупреждения
48 | rdb_issue_overdue: Изменить дату
49 | rdb_property_time: <span title="Затраченное время">%{actual}</span> / <span title="Оценка времени">%{estimated}</span>
50 | rdb_not_available: Нет
51 | rdb_column_done: Выполнено
52 | rdb_x_issues:
53 | one: 1 задача
54 | few: "%{count} задачи"
55 | many: "%{count} задач"
56 | other: "%{count} задач"
57 | rdb_dialog_update_issue_title: Обновить задачу
58 | rdb_dialog_update_issue_status: Выбрать новый статус задачи
59 | rdb_other_issues: Другие задачи <span>(%{count})</span>
60 | rdb_unassigned: Неназначенные
61 | rdb_others: Другие
62 | rdb_all_issues: Все задачи
63 | rdb_no_parent: Нет основной задачи
64 | rdb_issue_menu_progress_title: Обновить готовность
65 | rdb_issue_menu_progress: "%{count}%"
66 | rdb_issue_menu_redmine_issue: Задача
67 | rdb_issue_menu_show: Просмотр
68 | rdb_issue_menu_edit: Изменить
69 | rdb_issue_menu_assign_me: Назначить мне
70 | rdb_issue_menu_unassign_me: Снять назначение
71 | rdb_flash_illegal_workflow_action: "<p>У вас не достаточно прав для выполенния действия:</p><p>Переместить <b>%{issue}</b> из <b>%{source}</b> в <b>%{target}</b>.</p>"
72 | rdb_flash_missing_lock_version: "<p><b>Не задана версия</b>: Невозможно обновить задачу без определенной версии. Попробуйте позже."
73 | rdb_flash_stale_object: "<p>Задача устарела: <b>%{issue}</b>. Проверьте изменения и попробуйте снова."
74 | rdb_flash_invalid_request: "<p>Неверный запрос.</p>"
75 |
--------------------------------------------------------------------------------
/config/locales/tr.yml:
--------------------------------------------------------------------------------
1 | ---
2 | tr:
3 | project_module_dashboard: Gösterge Paneli
4 | permission_view_dashboards: Gösterge Panelini Görüntüle
5 | permission_configure_dashboards: Gösterge Panelini Yapılandır
6 | menu_label_dashboard: Gösterge Paneli
7 | rdb_taskboard: Görev Panosu
8 | rdb_planningboard: Planlama Panosu
9 | rdb_filter_version_all: Tüm Uyarlamalar
10 | rdb_filter_version_unassigned: Atanmamış
11 | rdb_filter_tracker_all: Tüm Takipçiler
12 | rdb_filter_tracker_multiple: Çoklu Takipçiler
13 | rdb_filter_category_all: Tüm Kategoriler
14 | rdb_filter_category_multiple: Çoklu Kategoriler
15 | rdb_filter_assignee_all: Tüm Atananlar
16 | rdb_filter_assignee_me: Taleplerim
17 | rdb_filter_assignee_none: Atanmamışlar
18 | rdb_filter_assignee_others: Diğerleri
19 | rdb_options: Seçenekler
20 | rdb_options_change_assignee: Atananı Değiştir
21 | rdb_options_change_assignee_info: Talep taşındığında atananı mevcut kullanıcıyla değiştirin.
22 | rdb_options_hide_done: Kapalı talepleri gizle
23 | rdb_options_hide_done_info: Kapalı talepleri gizler ve yapılan sütununu ufaltır.
24 | rdb_options_reset: Süzgeçi sıfırla
25 | rdb_options_reset_info: Tüm süzgeçleri geri varsayılana sıfırla
26 | rdb_options_configure: Yapılandır
27 | rdb_options_include_subprojects: Alt projeleri dahil et
28 | rdb_options_view: Göster
29 | rdb_options_issue_view: Talep Görünümü
30 | rdb_options_issue_view_card: Kart
31 | rdb_options_issue_view_compact: Derli toplu
32 | rdb_options_group: Talep Gruplama
33 | rdb_group_none: Gruplama Yok
34 | rdb_group_category: Kategori
35 | rdb_group_assignee: Atanan
36 | rdb_group_tracker: Takipçi
37 | rdb_group_priority: Öncelik
38 | rdb_group_version: Uyarlama
39 | rdb_group_parent: Üst Görev
40 | rdb_group_project: Proje
41 | rdb_options_board_view: Pano Görünüü
42 | rdb_options_board_view_compact: Klasik
43 | rdb_options_board_view_outline: Anahat
44 | rdb_options_columns: Pano Sütunları
45 | rdb_options_fullscreen: Tam Ekran
46 | rdb_legend_priorities: Öncelikler
47 | rdb_legend_warnings: Uyarılar
48 | rdb_issue_overdue: Vadesi geçmiş
49 | rdb_property_time: <span title="Gerçekleşen Zaman">%{actual}</span> / <span title="Tahmini zaman">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: Tamam
52 | rdb_x_issues:
53 | one: 1 İş
54 | other: "%{count}Talepler"
55 | rdb_dialog_update_issue_title: Talebi Güncelle
56 | rdb_dialog_update_issue_status: Yeni talep durumunu seçin
57 | rdb_other_issues: Diğer Talepler <span>(%{count})</span>
58 | rdb_unassigned: Atanmamış
59 | rdb_others: Diğerleri
60 | rdb_all_issues: Tüm Talepler
61 | rdb_no_parent: Üst görev yok
62 | rdb_issue_menu_progress_title: İlerlemeleri güncelle
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine Talep
65 | rdb_issue_menu_show: Göster
66 | rdb_issue_menu_edit: Düzenle
67 | rdb_issue_menu_assign_me: Bana Ata
68 | rdb_issue_menu_unassign_me: Atamamı kaldır
69 | rdb_flash_illegal_workflow_action: "<p>Bu İş akışı eylemini gerçekleştirme izniniz yok:</p><p>Taşı <b>%{issue}</b> durumundan <b>%{source}</b> <b>%{target}</b> durumuna.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>Kilit uyarlaması eksik</b>: Kilit uyarlaması olmadan talep güncellenemez. Tekrar deneyin."
71 | rdb_flash_stale_object: "<p>Bayat bir talebi güncelleme girişimi: <b>%{issue}</b>. Değişiklikleri denetleyin ve tekrar deneyin."
72 | rdb_flash_invalid_request: "<p>Geçersiz istem.</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/uk.yml:
--------------------------------------------------------------------------------
1 | ---
2 | uk:
3 | project_module_dashboard: Дошка завдань
4 | permission_view_dashboards: Перегляд
5 | permission_configure_dashboards: Керування
6 | menu_label_dashboard: Дошка завдань
7 | rdb_taskboard: Дошка завдань
8 | rdb_planningboard: Дошка планування
9 | rdb_filter_version_all: Всі версії
10 | rdb_filter_version_unassigned: Без версії
11 | rdb_filter_tracker_all: Всі координатори
12 | rdb_filter_tracker_multiple: Декілька координаторів
13 | rdb_filter_category_all: Всі категорії
14 | rdb_filter_category_multiple: Декілька категорій
15 | rdb_filter_assignee_all: Всі відповідальні
16 | rdb_filter_assignee_me: Мої завдання
17 | rdb_filter_assignee_none: Без відповідального
18 | rdb_filter_assignee_others: Інші
19 | rdb_options: Налаштування
20 | rdb_options_change_assignee: Змінити відповідального
21 | rdb_options_change_assignee_info: При перетягуванні завдання доручити його мені
22 | rdb_options_hide_done: Приховати виконанні завдання
23 | rdb_options_hide_done_info: Приховати виконанні завдання і зменшити колонку "Done"
24 | rdb_options_reset: Скинути фільтр
25 | rdb_options_reset_info: Скинути фільтр за замовчуванням
26 | rdb_options_configure: Налаштування
27 | rdb_options_include_subprojects: Додати підпроекти
28 | rdb_options_view: Вигляд
29 | rdb_options_issue_view: Вигляд
30 | rdb_options_issue_view_card: У формі картки
31 | rdb_options_issue_view_compact: Компактний
32 | rdb_options_group: Групувати за
33 | rdb_group_none: Без групування
34 | rdb_group_category: Категорією
35 | rdb_group_assignee: Відповідальним
36 | rdb_group_tracker: Координатором
37 | rdb_group_priority: Пріоритетом
38 | rdb_group_version: Версією
39 | rdb_group_parent: Батьківським завданням
40 | rdb_group_project: Проєктом
41 | rdb_options_board_view: Вигляд дошки
42 | rdb_options_board_view_compact: Класичний
43 | rdb_options_board_view_outline: Контур
44 | rdb_options_columns: Колонки дошки
45 | rdb_options_fullscreen: На повний екран
46 | rdb_legend_priorities: Пріоритети
47 | rdb_legend_warnings: Попередження
48 | rdb_issue_overdue: Протермінований
49 | rdb_property_time: <span title="Затрачено часу">%{actual}</span> / <span title="Орієнтований час">%{estimated}</span>
50 | rdb_not_available: Недоступно
51 | rdb_column_done: Done
52 | rdb_x_issues:
53 | one: 1 Завдання
54 | few: 1%{count} Завдання
55 | many: 1%{count} Завдання
56 | other: 1%{count} Завдання
57 | rdb_dialog_update_issue_title: Оновити завдання
58 | rdb_dialog_update_issue_status: Виберіть новий статус для завдання
59 | rdb_other_issues: Інші завдання <span>(%{count})</span>
60 | rdb_unassigned: Не призначений
61 | rdb_others: Інші
62 | rdb_all_issues: Всі завдання
63 | rdb_no_parent: Без батьківського завдання
64 | rdb_issue_menu_progress_title: Оновити прогрес
65 | rdb_issue_menu_progress: "%{count}%"
66 | rdb_issue_menu_redmine_issue: Завдання
67 | rdb_issue_menu_show: Перейти
68 | rdb_issue_menu_edit: Редагувати
69 | rdb_issue_menu_assign_me: Доручити мені
70 | rdb_issue_menu_unassign_me: Зняти з мене
71 | rdb_flash_illegal_workflow_action: "<p>Ви не маєте права виконувати цю дію в робочому процесі:</p><p>Перетягнути <b>%{issue}</b> з <b>%{source}</b> у <b>%{target}</b>.</p>"
72 | rdb_flash_missing_lock_version: "<p><b>Не вказана версія</b>: Не можливо оновити завдання не вказавши версію. Спробуйте ще раз."
73 | rdb_flash_stale_object: "<p>Завдання застарілe: <b>%{issue}</b>. Перевірте зміни і спробуйте ще раз."
74 | rdb_flash_invalid_request: "<p>Невірний запит.</p>"
75 |
--------------------------------------------------------------------------------
/config/locales/zh-TW.yml:
--------------------------------------------------------------------------------
1 | ---
2 | zh-TW:
3 | project_module_dashboard: 儀表板
4 | permission_view_dashboards: 檢視儀表板
5 | permission_configure_dashboards: 設定儀表板
6 | menu_label_dashboard: 儀表板
7 | rdb_taskboard: 任務板
8 | rdb_planningboard: 計畫板
9 | rdb_filter_version_all: 所有版本
10 | rdb_filter_version_unassigned: 未指派
11 | rdb_filter_tracker_all: 所有標籤
12 | rdb_filter_tracker_multiple: 多個標籤
13 | rdb_filter_category_all: 所有分類
14 | rdb_filter_category_multiple: 多個分類
15 | rdb_filter_assignee_all: 所有指派
16 | rdb_filter_assignee_me: 我的指派
17 | rdb_filter_assignee_none: 未指派
18 | rdb_filter_assignee_others: 其他指派
19 | rdb_options: 選項
20 | rdb_options_change_assignee: 更換指派
21 | rdb_options_change_assignee_info: 更換指派給移動這個問題的人
22 | rdb_options_hide_done: 隱藏已關閉的問題
23 | rdb_options_hide_done_info: 隱藏已關閉的問題且縮小完成欄
24 | rdb_options_reset: 重置篩選條件
25 | rdb_options_reset_info: 重置篩選條件成預設
26 | rdb_options_configure: 設定
27 | rdb_options_include_subprojects: 包含子項目
28 | rdb_options_view: 視圖
29 | rdb_options_issue_view: 問題視圖
30 | rdb_options_issue_view_card: 卡片
31 | rdb_options_issue_view_compact: 簡潔
32 | rdb_options_group: 問題分組
33 | rdb_group_none: 沒有分組
34 | rdb_group_category: 分類
35 | rdb_group_assignee: 指派人
36 | rdb_group_tracker: 標籤
37 | rdb_group_priority: 優先
38 | rdb_group_version: 版本
39 | rdb_group_parent: 父項目
40 | rdb_group_project: 項目
41 | rdb_options_board_view: 黑板視圖
42 | rdb_options_board_view_compact: 經典
43 | rdb_options_board_view_outline: 大綱
44 | rdb_options_columns: 黑板欄位
45 | rdb_options_fullscreen: 全螢幕
46 | rdb_legend_priorities: 優先權
47 | rdb_legend_warnings: 警告
48 | rdb_issue_overdue: 過期
49 | rdb_property_time: <span title="花費時間">%{actual}</span> / <span title="預計時間">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: 完成
52 | rdb_x_issues:
53 | other: "%{count} 問題"
54 | one: "%{count} 問題"
55 | rdb_dialog_update_issue_title: 更新問題
56 | rdb_dialog_update_issue_status: 選擇新問題的狀態
57 | rdb_other_issues: 其他問題 <span>(%{count})</span>
58 | rdb_unassigned: 未指派
59 | rdb_others: 其他
60 | rdb_all_issues: 所有問題
61 | rdb_no_parent: 沒有父項目
62 | rdb_issue_menu_progress_title: 更新進度
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine 問題
65 | rdb_issue_menu_show: 顯示
66 | rdb_issue_menu_edit: 編輯
67 | rdb_issue_menu_assign_me: 指派給我
68 | rdb_issue_menu_unassign_me: 解除我的指派
69 | rdb_flash_illegal_workflow_action: "<p>你不被允許進行這樣的工作流程:</p><p>將 <b>%{issue}</b> 從 <b>%{source}</b> 移動到 <b>%{target}</b>.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>遺失的鎖定版本</b>: 無法更新問題,請重試。"
71 | rdb_flash_stale_object: "<p>更改已穩定的問題: <b>%{issue}</b>請重試。"
72 | rdb_flash_invalid_request: "<p>無效的請求</p>"
73 |
--------------------------------------------------------------------------------
/config/locales/zh.yml:
--------------------------------------------------------------------------------
1 | ---
2 | zh:
3 | project_module_dashboard: 看板
4 | permission_view_dashboards: 浏览看板
5 | permission_configure_dashboards: 配置看板
6 | menu_label_dashboard: 看板
7 | rdb_taskboard: 任务看板
8 | rdb_planningboard: 计划看板
9 | rdb_filter_version_all: 所有版本
10 | rdb_filter_version_unassigned: 未指派
11 | rdb_filter_tracker_all: 所有跟踪
12 | rdb_filter_tracker_multiple: 多个跟踪
13 | rdb_filter_category_all: 所有类别
14 | rdb_filter_category_multiple: 多个类别
15 | rdb_filter_assignee_all: 所有指派
16 | rdb_filter_assignee_me: 我的指派
17 | rdb_filter_assignee_none: 未指派
18 | rdb_filter_assignee_others: 其他指派
19 | rdb_options: 选项
20 | rdb_options_change_assignee: 更换指派
21 | rdb_options_change_assignee_info: 更换指派给移动本问题的人
22 | rdb_options_hide_done: 隐藏已关闭的问题
23 | rdb_options_hide_done_info: 隐藏已关闭的问题并缩小栏位
24 | rdb_options_reset: 重置过滤条件
25 | rdb_options_reset_info: 重置过滤条件为预设
26 | rdb_options_configure: 配置
27 | rdb_options_include_subprojects: 包含子项目
28 | rdb_options_view: 视图
29 | rdb_options_issue_view: 问题视图
30 | rdb_options_issue_view_card: 卡片
31 | rdb_options_issue_view_compact: 精简
32 | rdb_options_group: 问题分组
33 | rdb_group_none: 沒有分组
34 | rdb_group_category: 类别
35 | rdb_group_assignee: 指派人
36 | rdb_group_tracker: 跟踪
37 | rdb_group_priority: 优先级
38 | rdb_group_version: 版本
39 | rdb_group_parent: 父项
40 | rdb_group_project: 项目
41 | rdb_options_board_view: 看板视图
42 | rdb_options_board_view_compact: 精简
43 | rdb_options_board_view_outline: 大纲
44 | rdb_options_columns: 看板栏位
45 | rdb_options_fullscreen: 全屏幕
46 | rdb_legend_priorities: 优先级
47 | rdb_legend_warnings: 警告
48 | rdb_issue_overdue: 过期
49 | rdb_property_time: <span title="花费时间">%{actual}</span> / <span title="预计时间">%{estimated}</span>
50 | rdb_not_available: N/A
51 | rdb_column_done: 完成
52 | rdb_x_issues:
53 | other: "%{count} 问题"
54 | one: "%{count} 问题"
55 | rdb_dialog_update_issue_title: 更新问题
56 | rdb_dialog_update_issue_status: 选择新问题的状态
57 | rdb_other_issues: 其他问题 <span>(%{count})</span>
58 | rdb_unassigned: 未指派
59 | rdb_others: 其他
60 | rdb_all_issues: 所有问题
61 | rdb_no_parent: 没有父项目
62 | rdb_issue_menu_progress_title: 更新进度
63 | rdb_issue_menu_progress: "%{count}%"
64 | rdb_issue_menu_redmine_issue: Redmine 问题
65 | rdb_issue_menu_show: 显示
66 | rdb_issue_menu_edit: 编辑
67 | rdb_issue_menu_assign_me: 指派给我
68 | rdb_issue_menu_unassign_me: 未指派给我
69 | rdb_flash_illegal_workflow_action: "<p>你未经许可进行以下操作步骤:</p><p>将 <b>%{issue}</b> 从 <b>%{source}</b> 移动到 <b>%{target}</b>.</p>"
70 | rdb_flash_missing_lock_version: "<p><b>丢失的冻结版本</b>: 无法更新问题,请重试。"
71 | rdb_flash_stale_object: "<p>试图更改已冻结的问题: <b>%{issue}</b>请重试。"
72 | rdb_flash_invalid_request: "<p>无效的请求</p>"
73 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Plugin's routes
4 | # See: http://guides.rubyonrails.org/routing.html
5 |
6 | match 'projects/:id/rdb/taskboard' => 'rdb_taskboard#index', :as => :rdb_taskboard, via: %i[get post]
7 | match 'projects/:id/rdb/taskboard/move' => 'rdb_taskboard#move', :as => :rdb_taskboard_move, via: %i[get post]
8 | match 'projects/:id/rdb/taskboard/update' => 'rdb_taskboard#update', :as => :rdb_taskboard_update, via: %i[get post]
9 | match 'projects/:id/rdb/taskboard/filter' => 'rdb_taskboard#filter', :as => :rdb_taskboard_filter, via: %i[get post]
10 |
11 | match 'projects/:id/rdb(/:board)' => 'rdb_dashboard#index', :as => :rdb, via: %i[get post]
12 | match 'projects/:id/dashboard' => 'rdb_dashboard#index', via: %i[get post]
13 |
--------------------------------------------------------------------------------
/init.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'redmine'
4 |
5 | Redmine::Plugin.register :redmine_dashboard do
6 | name 'Redmine Dashboard plugin'
7 | author 'Jan Graichen'
8 | description 'Add a task board and a planning board to Redmine'
9 | version '2.16.0'
10 | url 'https://github.com/jgraichen/redmine_dashboard'
11 | author_url 'mailto:jgraichen@altimos.de'
12 |
13 | requires_redmine '5.0'
14 |
15 | project_module :dashboard do
16 | permission :view_dashboards, {
17 | rdb_dashboard: [:index],
18 | rdb_taskboard: %i[index filter move update]
19 | }
20 | permission :configure_dashboards, {rdb_dashboard: [:configure]}
21 | end
22 | menu :project_menu, :dashboard, {controller: 'rdb_dashboard', action: 'index'},
23 | caption: :menu_label_dashboard, after: :new_issue
24 | end
25 |
--------------------------------------------------------------------------------