redmine_extensions 0.4.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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