redmine_airbrake_backend 0.1.0

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