redmine_extensions 0.4.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -0
  3. data/app/assets/javascripts/redmine_extensions/redmine_extensions.js +1 -0
  4. data/app/helpers/redmine_extensions/application_helper.rb +27 -36
  5. data/app/views/common/_collapsible_module_layout.html.erb +20 -2
  6. data/app/views/easy_front_end/_easy_front_end.html.erb +1 -19
  7. data/db/migrate/20150705172511_create_easy_settings.rb +1 -1
  8. data/db/migrate/20160519161300_create_entity_assignments.rb +1 -1
  9. data/db/migrate/20190206121100_remove_foreign_key_from_easy_settings.rb +1 -1
  10. data/lib/generators/redmine_extensions/entity/templates/_sidebar.html.erb.erb +1 -1
  11. data/lib/generators/redmine_extensions/entity/templates/show.html.erb.erb +1 -1
  12. data/lib/redmine_extensions/engine.rb +9 -23
  13. data/lib/redmine_extensions/hooks.rb +1 -9
  14. data/lib/redmine_extensions/migration.rb +1 -1
  15. data/lib/redmine_extensions/patch_manager.rb +43 -43
  16. data/lib/redmine_extensions/version.rb +3 -1
  17. metadata +11 -35
  18. data/app/assets/javascripts/redmine_extensions/jasmine_lib/boot.js +0 -153
  19. data/app/assets/javascripts/redmine_extensions/jasmine_lib/jasmine_helper.js +0 -292
  20. data/app/assets/javascripts/redmine_extensions/jasmine_lib/jasmine_lib.js +0 -3
  21. data/app/assets/javascripts/redmine_extensions/jasmine_tests/extend.js +0 -32
  22. data/app/assets/javascripts/redmine_extensions/jasmine_tests/jasmine_tests.js +0 -1
  23. data/app/assets/javascripts/redmine_extensions/jasmine_tests/modules.js +0 -164
  24. data/app/assets/stylesheets/redmine_extensions/jasmine.css +0 -35
  25. data/app/views/redmine_extensions/_development_mode.html.erb +0 -33
  26. data/app/views/redmine_extensions/_jasmine.html.erb +0 -7
  27. data/lib/redmine_extensions/html_formatting/formatter.rb +0 -86
  28. data/lib/redmine_extensions/html_formatting/helper.rb +0 -73
  29. data/lib/redmine_extensions/html_formatting/internals.rb +0 -23
  30. data/lib/redmine_extensions/html_formatting.rb +0 -3
  31. data/spec/features/autocomplete_spec.rb +0 -17
  32. data/spec/features/jasmine_spec.rb +0 -9
  33. data/spec/support/plugin_generator.rb +0 -111
@@ -1,164 +0,0 @@
1
- describe("Modules", function () {
2
- var nameCounter = 0;
3
-
4
- function getName() {
5
- return "jasmine_test_" + (nameCounter++);
6
- }
7
-
8
- function defineOptionModule(moduleName, context) {
9
- EasyGem.module.module(moduleName, function () {
10
- return function () {
11
- context.add.apply(context, arguments);
12
- }
13
- });
14
- }
15
-
16
- function defineCallbackModule(moduleName, context) {
17
- EasyGem.module.module(moduleName, function () {
18
- return {
19
- add: function () {
20
- context.add.apply(context, arguments);
21
- }
22
- };
23
- });
24
- }
25
-
26
- function resolveModules() {
27
- EasyGem.module.setUrl("", null);
28
- }
29
-
30
- beforeEach(function () {
31
- this.counter = 1;
32
- var self = this;
33
- this.count = function () {
34
- self.counter++;
35
- };
36
- this.add = function (add) {
37
- for (var i = 0; i < arguments.length; i++) {
38
- self.counter += arguments[i];
39
- }
40
- };
41
- });
42
- describe("simple", function () {
43
- it("works with option", function () {
44
- var moduleName = getName();
45
- defineOptionModule(moduleName, this);
46
- expect(this.counter).toEqual(1);
47
- EasyGem.loadModule(moduleName, 5);
48
- expect(this.counter).toEqual(6);
49
- });
50
- it("works with callback", function () {
51
- var moduleName = getName();
52
- defineCallbackModule(moduleName, this);
53
- expect(this.counter).toEqual(1);
54
- EasyGem.loadModule(moduleName, function (module) {
55
- module.add(5);
56
- });
57
- expect(this.counter).toEqual(6);
58
- });
59
- it("works with multiple options", function () {
60
- var moduleName = getName();
61
- defineOptionModule(moduleName, this);
62
- expect(this.counter).toEqual(1);
63
- EasyGem.loadModule(moduleName, 5, 9);
64
- expect(this.counter).toEqual(15);
65
- });
66
- it("works with multiple options - loadModules", function () {
67
- var moduleName = getName();
68
- defineOptionModule(moduleName, this);
69
- expect(this.counter).toEqual(1);
70
- EasyGem.loadModules([moduleName], 5, 9);
71
- expect(this.counter).toEqual(15);
72
- });
73
- });
74
- describe("complex", function () {
75
- describe("handle define after request", function () {
76
- it("option", function () {
77
- var moduleName = getName();
78
- expect(this.counter).toEqual(1);
79
- EasyGem.loadModule(moduleName, 8);
80
- expect(this.counter).toEqual(1);
81
- defineOptionModule(moduleName, this);
82
- resolveModules();
83
- expect(this.counter).toEqual(9);
84
- });
85
- it("callback", function () {
86
- var moduleName = getName();
87
- expect(this.counter).toEqual(1);
88
- EasyGem.loadModule(moduleName, function (module) {
89
- module.add(3);
90
- });
91
- expect(this.counter).toEqual(1);
92
- defineCallbackModule(moduleName, this);
93
- resolveModules();
94
- expect(this.counter).toEqual(4);
95
- });
96
- });
97
- describe("parts", function () {
98
- it("define first", function () {
99
- var moduleName = getName();
100
- var self = this;
101
- EasyGem.module.part(moduleName, [], function () {
102
- this.add = function (option) {
103
- self.add(option);
104
- }
105
- });
106
- EasyGem.module.part(moduleName, function () {
107
- this.addDouble = function (option) {
108
- self.add(option * 2);
109
- }
110
- });
111
- expect(this.counter).toEqual(1);
112
- EasyGem.loadModule(moduleName, function (module) {
113
- module.add(3);
114
- module.addDouble(2);
115
- });
116
- expect(this.counter).toEqual(8);
117
- });
118
- it("request first", function () {
119
- var moduleName = getName();
120
- var self = this;
121
- EasyGem.loadModule(moduleName, function (module) {
122
- module.add(3);
123
- module.addDouble(2);
124
- });
125
- expect(this.counter).toEqual(1);
126
- EasyGem.module.part(moduleName, [], function () {
127
- this.add = function (option) {
128
- self.add(option);
129
- }
130
- });
131
- expect(this.counter).toEqual(1);
132
- EasyGem.module.part(moduleName, function () {
133
- this.addDouble = function (option) {
134
- self.add(option * 2);
135
- }
136
- });
137
- resolveModules();
138
- expect(this.counter).toEqual(8);
139
- });
140
- });
141
- it("handle complex tree", function () {
142
- var moduleName = getName();
143
- var subModuleName1 = getName();
144
- var subModuleName2 = getName();
145
- EasyGem.module.module(moduleName, [subModuleName1, subModuleName2], function (sub1, sub2) {
146
- this.sub1 = sub1;
147
- this.sub2 = sub2;
148
- });
149
- expect(this.counter).toEqual(1);
150
- EasyGem.loadModule(moduleName, function (module) {
151
- module.sub1(2);
152
- module.sub2.add(3);
153
- });
154
- resolveModules();
155
- expect(this.counter).toEqual(1);
156
- defineOptionModule(subModuleName1, this);
157
- resolveModules();
158
- expect(this.counter).toEqual(1);
159
- defineCallbackModule(subModuleName2, this);
160
- resolveModules();
161
- expect(this.counter).toEqual(6);
162
- });
163
- });
164
- });
@@ -1,35 +0,0 @@
1
- .jasmine-specs {
2
- margin: 0;
3
- }
4
-
5
- .jasmine_html-reporter {
6
- margin-left: 100px;
7
- margin-right: 100px;
8
- }
9
-
10
- .logo-jasmine {
11
- width: 60px;
12
- font-size: 0;
13
- text-align: center;
14
- color: white;
15
- background-color: #fe7d99;
16
- font-weight: normal;
17
- }
18
-
19
- .logo-jasmine--pass {
20
- background-color: #01c8a9;
21
- }
22
-
23
- .logo-jasmine:before {
24
- content: '\2620';
25
- font-size: 40px;
26
- }
27
-
28
- .logo-jasmine--pass:before {
29
- content: '\2705';
30
- }
31
-
32
- .logo-jasmine * {
33
- height: 0 !important;
34
- background-image: none !important;
35
- }
@@ -1,33 +0,0 @@
1
- <% if RedmineExtensions::PatchManager.reloadable_patches_applied > 1 %>
2
- <div class="patches-applied">
3
- <i class="icon icon-reload"></i> Patches applied (<%= RedmineExtensions::PatchManager.reloadable_patches_applied %>x)
4
- </div>
5
-
6
- <style>
7
- .patches-applied {
8
- position: fixed;
9
- bottom: 0;
10
- left: 0;
11
- padding: 2px 10px;
12
- background: #f44336;
13
- border-top: 1px solid #b71c1c;
14
- border-right: 1px solid #b71c1c;
15
- animation: patches-applied-pulse 2s 5;
16
- color: #fff;
17
- z-index: 9999999;
18
- }
19
-
20
- @keyframes patches-applied-pulse {
21
- 0% {
22
- box-shadow: 0 0 0 0 rgba(229, 115, 115, 0.4);
23
- }
24
- 70% {
25
- box-shadow: 0 0 0 20px rgba(229, 115, 115, 0);
26
- }
27
- 100% {
28
- box-shadow: 0 0 0 0 rgba(229, 115, 115, 0);
29
- }
30
- }
31
- </style>
32
- <% end %>
33
-
@@ -1,7 +0,0 @@
1
- <% if params[:jasmine] %>
2
- <script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.1.0/jasmine.js"></script>
3
- <script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.1.0/jasmine-html.js"></script>
4
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.1.0/jasmine.css">
5
- <%= javascript_include_tag('redmine_extensions/jasmine_lib/jasmine_lib.js') %>
6
- <%= stylesheet_link_tag('redmine_extensions/jasmine.css') %>
7
- <% end %>
@@ -1,86 +0,0 @@
1
- module RedmineExtensions
2
- module HTMLFormatting
3
- class Formatter < String
4
- include ActionView::Helpers::TagHelper
5
-
6
- RULES = [:tidy_html_from_editor, :rinku_auto_link, :inline_auto_link, :inline_auto_mailto]
7
-
8
- def to_html(*rules, &block)
9
- ret = self.dup
10
- RULES.each do |rule_name|
11
- ret = (method(rule_name).call(ret) || ret)
12
- end
13
- ret
14
- end
15
-
16
- private
17
-
18
- AUTO_LINK_RE = %r{
19
- ( # leading text
20
- #<\w+.*?>| # leading HTML tag, or
21
- [^=<>!:'"/]| # leading punctuation, or
22
- \{\{\w+\(| # inside a macro?
23
- ^ # beginning of line
24
- )
25
- (
26
- (?:https?://)| # protocol spec, or
27
- (?:s?ftps?://)|
28
- (?:www\.)| # www.*
29
- (?:smb://)|
30
- (?:file://)
31
- )
32
- (
33
- (\S+?) # url
34
- (\/)? # slash
35
- )
36
- ((?:&gt;)?|[^\w\=\/;\(\)]*?) # post
37
- (?=<|\s|&nbsp;|$)
38
- }x unless const_defined?(:AUTO_LINK_RE)
39
-
40
- def rinku_auto_link(text)
41
- Rinku.auto_link(text) if defined?(Rinku)
42
- end
43
-
44
- # Turns all urls into clickable links (code from Rails).
45
- def inline_auto_link(text, regex_rules = AUTO_LINK_RE)
46
- text.gsub!(regex_rules) do
47
- all, leading, proto, url, post = $&, $1, $2, $3, $6
48
-
49
- if leading =~ /<a\s/i || leading =~ /![<>=]?/ || leading =~ /\{\{\w+\(/
50
- #don't replace URL's that are already linked
51
- #and URL's prefixed with ! !> !< != (textile images)
52
- all
53
- else
54
- #Idea below : an URL with unbalanced parethesis and
55
- #ending by ')' is put into external parenthesis
56
- if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
57
- url=url[0..-2] # discard closing parenth from url
58
- post = ")"+post # add closing parenth to post
59
- end
60
-
61
- tag = content_tag('a', proto + url, :href => "#{proto == 'www.' ? "http://#{proto}" : proto}#{url}", :class => 'external', :target => '_blank')
62
- %(#{leading}#{tag}#{post})
63
- end
64
- end
65
- end
66
-
67
- # Turns all email addresses into clickable links (code from Rails).
68
- def inline_auto_mailto(text)
69
- text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
70
- mail = $1
71
- if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
72
- mail
73
- else
74
- content_tag('a', mail, :href => "mailto:#{mail}", :class => "email")
75
- end
76
- end
77
- end
78
-
79
- # Tidy html. Fix html tags for correct show pages.
80
- def tidy_html_from_editor(text)
81
- Nokogiri::HTML::DocumentFragment.parse(text).to_html
82
- end
83
-
84
- end
85
- end
86
- end
@@ -1,73 +0,0 @@
1
- module RedmineExtensions
2
- module HTMLFormatting
3
- module Helper
4
-
5
- def wikitoolbar_for(field_id, options={})
6
- heads_for_wiki_formatter
7
-
8
- custom_settings = options.delete(:custom)
9
- options[:toolbar] ||= EasySetting.value('ckeditor_toolbar_config') || 'Basic'
10
- options[:lang] ||= User.current.language
11
- options[:lang] = Setting.default_language if options[:lang].blank?
12
- options[:language] = options[:lang] if options[:lang].present?
13
-
14
- # Syntax higlight
15
- if EasySetting.value('ckeditor_syntax_highlight_enabled')
16
- options[:codeSnippet_theme] ||= EasyCKEditor.syntaxt_higlight_template
17
- else
18
- if options[:removePlugins]
19
- options[:removePlugins] << ','
20
- else
21
- options[:removePlugins] = ''
22
- end
23
-
24
- options[:removePlugins] << 'codesnippet'
25
- end
26
-
27
- hook_settings = call_hook(:helper_ckeditor_wikitoolbar_for_add_option, {:field_id => field_id, :options => options})
28
-
29
- ck_options = options.collect{|k,v| "#{k}:'#{v}'"}
30
- ck_options << custom_settings unless custom_settings.blank?
31
- ck_options << hook_settings unless hook_settings.to_s.blank?
32
-
33
- reminder_confirm = options[:attachment_reminder_message] ? options[:attachment_reminder_message] : l(:text_easy_attachment_reminder_confirm)
34
- reminderjs = options[:attachment_reminder] ? "$('##{field_id}').addClass('set_attachment_reminder').data('ck', true).data('reminder_words', \"#{j(Attachment.attachment_reminder_words)}\").data('reminder_confirm', '#{j(reminder_confirm)}'); " : ''
35
-
36
- js = "var ta_editor = CKEDITOR.instances['#{field_id}']; if (ta_editor) {CKEDITOR.remove(ta_editor);} CKEDITOR.replace('#{field_id}',{#{ck_options.join(',')}});"
37
- js << "window.enableWarnLeavingUnsaved = '#{User.current.pref.warn_on_leaving_unsaved}';"
38
-
39
- javascript_tag(reminderjs + js)
40
- end
41
-
42
- def initial_page_content(page)
43
- end
44
-
45
- def heads_for_wiki_formatter
46
- unless @heads_for_wiki_formatter_included
47
- header = javascript_include_tag('redmine_extensions/ckeditor/ckeditor')
48
-
49
- # Syntax higlight
50
- if EasySetting.value('ckeditor_syntax_highlight_enabled')
51
- header << stylesheet_link_tag(EasyCKEditor.syntaxt_higlight_css)
52
- header << javascript_include_tag(EasyCKEditor.syntaxt_higlight_js)
53
-
54
- # Pre block have custom css attributes
55
- # .pre-hljs will set them to default
56
- header << javascript_tag(%{
57
- $(document).ready(function() {
58
- $('pre code').each(function(i, block) {
59
- hljs.highlightBlock(block);
60
- $(block).parent().addClass('pre-default');
61
- });
62
- });
63
- })
64
- end
65
-
66
- content_for(:header_tags, header)
67
- @heads_for_wiki_formatter_included = true
68
- end
69
-
70
- end
71
- end
72
- end
73
- end
@@ -1,23 +0,0 @@
1
- module EasyCKEditor
2
-
3
- def self.syntaxt_higlight_default_template
4
- @syntaxt_higlight_default_template ||= 'github'
5
- end
6
-
7
- def self.syntaxt_higlight_templates
8
- @syntaxt_higlight_templates ||= ['github', 'googlecode', 'idea', 'monokai', 'monokai_sublime', 'railscasts']
9
- end
10
-
11
- def self.syntaxt_higlight_template
12
- EasySetting.value('ckeditor_syntax_highlight_theme') || syntaxt_higlight_default_template
13
- end
14
-
15
- def self.syntaxt_higlight_css
16
- "/plugin_assets/ckeditor/javascripts/ckeditor/plugins/codesnippet/lib/highlight/styles/#{syntaxt_higlight_template}"
17
- end
18
-
19
- def self.syntaxt_higlight_js
20
- @syntaxt_higlight_js ||= "/plugin_assets/ckeditor/javascripts/ckeditor/plugins/codesnippet/lib/highlight/highlight.pack.js"
21
- end
22
-
23
- end
@@ -1,3 +0,0 @@
1
- require_relative './html_formatting/internals'
2
- require_relative './html_formatting/formatter'
3
- require_relative './html_formatting/helper'
@@ -1,17 +0,0 @@
1
- RSpec.describe 'autocomplete', type: :feature, js: true, logged: :admin do
2
-
3
- describe 'render' do
4
- it 'generate default autocomplete' do
5
- visit '/dummy_autocompletes'
6
- expect(page).to have_css('input#default[type="search"]')
7
- expect(page).to have_css('input[type="hidden"][name="default"][value="value1"]', visible: false)
8
- end
9
-
10
- it 'generate autocomplete_field in form_for' do
11
- visit '/dummy_autocompletes'
12
- expect(page).to have_css('input#dummy_entities_autocomplete[type="search"]')
13
- expect(page).to have_css('input[type="hidden"][name="dummy_entity[array_of_dummies][]"][value="value1"]', visible: false)
14
- end
15
- end
16
-
17
- end
@@ -1,9 +0,0 @@
1
- RSpec.describe 'jasmine', type: :feature, js: true, logged: :admin do
2
-
3
- it 'run tests' do
4
- visit "/dummy_entities?jasmine=true"
5
- expect(page).to have_css('.jasmine-bar')
6
- expect(page.evaluate_script('window.jasmineHelper.parseResult();')).to eq('success')
7
- end
8
-
9
- end
@@ -1,111 +0,0 @@
1
- require 'rails/generators'
2
-
3
- module PluginGenerator
4
- def self.generate_test_plugin!
5
- Rails::Generators.invoke 'redmine_extensions:plugin', ['DummyPlugin'], behavior: :revoke, destination_root: Rails.root
6
- Rails::Generators.invoke 'redmine_extensions:plugin', ['DummyPlugin'], behavior: :invoke, destination_root: Rails.root
7
- generate_autocomplete!
8
- end
9
-
10
- def self.generate_autocomplete!
11
- generate_dummy_entity!
12
- generate_entities_view!
13
- generate_autocomplete_controller!
14
- generate_autocomplete_routes!
15
- generate_autocomplete_view!
16
- end
17
-
18
- def self.generate_dummy_entity!
19
- File.open( Rails.root.join('plugins', 'dummy_plugin', 'db', 'migrate', '20162010160230_create_dummy_entities.rb'), 'w' ) do |file|
20
- file.write( <<-END_RUBY )
21
- class CreateDummyEntities < ActiveRecord::Migration[4.2]
22
- def change
23
- create_table :dummy_entities do |t|
24
- t.string :name
25
- t.integer :value
26
- t.references :project, index: true
27
- t.text :array_of_dummies
28
- end
29
- end
30
- end
31
- END_RUBY
32
- end
33
-
34
- File.open( Rails.root.join('plugins', 'dummy_plugin', 'app', 'models', 'dummy_entity.rb'), 'w' ) do |file|
35
- file.write( <<-END_RUBY )
36
- class DummyEntity < ActiveRecord::Base
37
- include Redmine::SafeAttributes
38
- safe_attributes 'name',
39
- 'value',
40
- 'project_id',
41
- 'array_of_dummies'
42
-
43
- serialize :array_of_dummies, Array
44
- end
45
- END_RUBY
46
- end
47
-
48
- File.open(Rails.root.join('plugins', 'dummy_plugin', 'app', 'controllers', 'dummy_entities_controller.rb'), 'w') do |file|
49
- file.write( <<-END_RUBY )
50
- class DummyEntitiesController < ApplicationController
51
- def index
52
- end
53
-
54
- def create
55
- @entity = DummyEntity.new
56
- @entity.safe_attributes = params[:dummy_entity]
57
- @entity.save
58
- end
59
- end
60
- END_RUBY
61
- end
62
- end
63
-
64
- def self.generate_autocomplete_controller!
65
- File.open(Rails.root.join('plugins', 'dummy_plugin', 'app', 'controllers', 'dummy_autocompletes_controller.rb'), 'w') do |file|
66
- file.write( <<-END_RUBY )
67
- class DummyAutocompletesController < ApplicationController
68
- def index
69
- end
70
- end
71
- END_RUBY
72
- end
73
- end
74
-
75
- def self.generate_autocomplete_routes!
76
- File.open(Rails.root.join('plugins', 'dummy_plugin', 'config', 'routes.rb'), 'w') do |file|
77
- file.write( <<-END_ROUTES )
78
- resources :dummy_autocompletes
79
- resources :dummy_entities
80
- END_ROUTES
81
- end
82
- end
83
-
84
- def self.generate_autocomplete_view!
85
- dir = Rails.root.join('plugins', 'dummy_plugin', 'app', 'views', 'dummy_autocompletes')
86
- Dir.mkdir dir
87
- File.open(dir.join('index.html.erb'), 'w') do |file|
88
- file.write( <<-END_ERB )
89
- <%= form_tag('/dummy_autocompletes', id: 'autocompletes_form') do %>
90
- <%= autocomplete_field_tag('default', ['value1', 'value2'], ['value1']) %>
91
- <% end %>
92
-
93
- <%= form_for(DummyEntity.new(array_of_dummies: ['value1'])) do |f| %>
94
- <%= f.autocomplete_field(:array_of_dummies, ['value1', 'value2'], {}, id: 'dummy_entities_autocomplete') %>
95
- <% end %>
96
- END_ERB
97
- end
98
- end
99
-
100
- def self.generate_entities_view!
101
- dir = Rails.root.join('plugins', 'dummy_plugin', 'app', 'views', 'dummy_entities')
102
- Dir.mkdir dir
103
- File.open(dir.join('index.html.erb'), 'w') do |file|
104
- file.write( <<-END_ERB )
105
- <% DummyEntity.all.each do |entity| %>
106
- <%= entity.name %>: <%= entity.value %>
107
- <% end %>
108
- END_ERB
109
- end
110
- end
111
- end