redmine_airbrake_backend 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7d8a9f1faf769ff932d95c7c834ee697dac4c686
4
+ data.tar.gz: 1b6e0a26f4e5587afa7be9fa38bedf892e79c784
5
+ SHA512:
6
+ metadata.gz: 1abf3729e9b38f7d0eb6d195d642632513ba8457cd84b0aa710d10d62725be77af8c25fd272341901033c62d5d5acbf5e8923da33d3ce6862f5c06de744ead16
7
+ data.tar.gz: 0ced6e811c22c9e5ed5587dad4c359f120edb1df50745c94916da5495fe02cb6acba2353ecb86d536d2fca50024528bf723e5567676fd92aed659ae678f12627
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ test/dummy/db/*.sqlite3
5
+ test/dummy/db/*.sqlite3-journal
6
+ test/dummy/log/*.log
7
+ test/dummy/tmp/
8
+ test/dummy/.sass-cache
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redmine_airbrake_backend.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,92 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ redmine_airbrake_backend (0.1.0)
5
+ hpricot
6
+ htmlentities
7
+ rails
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionmailer (4.0.0)
13
+ actionpack (= 4.0.0)
14
+ mail (~> 2.5.3)
15
+ actionpack (4.0.0)
16
+ activesupport (= 4.0.0)
17
+ builder (~> 3.1.0)
18
+ erubis (~> 2.7.0)
19
+ rack (~> 1.5.2)
20
+ rack-test (~> 0.6.2)
21
+ activemodel (4.0.0)
22
+ activesupport (= 4.0.0)
23
+ builder (~> 3.1.0)
24
+ activerecord (4.0.0)
25
+ activemodel (= 4.0.0)
26
+ activerecord-deprecated_finders (~> 1.0.2)
27
+ activesupport (= 4.0.0)
28
+ arel (~> 4.0.0)
29
+ activerecord-deprecated_finders (1.0.3)
30
+ activesupport (4.0.0)
31
+ i18n (~> 0.6, >= 0.6.4)
32
+ minitest (~> 4.2)
33
+ multi_json (~> 1.3)
34
+ thread_safe (~> 0.1)
35
+ tzinfo (~> 0.3.37)
36
+ arel (4.0.0)
37
+ atomic (1.1.10)
38
+ builder (3.1.4)
39
+ erubis (2.7.0)
40
+ hike (1.2.3)
41
+ hpricot (0.8.6)
42
+ htmlentities (4.3.1)
43
+ i18n (0.6.4)
44
+ mail (2.5.4)
45
+ mime-types (~> 1.16)
46
+ treetop (~> 1.4.8)
47
+ mime-types (1.23)
48
+ minitest (4.7.5)
49
+ multi_json (1.7.7)
50
+ polyglot (0.3.3)
51
+ rack (1.5.2)
52
+ rack-test (0.6.2)
53
+ rack (>= 1.0)
54
+ rails (4.0.0)
55
+ actionmailer (= 4.0.0)
56
+ actionpack (= 4.0.0)
57
+ activerecord (= 4.0.0)
58
+ activesupport (= 4.0.0)
59
+ bundler (>= 1.3.0, < 2.0)
60
+ railties (= 4.0.0)
61
+ sprockets-rails (~> 2.0.0)
62
+ railties (4.0.0)
63
+ actionpack (= 4.0.0)
64
+ activesupport (= 4.0.0)
65
+ rake (>= 0.8.7)
66
+ thor (>= 0.18.1, < 2.0)
67
+ rake (10.1.0)
68
+ sprockets (2.10.0)
69
+ hike (~> 1.2)
70
+ multi_json (~> 1.0)
71
+ rack (~> 1.0)
72
+ tilt (~> 1.1, != 1.3.0)
73
+ sprockets-rails (2.0.0)
74
+ actionpack (>= 3.0)
75
+ activesupport (>= 3.0)
76
+ sprockets (~> 2.8)
77
+ thor (0.18.1)
78
+ thread_safe (0.1.0)
79
+ atomic
80
+ tilt (1.4.1)
81
+ treetop (1.4.14)
82
+ polyglot
83
+ polyglot (>= 0.3.1)
84
+ tzinfo (0.3.37)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ bundler (~> 1.3)
91
+ rake
92
+ redmine_airbrake_backend!
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Florian Schwab
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Redmine Airbrake Backend
2
+
3
+ This plugin provides the necessary API to use Redmine as an Airbrake backend.
4
+
5
+ ## Installation
6
+
7
+ Apply this [patch](http://www.redmine.org/issues/14402) to Redmine.
8
+
9
+ Add this line to your Redmine Gemfile:
10
+
11
+ gem 'redmine_airbrake_backend'
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+ $ rake redmine:plugins:migrate
17
+
18
+ ## Integration
19
+
20
+ 1. Create the following custom fields for issues:
21
+ * Airbrake Hash (String)
22
+ * Number of Occurrences (Integer) (optional)
23
+ 2. Configure the plugin to use these 2 custom fields (Administration -> Plugins -> Configure)
24
+ 3. Enable the project module (Airbrake) in your project settings
25
+ 4. Configure additionale defaults under the settings tab (Airbrake)
26
+
27
+ ## Client configuration
28
+
29
+ For a Rails application, just setup the [Airbrake notifier](https://github.com/airbrake/airbrake) as usual, then modify `config/initializers/airbrake.rb` according to your needs using this template:
30
+
31
+ ```ruby
32
+ Airbrake.configure do |config|
33
+ config.api_key = {
34
+ project: 'redmine_project_identifier', # the identifier you specified for your project in Redmine
35
+ api_key: 'redmine_api_key', # the api key for a user which has permission to create issues in the project specified in the previous step
36
+ tracker: 'Bug', # the name or id of your desired tracker (optional if default is configured)
37
+ category: 'Development', # the name or id of a ticket category, optional
38
+ priority: 5 # the name or id of the priority for new tickets, optional.
39
+ assignee: 'admin', # the login or id of a user the ticket should get assigned to by default, optional
40
+ }.to_json
41
+ config.host = 'my_redmine_host.com' # the hostname your Redmine runs at
42
+ config.port = 443 # the port your Redmine runs at
43
+ config.secure = true # sends data to your server using SSL, optional
44
+ end
45
+ ```
46
+
47
+ ## Notes
48
+
49
+ Based and inspired by https://github.com/milgner/redmine_airbrake_server
50
+
51
+ ## Code Status
52
+
53
+ * [![Dependencies](https://gemnasium.com/ydkn/redmine_airbrake_backend.png?travis)](https://gemnasium.com/ydkn/redmine_airbrake_backend)
54
+ * [![Code Climate](https://codeclimate.com/github/ydkn/redmine_airbrake_backend.png)](https://codeclimate.com/github/ydkn/redmine_airbrake_backend)
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RedmineAirbrakeBackend'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,182 @@
1
+ require 'json'
2
+ require 'hpricot'
3
+ require 'htmlentities'
4
+
5
+ class AirbrakeController < ::ApplicationController
6
+ SUPPORTED_API_VERSIONS = %w(2.4)
7
+
8
+ prepend_before_filter :set_api_auth
9
+ before_filter :parse_notice
10
+ before_filter :init_vars
11
+
12
+ accept_api_auth :notice
13
+
14
+ def notice
15
+ return unless authorize(:issues, :create)
16
+
17
+ raise ArgumentError.new("Airbrake version not supported") unless SUPPORTED_API_VERSIONS.include?(@notice[:version])
18
+ raise ArgumentError.new("Project not found") unless @project
19
+ raise ArgumentError.new("Tracker not found") unless @tracker
20
+ raise ArgumentError.new("Tracker does not have a notice hash custom field") unless @tracker.custom_fields.where(id: notice_hash_field.id).first
21
+
22
+ # Issue by project, tracker and hash
23
+ issue_ids = CustomValue.where(customized_type: Issue.name, custom_field_id: notice_hash_field.id, value: notice_hash).select([:customized_id]).collect{|cv| cv.customized_id}
24
+ @issue = Issue.where(id: issue_ids, project_id: @project.id, tracker_id: @tracker.id).first
25
+ @issue = Issue.new(
26
+ subject: subject,
27
+ project: @project,
28
+ tracker: @tracker,
29
+ author: User.current,
30
+ category: @category,
31
+ priority: @priority,
32
+ description: render_to_string(partial: 'issue_description'),
33
+ assigned_to: @assignee
34
+ ) unless @issue
35
+
36
+ custom_field_values = {}
37
+
38
+ # Update occurrences
39
+ occurrences_value = @issue.custom_value_for(occurrences_field.id)
40
+ custom_field_values[occurrences_field.id] = ((occurrences_value ? occurrences_value.value.to_i : 0) + 1).to_s if occurrences_field.present?
41
+
42
+ # Reopen if closed
43
+ if reopen? && @issue.status.is_closed?
44
+ @issue.status = IssueStatus.where(is_default: true).order(:position).first
45
+ @issue.init_journal(User.current, "Issue reopened after occurring again in environment #{@notice[:server_environment][:environment_name]}")
46
+ end
47
+
48
+ # Hash
49
+ custom_field_values[notice_hash_field.id] = notice_hash if @issue.new_record?
50
+
51
+ @issue.custom_field_values = custom_field_values
52
+
53
+ if @issue.save
54
+ render xml: {
55
+ notice: {
56
+ id: notice_hash,
57
+ url: issue_url(@issue)
58
+ }
59
+ }
60
+ else
61
+ render nothing: true, status: :internal_server_error
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def set_api_auth
68
+ params[:key] = redmine_params[:api_key] rescue nil
69
+ end
70
+
71
+ def redmine_params
72
+ JSON.parse(params[:notice][:api_key]).symbolize_keys
73
+ end
74
+ helper_method :redmine_params
75
+
76
+ def subject
77
+ if @notice[:error][:message].starts_with?("#{@notice[:error][:class]}:")
78
+ "[#{notice_hash[0..7]}] #{@notice[:error][:message]}"[0..254]
79
+ else
80
+ "[#{notice_hash[0..7]}] #{@notice[:error][:class]} #{@notice[:error][:message]}"[0..254]
81
+ end
82
+ end
83
+
84
+ def backtrace
85
+ if @notice[:error][:backtrace][:line].is_a?(Hash)
86
+ [@notice[:error][:backtrace][:line]]
87
+ else
88
+ @notice[:error][:backtrace][:line]
89
+ end
90
+ end
91
+ helper_method :backtrace
92
+
93
+ def notice_hash
94
+ h = []
95
+ h << @notice[:error][:class]
96
+ h << @notice[:error][:message]
97
+ h += backtrace.collect{|element| "#{element[:file]}|#{element[:method].gsub(/_\d+_/, '')}|#{element[:number]}"}
98
+
99
+ Digest::MD5.hexdigest(h.compact.join("\n"))
100
+ end
101
+
102
+ def notice_hash_field
103
+ custom_field(:hash_field)
104
+ end
105
+
106
+ def occurrences_field
107
+ custom_field(:occurrences_field)
108
+ end
109
+
110
+ def custom_field(key)
111
+ @project.issue_custom_fields.where(id: setting(key)).first || CustomField.where(id: setting(key), is_for_all: true).first
112
+ end
113
+
114
+ def reopen?
115
+ return false if project_setting(:reopen_regexp).blank?
116
+ !!(@notice[:server_environment][:environment_name] =~ /#{project_setting(:reopen_regexp)}/i)
117
+ end
118
+
119
+ def setting(key)
120
+ Setting.plugin_redmine_airbrake_backend[key]
121
+ end
122
+
123
+ def project_setting(key)
124
+ return nil if @project.airbrake_settings.blank?
125
+ @project.airbrake_settings.send(key) if @project.airbrake_settings.respond_to?(key)
126
+ end
127
+
128
+ def parse_notice
129
+ @notice = params[:notice]
130
+
131
+ return @notice if @notice[:request].blank?
132
+
133
+ doc = Hpricot::XML(request.body)
134
+
135
+ convert_request_vars(:params, :params)
136
+ convert_request_vars(:cgi_data, :'cgi-data')
137
+ convert_request_vars(:session, :session)
138
+
139
+ unless @notice[:request][:params].blank?
140
+ @notice[:request][:params].delete(:action) # already known
141
+ @notice[:request][:params].delete(:controller) # already known
142
+ end
143
+ rescue
144
+ @notice = nil
145
+ render nothing: true, status: :bad_request
146
+ end
147
+
148
+ def convert_request_vars(type, pathname)
149
+ unless @notice[:request][type.to_sym].blank?
150
+ vars = convert_var_elements(doc/"/notice/request/#{pathname}/var")
151
+ @notice[:request][type.to_sym] = vars
152
+ end
153
+ end
154
+
155
+ def convert_var_elements(elements)
156
+ result = {}
157
+ elements.each do |elem|
158
+ result[elem.attributes['key']] = elem.inner_text
159
+ end
160
+ result.delete_if{|k,v| k.strip.blank?}
161
+ result.symbolize_keys
162
+ end
163
+
164
+ def init_vars
165
+ # Project
166
+ @project = Project.where(identifier: redmine_params[:project]).first
167
+ return unless @project
168
+
169
+ # Tracker
170
+ @tracker = @project.trackers.where(id: redmine_params[:tracker]).first || @project.trackers.where(name: redmine_params[:tracker]).first || project_setting(:tracker)
171
+
172
+ # Category
173
+ @category = @project.issue_categories.where(id: redmine_params[:category]).first || @project.issue_categories.where(name: redmine_params[:category]).first || project_setting(:category)
174
+
175
+ # Priority
176
+ @priority = IssuePriority.where(id: redmine_params[:priority]).first || IssuePriority.where(name: redmine_params[:priority]).first || project_setting(:priority) || IssuePriority.default
177
+
178
+ # Assignee
179
+ @assignee = @project.users.where(id: redmine_params[:assignee]).first || @project.users.where(login: redmine_params[:assignee]).first
180
+ end
181
+
182
+ end
@@ -0,0 +1,25 @@
1
+ class AirbrakeProjectSettingsController < ::ApplicationController
2
+ before_filter :find_project
3
+
4
+ menu_item :settings
5
+
6
+ def update
7
+ @airbrake_project_setting = @project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)
8
+
9
+ @airbrake_project_setting.tracker = @project.trackers.where(id: params[:airbrake_project_setting][:tracker_id]).first
10
+ @airbrake_project_setting.category = @project.issue_categories.where(id: params[:airbrake_project_setting][:category_id]).first
11
+ @airbrake_project_setting.priority = IssuePriority.where(id: params[:airbrake_project_setting][:priority_id]).first
12
+ @airbrake_project_setting.reopen_regexp = params[:airbrake_project_setting][:reopen_regexp]
13
+
14
+ @airbrake_project_setting.save
15
+
16
+ redirect_to settings_project_path(@project, :tab => 'airbrake')
17
+ end
18
+
19
+ private
20
+
21
+ def find_project
22
+ @project = Project.find(params[:id])
23
+ end
24
+
25
+ end
@@ -0,0 +1,48 @@
1
+ module AirbrakeHelper
2
+
3
+ def format_table(data)
4
+ lines = []
5
+ data.each do |key, value|
6
+ lines << "|@#{key}@|#{value.strip.blank? ? value : "@#{value}@"}|"
7
+ end
8
+ lines.join("\n")
9
+ end
10
+
11
+ def format_list_item(name, value)
12
+ return '' if value.to_s.strip.blank?
13
+ "* *#{name}:* #{value}"
14
+ end
15
+
16
+ def format_backtrace(element)
17
+ @htmlentities ||= HTMLEntities.new
18
+
19
+ repository = repository_for_backtrace_element(element)
20
+
21
+ if repository.blank?
22
+ file = "@#{@htmlentities.decode(element[:file])}:#{element[:number]}@"
23
+ elsif repository.identifier.blank?
24
+ file = "source:\"#{@htmlentities.decode(element[:file][14..-1])}#L#{element[:number]}\""
25
+ else
26
+ file = "source:\"#{repository.identifier}|#{@htmlentities.decode(element[:file][14..-1])}#L#{element[:number]}\""
27
+ end
28
+
29
+ file + " in ??<notextile>#{@htmlentities.decode(element[:method])}</notextile>??"
30
+ end
31
+
32
+ private
33
+
34
+ def repository_for_backtrace_element(element)
35
+ if element[:file].start_with?('[PROJECT_ROOT]')
36
+ file = element[:file][14..-1]
37
+ if redmine_params.key?(:repository)
38
+ r = @project.repositories.where(identifier: (redmine_params[:repository] || '')).first
39
+ return r if r.present? && r.entry(file)
40
+ else
41
+ return @project.repositories.select{|r| r.entry(file)}.first
42
+ end
43
+ end
44
+
45
+ nil
46
+ end
47
+
48
+ end
@@ -0,0 +1,15 @@
1
+ class AirbrakeProjectSetting < ActiveRecord::Base
2
+ belongs_to :project
3
+ Project.has_one :airbrake_settings, class_name: AirbrakeProjectSetting.name, dependent: :destroy
4
+
5
+ belongs_to :tracker
6
+ Tracker.has_many :airbrake_project_settings, dependent: :nullify
7
+
8
+ belongs_to :category, class_name: IssueCategory.name
9
+ IssueCategory.has_many :airbrake_project_settings, dependent: :nullify
10
+
11
+ belongs_to :priority, class_name: IssuePriority.name
12
+ IssuePriority.has_many :airbrake_project_settings, dependent: :nullify
13
+
14
+ validates_presence_of :project_id
15
+ end
@@ -0,0 +1,38 @@
1
+ h1. <%= @notice[:error][:message] %>
2
+
3
+ h2. Stacktrace:
4
+
5
+ p((. <%=raw backtrace.collect{|e| format_backtrace(e)}.join("\n") %>
6
+
7
+ <% if @notice[:request].present? %>
8
+ h2. Request:
9
+
10
+ <%=format_list_item('URL', @notice[:request][:url]) %>
11
+ <%=format_list_item('Component', @notice[:request][:component]) %>
12
+ <%=format_list_item('Action', @notice[:request][:action]) %>
13
+
14
+ <% if @notice[:request][:params].present? %>
15
+ h2. Parameters:
16
+
17
+ <%=format_table(@notice[:request][:params]) %>
18
+ <% end %>
19
+
20
+ <% if @notice[:request][:cgi_data].present? %>
21
+ h2. Headers:
22
+
23
+ <%=format_table(@notice[:request][:cgi_data]) %>
24
+ <% end %>
25
+
26
+ <% if @notice[:request][:session].present? %>
27
+ h2. Session:
28
+
29
+ <%=format_table(@notice[:request][:session]) %>
30
+ <% end %>
31
+ <% end %>
32
+
33
+ <% if @notice[:server_environment].present? %>
34
+ h2. Environment
35
+
36
+ <%=format_list_item('Directory', @notice[:server_environment][:project_root]) %>
37
+ <%=format_list_item('Hostname', @notice[:server_environment][:hostname]) %>
38
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <% @airbrake_project_setting ||= (@project.airbrake_settings || AirbrakeProjectSetting.new(project: @project)) %>
2
+
3
+ <h2><%= l(:label_airbrake) %></h2>
4
+
5
+ <%= labelled_form_for :airbrake_project_setting, @airbrake_project_setting, url: airbrake_settings_project_path(@project), html: {method: :post, id: 'airbrake-form'} do |f| %>
6
+ <%= error_messages_for 'airbrake_project_setting' %>
7
+
8
+ <div class="box tabular">
9
+ <p>
10
+ <%=f.label :tracker_id %>
11
+ <%=f.select :tracker_id, options_from_collection_for_select(@project.trackers, :id, :name, @airbrake_project_setting.tracker_id), include_blank: true %>
12
+ </p>
13
+ <p>
14
+ <%=f.label :category_id %>
15
+ <%=f.select :category_id, options_from_collection_for_select(@project.issue_categories, :id, :name, @airbrake_project_setting.category_id), include_blank: true %>
16
+ </p>
17
+ <p>
18
+ <%=f.label :priority_id %>
19
+ <%=f.select :priority_id, options_from_collection_for_select(IssuePriority.order(:position), :id, :name, @airbrake_project_setting.priority_id), include_blank: true %>
20
+ </p>
21
+ <p>
22
+ <%=f.label :reopen_regexp %>
23
+ <%=f.text_field :reopen_regexp %>
24
+ <br />
25
+ (<%=l(:text_regexp_info)%>)
26
+ </p>
27
+ </div>
28
+
29
+ <p>
30
+ <%= submit_tag(l(:button_save)) %>
31
+ </p>
32
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <p>
2
+ <%=label_tag 'settings[hash_field]', l(:settings_airbrake_hash_field) %>
3
+ <%=select('settings[hash_field]', nil, IssueCustomField.where(field_format: 'string').order(:name).collect{|cf| [cf.name, cf.id]}, selected: @settings[:hash_field]) %>
4
+ </p>
5
+
6
+ <p>
7
+ <%=label_tag 'settings[occurrences_field]', l(:settings_airbrake_occurrences_field) %>
8
+ <%=select('settings[occurrences_field]', nil, IssueCustomField.where(field_format: 'int').order(:name).collect{|cf| [cf.name, cf.id]}, selected: @settings[:occurrences_field], include_blank: true) %>
9
+ </p>
data/bin/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/redmine_airbrake_backend/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'
@@ -0,0 +1,10 @@
1
+ en:
2
+ label_airbrake: Airbrake
3
+
4
+ field_reopen_regexp: Reopen regular expression
5
+
6
+ settings_airbrake_hash_field: Notice Hash Custom Field
7
+ settings_airbrake_occurrences_field: Occurrences Custom Field
8
+
9
+ project_module_airbrake: Airbrake
10
+ permission_manage_airbrake: Manage Airbrake
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Rails.application.routes.draw do
2
+ post '/notifier_api/v2/notices/' => 'airbrake#notice'
3
+
4
+ resources :projects do
5
+ member do
6
+ post 'airbrake_settings' => 'airbrake_project_settings#update'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ class CreateAirbrakeProjectSettings < ActiveRecord::Migration
2
+
3
+ def up
4
+ create_table :airbrake_project_settings do |t|
5
+ t.references :project, index: true, null: false
6
+ t.references :tracker
7
+ t.references :category
8
+ t.references :priority
9
+ t.string :reopen_regexp
10
+ t.timestamps
11
+ end
12
+ end
13
+
14
+ def down
15
+ drop_table :airbrake_project_settings
16
+ end
17
+
18
+ end
@@ -0,0 +1,4 @@
1
+ require 'redmine_airbrake_backend/engine'
2
+
3
+ module RedmineAirbrakeBackend
4
+ end
@@ -0,0 +1,28 @@
1
+ module RedmineAirbrakeBackend
2
+ class Engine < ::Rails::Engine
3
+ initializer 'redmine_airbrake_backend.register_redmine_plugin', after: :load_config_initializers do |app|
4
+ register_plugin
5
+
6
+ require_dependency 'airbrake_project_setting'
7
+ require_dependency 'redmine_airbrake_backend/project_helper_patch'
8
+ end
9
+
10
+ private
11
+
12
+ def register_plugin
13
+ Redmine::Plugin.register :redmine_airbrake_backend do
14
+ name 'Airbrake Backend'
15
+ author 'Florian Schwab'
16
+ description 'Airbrake Backend for Redmine'
17
+ version '0.0.1'
18
+ directory File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
19
+
20
+ project_module :airbrake do
21
+ permission :manage_airbrake, {airbrake: [:update]}
22
+ end
23
+
24
+ settings default: {hash_field: '', occurrences_field: ''}, partial: 'settings/airbrake'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ ::ProjectsHelper.module_eval %Q{
2
+ alias_method :project_settings_tabs_original_airbrake, :project_settings_tabs
3
+
4
+ def project_settings_tabs
5
+ tabs = project_settings_tabs_original_airbrake
6
+
7
+ tabs.push({
8
+ :name => 'airbrake',
9
+ :action => :manage_airbrake,
10
+ :partial => 'projects/settings/airbrake',
11
+ :label => :project_module_airbrake
12
+ })
13
+
14
+ tabs.select{|tab| User.current.allowed_to?(tab[:action], @project)}
15
+ end
16
+ }
@@ -0,0 +1,3 @@
1
+ module RedmineAirbrakeBackend
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redmine_airbrake_backend/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'redmine_airbrake_backend'
8
+ spec.version = RedmineAirbrakeBackend::VERSION
9
+ spec.authors = ['Florian Schwab']
10
+ spec.email = ['me@ydkn.de']
11
+ spec.description = %q{Plugin which adds Airbrake support to Redmine}
12
+ spec.summary = %q{This plugin provides the necessary API to use Redmine as a Airbrake backend}
13
+ spec.homepage = 'https://github.com/ydkn/redmine_airbrake_backend'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'rails'
22
+ spec.add_dependency 'hpricot'
23
+ spec.add_dependency 'htmlentities'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake'
27
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redmine_airbrake_backend
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Florian Schwab
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hpricot
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: htmlentities
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Plugin which adds Airbrake support to Redmine
84
+ email:
85
+ - me@ydkn.de
86
+ executables:
87
+ - rails
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - MIT-LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - app/controllers/airbrake_controller.rb
98
+ - app/controllers/airbrake_project_settings_controller.rb
99
+ - app/helpers/airbrake_helper.rb
100
+ - app/models/airbrake_project_setting.rb
101
+ - app/views/airbrake/_issue_description.erb
102
+ - app/views/projects/settings/_airbrake.html.erb
103
+ - app/views/settings/_airbrake.html.erb
104
+ - bin/rails
105
+ - config/locales/en.yml
106
+ - config/routes.rb
107
+ - db/migrate/20130708102357_create_airbrake_project_settings.rb
108
+ - lib/redmine_airbrake_backend.rb
109
+ - lib/redmine_airbrake_backend/engine.rb
110
+ - lib/redmine_airbrake_backend/project_helper_patch.rb
111
+ - lib/redmine_airbrake_backend/version.rb
112
+ - redmine_airbrake_backend.gemspec
113
+ homepage: https://github.com/ydkn/redmine_airbrake_backend
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.0.3
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: This plugin provides the necessary API to use Redmine as a Airbrake backend
137
+ test_files: []