redmine_extensions 0.6.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -32
- data/Rakefile +0 -8
- data/app/assets/javascripts/redmine_extensions/blocking.js +0 -3
- data/app/assets/javascripts/redmine_extensions/blocking_namespace.js +1 -2
- data/app/assets/javascripts/redmine_extensions/redmine_extensions.js +1 -33
- data/config/routes.rb +0 -7
- data/lib/redmine_extensions/easy_settings.rb +5 -5
- data/lib/redmine_extensions/engine.rb +12 -32
- data/lib/redmine_extensions/hooks.rb +6 -35
- data/lib/redmine_extensions/version.rb +1 -1
- data/lib/redmine_extensions.rb +5 -2
- data/spec/spec_helper.rb +10 -2
- metadata +3 -76
- data/app/assets/javascripts/redmine_extensions/blocking_render.js +0 -137
- data/app/assets/javascripts/redmine_extensions/blocking_schedule.js +0 -280
- data/app/assets/javascripts/redmine_extensions/event_bus.js +0 -61
- data/app/assets/stylesheets/redmine_extensions/application.css +0 -15
- data/lib/generators/redmine_extensions/entity/USAGE +0 -11
- data/lib/generators/redmine_extensions/entity/entity_generator.rb +0 -418
- data/lib/generators/redmine_extensions/entity/templates/_form.html.erb.erb +0 -51
- data/lib/generators/redmine_extensions/entity/templates/_list.html.erb.erb +0 -70
- data/lib/generators/redmine_extensions/entity/templates/_sidebar.html.erb.erb +0 -9
- data/lib/generators/redmine_extensions/entity/templates/app/models/%model_name_underscored%_query.rb.tt +0 -42
- data/lib/generators/redmine_extensions/entity/templates/app/views/%model_name_pluralize_underscored%/_%model_name_underscored%.api.rsb.tt +0 -18
- data/lib/generators/redmine_extensions/entity/templates/app/views/%model_name_pluralize_underscored%/_form.html.erb.tt +0 -62
- data/lib/generators/redmine_extensions/entity/templates/app/views/%model_name_pluralize_underscored%/edit.html.erb.tt +0 -8
- data/lib/generators/redmine_extensions/entity/templates/app/views/%model_name_pluralize_underscored%/new.html.erb.tt +0 -8
- data/lib/generators/redmine_extensions/entity/templates/context_menu.html.erb.erb +0 -20
- data/lib/generators/redmine_extensions/entity/templates/controller.rb.erb +0 -208
- data/lib/generators/redmine_extensions/entity/templates/custom_field.rb.erb +0 -11
- data/lib/generators/redmine_extensions/entity/templates/easy_query.rb.erb +0 -37
- data/lib/generators/redmine_extensions/entity/templates/edit.html.erb.erb +0 -19
- data/lib/generators/redmine_extensions/entity/templates/edit.js.erb.erb +0 -18
- data/lib/generators/redmine_extensions/entity/templates/en.yml.erb +0 -16
- data/lib/generators/redmine_extensions/entity/templates/entity_attribute_helper_patch.rb.erb +0 -10
- data/lib/generators/redmine_extensions/entity/templates/helper.rb.erb +0 -3
- data/lib/generators/redmine_extensions/entity/templates/hooks.rb.erb +0 -5
- data/lib/generators/redmine_extensions/entity/templates/index.api.rsb.erb +0 -3
- data/lib/generators/redmine_extensions/entity/templates/index.html.erb.erb +0 -30
- data/lib/generators/redmine_extensions/entity/templates/index.js.erb.erb +0 -12
- data/lib/generators/redmine_extensions/entity/templates/mail_added.html.erb.erb +0 -1
- data/lib/generators/redmine_extensions/entity/templates/mail_added.text.erb.erb +0 -2
- data/lib/generators/redmine_extensions/entity/templates/mail_updated.html.erb.erb +0 -1
- data/lib/generators/redmine_extensions/entity/templates/mail_updated.text.erb.erb +0 -2
- data/lib/generators/redmine_extensions/entity/templates/mailer.rb.erb +0 -41
- data/lib/generators/redmine_extensions/entity/templates/migration.rb.erb +0 -16
- data/lib/generators/redmine_extensions/entity/templates/model.rb.erb +0 -143
- data/lib/generators/redmine_extensions/entity/templates/new.html.erb.erb +0 -19
- data/lib/generators/redmine_extensions/entity/templates/new.js.erb.erb +0 -18
- data/lib/generators/redmine_extensions/entity/templates/query.rb.erb +0 -67
- data/lib/generators/redmine_extensions/entity/templates/routes.rb.erb +0 -15
- data/lib/generators/redmine_extensions/entity/templates/show.api.rsb.erb +0 -1
- data/lib/generators/redmine_extensions/entity/templates/show.html.erb.erb +0 -50
- data/lib/generators/redmine_extensions/entity/templates/show.js.erb.erb +0 -12
- data/lib/generators/redmine_extensions/entity/templates/spec/controllers/%model_name_underscored%_controller_spec.rb.tt +0 -94
- data/lib/generators/redmine_extensions/entity/templates/spec/factories.rb.erb +0 -11
- data/lib/generators/redmine_extensions/entity/templates/spec/requests/%model_name_underscored%_spec.rb.tt +0 -19
- data/lib/generators/redmine_extensions/plugin/USAGE +0 -13
- data/lib/generators/redmine_extensions/plugin/plugin_generator.rb +0 -74
- data/lib/generators/redmine_extensions/plugin/templates/Gemfile.erb +0 -1
- data/lib/generators/redmine_extensions/plugin/templates/after_init.rb.erb +0 -23
- data/lib/generators/redmine_extensions/plugin/templates/en.yml.erb +0 -3
- data/lib/generators/redmine_extensions/plugin/templates/gitkeep.erb +0 -0
- data/lib/generators/redmine_extensions/plugin/templates/hooks.rb.erb +0 -5
- data/lib/generators/redmine_extensions/plugin/templates/init.rb.erb +0 -20
- data/lib/generators/redmine_extensions/plugin/templates/internals.rb.erb +0 -4
- data/lib/generators/redmine_extensions/plugin/templates/issue_patch.example.erb +0 -30
- data/lib/generators/redmine_extensions/plugin/templates/issues_controller_patch.example.erb +0 -30
- data/lib/generators/redmine_extensions/plugin/templates/issues_helper_patch.example.erb +0 -30
- data/lib/generators/redmine_extensions/plugin/templates/javascript.js +0 -1
- data/lib/generators/redmine_extensions/plugin/templates/routes.rb.erb +0 -0
- data/lib/generators/redmine_extensions/plugin/templates/stylesheet.css +0 -3
- data/lib/redmine_extensions/core_ext/date_range.rb +0 -152
- data/lib/redmine_extensions/core_ext/object.rb +0 -19
- data/lib/redmine_extensions/core_ext/string.rb +0 -5
- data/lib/redmine_extensions/core_ext.rb +0 -3
- data/lib/redmine_extensions/deprecator.rb +0 -11
- data/lib/redmine_extensions/migration.rb +0 -9
- data/lib/redmine_extensions/patch_manager.rb +0 -440
- data/lib/redmine_extensions/rails_patches/route_set_generator_patch.rb +0 -10
- data/lib/redmine_extensions/redmine_patches/controllers/application_controller_patch.rb +0 -65
- data/lib/redmine_extensions/redmine_patches/models/custom_field_patch.rb +0 -11
- data/lib/redmine_extensions/redmine_patches/models/project_patch.rb +0 -10
- data/lib/tasks/redmine_extensions_tasks.rake +0 -4
- data/spec/init_rails.rb +0 -13
@@ -1,280 +0,0 @@
|
|
1
|
-
(function () {
|
2
|
-
"use strict";
|
3
|
-
/**
|
4
|
-
* @callback SchedulePrerequisite
|
5
|
-
* @return {boolean}
|
6
|
-
*/
|
7
|
-
/**
|
8
|
-
* @typedef {{func:Function,[priority]:number,[pre]:SchedulePrerequisite,[pres]:Array.<SchedulePrerequisite>}} ScheduleTask
|
9
|
-
*/
|
10
|
-
// noinspection JSMismatchedCollectionQueryUpdate
|
11
|
-
/** @type {Array.<ScheduleTask>} */
|
12
|
-
var mainArray = [];
|
13
|
-
/** @type {Array.<ScheduleTask>} */
|
14
|
-
var lateArray = [];
|
15
|
-
/** @type {Array.<?ScheduleTask>} */
|
16
|
-
var prerequisiteArray = [];
|
17
|
-
/**
|
18
|
-
* Predefined getters for [require] function. Just specify the name of the module
|
19
|
-
* @type {{jquery: jquery, jqueryui: jqueryui, c3: c3, ckeditor: ckeditor}}
|
20
|
-
*/
|
21
|
-
var moduleGetters = {
|
22
|
-
jquery: function () {
|
23
|
-
return window.jQuery;
|
24
|
-
},
|
25
|
-
jqueryui: function () {
|
26
|
-
return window.jQuery && jQuery.Widget;
|
27
|
-
},
|
28
|
-
c3: function () {
|
29
|
-
return window.c3;
|
30
|
-
},
|
31
|
-
ckeditor: function () {
|
32
|
-
return window.CKEDITOR;
|
33
|
-
}
|
34
|
-
};
|
35
|
-
var moduleInstances = {};
|
36
|
-
var writeOut = false;
|
37
|
-
|
38
|
-
/** @param {ScheduleTask} a
|
39
|
-
* @param {ScheduleTask} b
|
40
|
-
*/
|
41
|
-
var sortFunction = function (a, b) {
|
42
|
-
return b.priority - a.priority;
|
43
|
-
};
|
44
|
-
|
45
|
-
var tick = function () {
|
46
|
-
var count1 = 0;
|
47
|
-
if (mainArray.length > 0) {
|
48
|
-
count1 = mainArray.length;
|
49
|
-
mainArray.sort(sortFunction);
|
50
|
-
var queue = mainArray;
|
51
|
-
mainArray = [];
|
52
|
-
for (var i = 0; i < queue.length; i++) {
|
53
|
-
queue[i].func();
|
54
|
-
}
|
55
|
-
}
|
56
|
-
var count2 = executePrerequisites();
|
57
|
-
var count3 = 0;
|
58
|
-
if (lateArray.length && count1 === 0 && count2 === 0) {
|
59
|
-
lateArray.sort(sortFunction);
|
60
|
-
var limitPriority = lateArray[0].priority - 5;
|
61
|
-
for (i = 0; i < lateArray.length; i++) {
|
62
|
-
if (lateArray[i].priority <= limitPriority) break;
|
63
|
-
}
|
64
|
-
count3 = i;
|
65
|
-
if (i === lateArray.length) {
|
66
|
-
queue = lateArray;
|
67
|
-
lateArray = [];
|
68
|
-
} else {
|
69
|
-
queue = lateArray.slice(0, i);
|
70
|
-
lateArray = lateArray.slice(i);
|
71
|
-
}
|
72
|
-
for (i = 0; i < queue.length; i++) {
|
73
|
-
if (queue[i].priority <= limitPriority) break;
|
74
|
-
queue[i].func.call(window);
|
75
|
-
}
|
76
|
-
}
|
77
|
-
if (writeOut && (count1 || count2 || count3)) {
|
78
|
-
console.log("MAIN: " + count1 + " REQ: " + count2 + " LATE: " + count3);
|
79
|
-
}
|
80
|
-
};
|
81
|
-
var isNotNull = function (a) {
|
82
|
-
return a !== null;
|
83
|
-
};
|
84
|
-
/** @return {number} */
|
85
|
-
var executePrerequisites = function () {
|
86
|
-
if (prerequisiteArray.length === 0) return 0;
|
87
|
-
var count = 0;
|
88
|
-
for (var i = 0; i < prerequisiteArray.length; i++) {
|
89
|
-
var pack = prerequisiteArray[i];
|
90
|
-
if (!pack) {
|
91
|
-
count++;
|
92
|
-
continue;
|
93
|
-
}
|
94
|
-
if (pack.pre) {
|
95
|
-
var instance = preparePrerequisite(pack.pre);
|
96
|
-
if (instance) {
|
97
|
-
count++;
|
98
|
-
prerequisiteArray[i] = null;
|
99
|
-
pack.func.call(window, instance);
|
100
|
-
}
|
101
|
-
} else {
|
102
|
-
var instances = prepareMorePrerequisites(pack.pres);
|
103
|
-
if (instances) {
|
104
|
-
count++;
|
105
|
-
prerequisiteArray[i] = null;
|
106
|
-
pack.func.apply(window, instances);
|
107
|
-
}
|
108
|
-
}
|
109
|
-
}
|
110
|
-
if (count) {
|
111
|
-
prerequisiteArray = prerequisiteArray.filter(isNotNull);
|
112
|
-
count += executePrerequisites();
|
113
|
-
}
|
114
|
-
return count;
|
115
|
-
};
|
116
|
-
/**
|
117
|
-
* @param {(Function|string)} getter
|
118
|
-
* @return {(Object|null)}
|
119
|
-
*/
|
120
|
-
var preparePrerequisite = function (getter) {
|
121
|
-
var instance;
|
122
|
-
if (typeof getter === "string") {
|
123
|
-
if (moduleInstances[getter]) {
|
124
|
-
return moduleInstances[getter];
|
125
|
-
} else if (moduleGetters[getter]) {
|
126
|
-
instance = moduleGetters[getter]();
|
127
|
-
if (instance) {
|
128
|
-
moduleInstances[getter] = instance;
|
129
|
-
}
|
130
|
-
return instance;
|
131
|
-
}
|
132
|
-
return null;
|
133
|
-
} else {
|
134
|
-
return getter();
|
135
|
-
}
|
136
|
-
};
|
137
|
-
var prepareMorePrerequisites = function (getters) {
|
138
|
-
var instance, instances = [];
|
139
|
-
for (var j = 0; j < getters.length; j++) {
|
140
|
-
instance = preparePrerequisite(getters[j]);
|
141
|
-
if (!instance) return null;
|
142
|
-
instances.push(instance);
|
143
|
-
}
|
144
|
-
return instances;
|
145
|
-
};
|
146
|
-
|
147
|
-
var cycle = function scheduleCycle() {
|
148
|
-
setTimeout(cycle, 30);
|
149
|
-
tick();
|
150
|
-
};
|
151
|
-
document.addEventListener("DOMContentLoaded", cycle);
|
152
|
-
/**
|
153
|
-
*
|
154
|
-
* @type {{late: EasyGem.schedule.late, require: EasyGem.schedule.require, main: EasyGem.schedule.main, define: EasyGem.schedule.define}}
|
155
|
-
*/
|
156
|
-
EasyGem.schedule = {
|
157
|
-
/**
|
158
|
-
* Functions, which should be executed right after "DOMContentLoaded" event.
|
159
|
-
* @param {Function} func
|
160
|
-
* @param {number} [priority=0] - Greater the priority, sooner [func] are called. Each 5 priority delays execution
|
161
|
-
* by 30ms. Also negative values are accepted.
|
162
|
-
*/
|
163
|
-
main: function (func, priority) {
|
164
|
-
mainArray.push({func: func, priority: priority || 0})
|
165
|
-
},
|
166
|
-
/**
|
167
|
-
* Functions, which should wait for [prerequisite] fulfillment
|
168
|
-
* After that [func] is executed with return value of [prerequisite] as parameter
|
169
|
-
* @example
|
170
|
-
* // execute function after jQuery and window.logger are present
|
171
|
-
* EasyGem.schedule.require(function($,logger){
|
172
|
-
* logger.log($.fn.jquery);
|
173
|
-
* },'jQuery',function(){
|
174
|
-
* return window.logger;
|
175
|
-
* });
|
176
|
-
* @param {Function} func - function which will be called when all prerequisites are met. Results of prerequisites
|
177
|
-
* are send into [func] as parameters
|
178
|
-
* @param {...(SchedulePrerequisite|string)} prerequisite - more than one prerequisite can be specified here
|
179
|
-
* as rest parameters. Function or String are accepted. If String is used,
|
180
|
-
* predefined getter from [moduleGetters] or getter defined by [define]
|
181
|
-
* are called.
|
182
|
-
*/
|
183
|
-
require: function (func, prerequisite) {
|
184
|
-
if (arguments.length > 2) {
|
185
|
-
var pres = [];
|
186
|
-
for (var i = 1; i < arguments.length; i++) {
|
187
|
-
if (typeof arguments[i] === "string") {
|
188
|
-
pres.push(arguments[i].toLowerCase());
|
189
|
-
} else {
|
190
|
-
pres.push(arguments[i]);
|
191
|
-
}
|
192
|
-
}
|
193
|
-
var instances = prepareMorePrerequisites(pres);
|
194
|
-
if (instances) {
|
195
|
-
func.apply(window, instances);
|
196
|
-
} else {
|
197
|
-
prerequisiteArray.push({func: func, pres: pres});
|
198
|
-
}
|
199
|
-
} else {
|
200
|
-
if (typeof prerequisite === "string") {
|
201
|
-
prerequisite = prerequisite.toLowerCase();
|
202
|
-
}
|
203
|
-
var instance = preparePrerequisite(prerequisite);
|
204
|
-
if (instance) {
|
205
|
-
func.call(window, instance);
|
206
|
-
} else {
|
207
|
-
prerequisiteArray.push({func: func, pre: prerequisite});
|
208
|
-
}
|
209
|
-
}
|
210
|
-
},
|
211
|
-
/**
|
212
|
-
* Functions, which should be executed after several loops after "DOMContentLoaded" event.
|
213
|
-
* Each 5 levels of priority increase delay by one stack.
|
214
|
-
* @param {Function} func
|
215
|
-
* @param {number} [priority=0]
|
216
|
-
*/
|
217
|
-
late: function (func, priority) {
|
218
|
-
lateArray.push({func: func, priority: priority || 0})
|
219
|
-
},
|
220
|
-
/**
|
221
|
-
* Define module, which will be loaded by [require] function with [name] prerequisite
|
222
|
-
* Only one instance will be created and cached also for future use.
|
223
|
-
* If no one request the module, getter is never called.
|
224
|
-
* @example
|
225
|
-
* EasyGem.schedule.define('Counter', function () {
|
226
|
-
* var count = 0;
|
227
|
-
* return function () {
|
228
|
-
* console.log("Count: " + count++);
|
229
|
-
* }
|
230
|
-
* });
|
231
|
-
* @param {string} name
|
232
|
-
* @param {Function} getter - getter or constructor
|
233
|
-
* @param {...(SchedulePrerequisite|string)} [prerequisite] - more than one prerequisite can be specified here
|
234
|
-
* as rest parameters. Function or String are accepted. If String is used,
|
235
|
-
* predefined getter from [moduleGetters] or getter defined by [define]
|
236
|
-
* are called.
|
237
|
-
*/
|
238
|
-
define: function (name, getter, prerequisite) {
|
239
|
-
if (prerequisite) {
|
240
|
-
this.require.apply(this, [function () {
|
241
|
-
moduleGetters[name.toLowerCase()] = getter;
|
242
|
-
}].concat(Array.prototype.slice.call(arguments, 2)));
|
243
|
-
} else {
|
244
|
-
moduleGetters[name.toLowerCase()] = getter;
|
245
|
-
}
|
246
|
-
}
|
247
|
-
};
|
248
|
-
EASY.schedule = EasyGem.schedule;
|
249
|
-
EasyGem.test.schedule = {
|
250
|
-
setOut: function (state) {
|
251
|
-
writeOut = state;
|
252
|
-
},
|
253
|
-
isLoaded: function () {
|
254
|
-
if (mainArray.length > 0) return false;
|
255
|
-
if (prerequisiteArray.length > 0) return false;
|
256
|
-
return lateArray.length <= 0;
|
257
|
-
},
|
258
|
-
lateIsLoaded: function () {
|
259
|
-
return lateArray.length <= 0;
|
260
|
-
},
|
261
|
-
queueContents: function () {
|
262
|
-
var modules = [];
|
263
|
-
for (var i = 0; i < prerequisiteArray.length; i++) {
|
264
|
-
var prerequisite = prerequisiteArray[i];
|
265
|
-
if (typeof prerequisite.pre === "string") {
|
266
|
-
modules.push(prerequisite.pre);
|
267
|
-
} else if (prerequisite.pres) {
|
268
|
-
for (var j = 0; j < prerequisite.pres.length; j++) {
|
269
|
-
var pre = prerequisite.pres[j];
|
270
|
-
if (typeof pre === "string") {
|
271
|
-
modules.push(pre);
|
272
|
-
}
|
273
|
-
}
|
274
|
-
}
|
275
|
-
}
|
276
|
-
return {main: mainArray.length, late: lateArray, require: prerequisiteArray.length, waitsFor: modules};
|
277
|
-
}
|
278
|
-
};
|
279
|
-
})();
|
280
|
-
|
@@ -1,61 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Created by hosekp on 11/10/16.
|
3
|
-
*/
|
4
|
-
(function () {
|
5
|
-
/**
|
6
|
-
* @method fire
|
7
|
-
* @method on
|
8
|
-
* @method of
|
9
|
-
* @constructor
|
10
|
-
*/
|
11
|
-
function EventBus() {
|
12
|
-
this.registeredMap = {};
|
13
|
-
}
|
14
|
-
|
15
|
-
/**
|
16
|
-
* send event to all of the listeners of specified event
|
17
|
-
* @param {String} event
|
18
|
-
* @param {...any}
|
19
|
-
*/
|
20
|
-
EventBus.prototype.fire = function (event) {
|
21
|
-
var proFunctions = this.registeredMap[event];
|
22
|
-
if (!proFunctions) return;
|
23
|
-
var slicedArgs = Array.prototype.slice.call(arguments, 1);
|
24
|
-
for (var i = 0; i < proFunctions.length; i++) {
|
25
|
-
proFunctions[i].apply(this, slicedArgs);
|
26
|
-
}
|
27
|
-
};
|
28
|
-
/**
|
29
|
-
* Register listener of specified event
|
30
|
-
* @param {String} event
|
31
|
-
* @param {Function} func
|
32
|
-
*/
|
33
|
-
EventBus.prototype.on = function (event, func) {
|
34
|
-
var eventList = this.registeredMap[event];
|
35
|
-
if (!eventList) this.registeredMap[event] = eventList = [];
|
36
|
-
for (var i = 0; i < eventList.length; i++) {
|
37
|
-
if (eventList[i] === func) {
|
38
|
-
return;
|
39
|
-
}
|
40
|
-
}
|
41
|
-
eventList.push(func);
|
42
|
-
};
|
43
|
-
/**
|
44
|
-
*
|
45
|
-
* @param {String} event
|
46
|
-
* @param {Function} func
|
47
|
-
*/
|
48
|
-
EventBus.prototype.off = function (event, func) {
|
49
|
-
var eventList = this.registeredMap[event];
|
50
|
-
if (!eventList) return;
|
51
|
-
for (var i = 0; i < eventList.length; i++) {
|
52
|
-
if (eventList[i] === func) {
|
53
|
-
eventList.splice(i, 1);
|
54
|
-
return;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
};
|
58
|
-
window.EASY = window.EASY || {};
|
59
|
-
EASY.eventBusFactory = EventBus;
|
60
|
-
EASY.eventBus = new EventBus();
|
61
|
-
})();
|
@@ -1,15 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
-
* listed below.
|
4
|
-
*
|
5
|
-
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
-
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
-
*
|
8
|
-
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
-
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
-
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
-
* file per style scope.
|
12
|
-
*
|
13
|
-
*= require_tree .
|
14
|
-
*= require_self
|
15
|
-
*/
|
@@ -1,11 +0,0 @@
|
|
1
|
-
Description:
|
2
|
-
The entity generator creates new entity for Redmine plugin.
|
3
|
-
New entity is prepared for use in Redmine and Easy Redmine as well.
|
4
|
-
|
5
|
-
Example:
|
6
|
-
rails g redmine_extensions:entity NameOfNewPlugin Post
|
7
|
-
create plugins/name_of_new_plugin/app/controllers
|
8
|
-
...
|
9
|
-
|
10
|
-
Help:
|
11
|
-
rails g redmine_extensions:entity --help
|