aizuchi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *~
6
+ .#*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in aizuchi.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Phil Hofmann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ Aizuchi - Instant Feedback Tool
2
+ ===============================
3
+
4
+ [Aizuchi](http://en.wikipedia.org/wiki/Aizuchi) is a
5
+ [Rack](http://rack.rubyforge.org/) based middleware which
6
+ automatically integrates with [Rails](http://rubyonrails.org/) to
7
+ display a form to collect feedback from your clients. Aizuchi
8
+ therefore creates issues via [Redmine's](http://www.redmine.org/) REST
9
+ API.
10
+
11
+ Install in Rails
12
+ ----------------
13
+
14
+ Put the following in your Gemfile and run `bundle install` or let
15
+ [Guard](https://github.com/guard/guard-bundler) kick in.
16
+
17
+ gem 'aizuchi'
18
+
19
+ Using Aizuchi outside of Rails
20
+ ------------------------------
21
+
22
+ require 'aizuchi/middleware'
23
+ use Aizuchi::Middleware, :config => 'path/to/config/aizuchi.yml'
24
+
25
+ Configure
26
+ ---------
27
+
28
+ Aizuchi will create a sample config file in `config/aizuchi.yml`.
29
+ Aizuchi is disabled by default, but you'll have to edit this config file
30
+ anyways.
31
+
32
+ Patches and the like
33
+ --------------------
34
+
35
+ If you run into bugs, have suggestions, patches or want to use Aizuchi
36
+ with something else than Rails or Redmine feel free to drop me a line.
37
+
38
+ License
39
+ -------
40
+
41
+ Aizuchi is release under MIT License, see LICENSE.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,20 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "aizuchi/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "aizuchi"
6
+ s.version = Aizuchi::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Phil Hofmann"]
9
+ s.email = ["phil@branch14.org"]
10
+ s.homepage = "http://branch14.org/aizuchi"
11
+ s.summary = %q{Collect instant feedback into Redmine}
12
+ s.description = %q{Collect instant feedback into Redmine}
13
+
14
+ # s.rubyforge_project = "aizuchi"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib", "rails"]
20
+ end
@@ -0,0 +1,2 @@
1
+ require "aizuchi/railtie" if defined? Rails
2
+
@@ -0,0 +1,282 @@
1
+ # (c) 2011 Phil Hofmann <pho@panter.ch>
2
+ #
3
+ # after install see config/aizuchi.yml for more information
4
+
5
+ require 'net/https'
6
+
7
+ module Aizuchi
8
+ class Middleware < Struct.new :app, :options
9
+
10
+ def call(env)
11
+ return app.call(env) unless config['enabled']
12
+ if env["REQUEST_METHOD"] == "POST" and
13
+ env["PATH_INFO"] =~ /^\/aizuchi/
14
+ # proxy
15
+ request = Rack::Request.new env
16
+ res = post(config['target'], request.POST.to_json,
17
+ config['user'], config['password'])
18
+ case res
19
+ when Net::HTTPSuccess
20
+ [ 200, {}, [] ]
21
+ else
22
+ [ 500, {}, [] ]
23
+ end
24
+ else
25
+ # inject
26
+ status, headers, response = app.call(env)
27
+ if headers["Content-Type"] =~ /text\/html|application\/xhtml\+xml/
28
+ body = ""
29
+ response.each { |part| body << part }
30
+ index = body.rindex "</body>"
31
+ if index
32
+ body.insert index, data
33
+ headers["Content-Length"] = body.length.to_s
34
+ response = [ body ]
35
+ end
36
+ end
37
+ [ status, headers, response ]
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def default_config
44
+ <<-EOB
45
+ ---
46
+ # Aizuchi -- Instant Feedback
47
+ # Aizuchi depends on jQuery >= 1.4.4
48
+ # upon changing this file you will need to restart your app
49
+ enabled: false
50
+ target: https://your.redmine.instance/issues.xml
51
+ user: feedback
52
+ password: secret
53
+ init:
54
+ params:
55
+ project_id: project-handle
56
+ assigned_to_id: 123
57
+ tracker_id: 4
58
+ text:
59
+ imperative: please type your feedback here
60
+ submit: submit
61
+ cancel: cancel
62
+ css:
63
+ hidden:
64
+ top: 150px
65
+ left: -600px
66
+ height: 60px
67
+ visible:
68
+ top: 150px
69
+ left: 0px
70
+ height: 200px
71
+ # uncomment the following two lines, if your project
72
+ # doesn't use jquery already
73
+ # javascripts:
74
+ # - http://code.jquery.com/jquery-1.5.1.min.js
75
+ # or this line if you want to ship it yourself
76
+ # - /javascripts/jquery-1.5.1.min.js
77
+ # uncomment the following line, in case your project uses prototype
78
+ # jquery_noconflict: true
79
+ EOB
80
+ end
81
+
82
+ def config
83
+ return @config unless @config.nil?
84
+ path = options[:config]
85
+ raise "no config path given" unless path
86
+ ::File.open(path, 'w') { |f| f.puts default_config } unless ::File.exist?(path)
87
+ @config = ::File.open(path) { |yf| YAML::load(yf) }
88
+ end
89
+
90
+ def post(uri_str, body_str, user=nil , pass=nil)
91
+ url = URI.parse uri_str
92
+ http = Net::HTTP.new url.host, url.port
93
+ http.use_ssl = (url.scheme == 'https')
94
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
95
+ request = Net::HTTP::Post.new url.path
96
+ request["Content-Type"] = "application/json"
97
+ request.basic_auth user, pass
98
+ request.body = body_str
99
+ response = http.start { |http| http.request(request) }
100
+ case response
101
+ when Net::HTTPSuccess then response
102
+ else
103
+ response.error!
104
+ end
105
+ end
106
+
107
+ def data # DATA doesn't work with Rack
108
+ template = ::File.read(__FILE__).gsub(/.*__END__\n/m, '')
109
+ (config['javascripts'] || []).map do |j|
110
+ "<script type='text/javascript' src='%s'></script>" % j
111
+ end * "\n" + ERB.new(template).result(binding)
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ __END__
118
+ <script type="text/javascript">
119
+ (function( $ ){
120
+
121
+ <% if config['jquery_noconflict'] %>
122
+ $.noConflict();
123
+ <% end %>
124
+
125
+ $(function() { Aizuchi.init(<%= config['init'].to_json %>); });
126
+
127
+ Aizuchi = {
128
+ settings: {},
129
+ state: "visible",
130
+
131
+ // returns the root url of the application
132
+ root_url: function() {
133
+ var url = window.location.protocol + "//" + window.location.hostname;
134
+ if(window.location.port != "") { url += ":" + window.location.port; }
135
+ return url;
136
+ },
137
+
138
+ // build a string, which is posted as the description of the issue
139
+ description: function() {
140
+ var desc = "h1. User Feedback\n\n"
141
+ + $('#aizuchi textarea').val() + "\n\n";
142
+ // meta data
143
+ var referer = document.referrer;
144
+ if(referer == "") { referer = "(no referer)"; }
145
+ desc += "<pre>"
146
+ + "sent from: " + document.title + "\n"
147
+ + "URL: " + document.URL + "\n"
148
+ + "referer: " + referer + "\n"
149
+ + "user agent: " + navigator.userAgent + "\n"
150
+ + "screen size: " + screen.width + "x" + screen.height
151
+ + ", color depth: " + screen.colorDepth + "\n";
152
+ desc += "</pre>\n";
153
+ desc += "Installed plugins:\n\n";
154
+ // plugins & mimetypes
155
+ for(var pid=0;pid<navigator.plugins.length;pid++) {
156
+ var plugin = navigator.plugins[pid];
157
+ desc += "* " + plugin.name + "(" + plugin.description + ")\n";
158
+ for(var mid=0;mid<plugin.length;mid++) {
159
+ var mime = plugin[mid];
160
+ desc += "** " + mime.type + " (" + mime.description + ")\n";
161
+ }
162
+ }
163
+ return desc;
164
+ },
165
+
166
+ subject: function() {
167
+ return $('#aizuchi textarea').val().substring(0, 50) + '...';
168
+ },
169
+
170
+ markup: function() {
171
+ return "<div id='aizuchi' onmouseover='Aizuchi.show()'>"
172
+ + "<form>"
173
+ + "<textarea>" + this.settings.text.imperative + "</textarea>"
174
+ + "<div class='buttons'>"
175
+ + "<input type='button' value='" + this.settings.text.submit
176
+ + "' onclick='Aizuchi.send()' />"
177
+ + "<input type='button' value='" + this.settings.text.cancel
178
+ + "' onclick='Aizuchi.hide()' />"
179
+ + "</div>"
180
+ + "<form>"
181
+ + "</div>";
182
+ },
183
+
184
+ resizeHack: function() {
185
+ $('#aizuchi textarea').height($('#aizuchi').height() - 35);
186
+ },
187
+
188
+ show: function() {
189
+ if(this.state == 'visible') { return; }
190
+ $('#aizuchi').animate(this.settings.css.visible, 150, 'swing', function() {
191
+ Aizuchi.resizeHack();
192
+ // $('#aizuchi textarea').focus(function() { this.select(); });
193
+ // $('#aizuchi textarea').first().focus();
194
+ Aizuchi.state = 'visible';
195
+ });
196
+ },
197
+
198
+ hide: function() {
199
+ $('#aizuchi').animate(this.settings.css.hidden, 150, 'linear', function() {
200
+ Aizuchi.resizeHack();
201
+ Aizuchi.state = 'hidden';
202
+ });
203
+ // setTimeout(function() { Aizuchi.state = 'hidden'; }, 500);
204
+ },
205
+
206
+ init: function(hash) {
207
+ var dyn = {
208
+ params: {
209
+ target_url: this.root_url() + "/aizuchi",
210
+ subject: this.subject,
211
+ description: this.description
212
+ }
213
+ };
214
+ $.extend(true, this.settings, dyn, hash);
215
+ $('body').append(this.markup());
216
+ this.hide();
217
+ // setTimeout(function() { $('#aizuchi').css('display', 'block'); }, 1000);
218
+ setTimeout(function() {
219
+ $('#aizuchi').animate({opacity: 1});
220
+ Aizuchi.state = 'hidden';
221
+ }, 1000);
222
+ },
223
+
224
+ responseHandler: function(data, status, request) {},
225
+
226
+ send: function() {
227
+ this.sendToRedmine();
228
+ this.hide();
229
+ $('#aizuchi textarea').val(this.settings.text.imperative);
230
+ },
231
+
232
+ sendToRedmine: function(hash) {
233
+ if(typeof hash == undefined) { hash = {}; }
234
+ var data = $.extend({}, this.settings.params, hash);
235
+ var url = data.target_url;
236
+ $.post(url, {"issue": data}, this.responseHandler);
237
+ },
238
+
239
+ log: function(msg) {
240
+ if(typeof console == undefined) { return; }
241
+ console.log(msg);
242
+ }
243
+ };
244
+ })( jQuery );
245
+ </script>
246
+ <style>
247
+ #aizuchi {
248
+ width: 600px;
249
+ height: 200px;
250
+ padding: 10px;
251
+ -moz-border-radius: 0 15px 15px 0;
252
+ border-radius: 0 15px 15px 0;
253
+ position: absolute;
254
+ background-color: #00bcff;
255
+ z-index: 999;
256
+ /*display: none;*/
257
+ opacity: 0;
258
+ }
259
+
260
+ #aizuchi textarea {
261
+ background-color: #c0c0c0;
262
+ width: 580px;
263
+ height: 100%;
264
+ border: 0px;
265
+ padding: 8px;
266
+ font-size: 18px;
267
+ -moz-border-radius: 10px;
268
+ border-radius: 10px;
269
+ }
270
+
271
+ #aizuchi .buttons {
272
+ float: right;
273
+ }
274
+
275
+ #aizuchi input {
276
+ margin: 0 5px 0 0;
277
+ }
278
+
279
+ #aizuchi img {
280
+ border: none;
281
+ }
282
+ </style>
@@ -0,0 +1,11 @@
1
+ require 'aizuchi/middleware'
2
+
3
+ module Aizuchi
4
+ class Railtie < Rails::Railtie
5
+ initializer "aizuchi.insert_middleware" do |app|
6
+ app.config.middleware.use "Aizuchi::Middleware",
7
+ :config => File.expand_path('config/aizuchi.yml', Rails.root)
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,3 @@
1
+ module Aizuchi
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aizuchi
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Phil Hofmann
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-28 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Collect instant feedback into Redmine
23
+ email:
24
+ - phil@branch14.org
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - aizuchi.gemspec
38
+ - lib/aizuchi.rb
39
+ - lib/aizuchi/middleware.rb
40
+ - lib/aizuchi/railtie.rb
41
+ - lib/aizuchi/version.rb
42
+ has_rdoc: true
43
+ homepage: http://branch14.org/aizuchi
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ - rails
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.4.1
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Collect instant feedback into Redmine
77
+ test_files: []
78
+