oneis 1.0.0-java

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.
data/lib/manifest.rb ADDED
@@ -0,0 +1,41 @@
1
+
2
+ module PluginTool
3
+
4
+ ALLOWED_PLUGIN_DIRS = ['js', 'static', 'template', 'test', 'data']
5
+
6
+ def self.generate_manifest(directory)
7
+ manifest = Hash.new
8
+ Dir.glob("#{directory}/**/*").sort.each do |pathname|
9
+ # Ignore directories
10
+ next unless File.file? pathname
11
+ # Check file
12
+ filename = pathname.slice(directory.length + 1, pathname.length)
13
+ raise "Bad filename for #{filename}" unless filename =~ /\A([a-zA-Z0-9_\/\-]+\/)?([a-z0-9_-]+\.[a-z0-9]+)\Z/
14
+ dir = $1
15
+ name = $2
16
+ if dir != nil
17
+ dir = dir.gsub(/\/\Z/,'')
18
+ raise "Bad directory #{dir}" unless dir =~ /\A([a-zA-Z0-9_\-]+)[a-zA-Z0-9_\/\-]*\Z/
19
+ raise "Bad root directory #{$1}" unless ALLOWED_PLUGIN_DIRS.include?($1)
20
+ end
21
+ # Get hash of file
22
+ digest = File.open(pathname) { |f| Digest::SHA1.hexdigest(f.read) }
23
+ # And add to manifest
24
+ manifest[filename] = digest
25
+ end
26
+ manifest
27
+ end
28
+
29
+ def self.determine_manifest_changes(from, to)
30
+ changes = []
31
+ from.each_key do |name|
32
+ changes << [name, :delete] unless to.has_key?(name)
33
+ end
34
+ to.each do |name,hash|
35
+ changes << [name, hash] unless from[name] == hash
36
+ end
37
+ changes
38
+ end
39
+
40
+ end
41
+
data/lib/minimise.rb ADDED
@@ -0,0 +1,68 @@
1
+
2
+ module PluginTool
3
+
4
+ class Minimiser
5
+ Context = Java::OrgMozillaJavascript::Context
6
+
7
+ def initialize
8
+ # Load UglifyJS into a JavaScript interpreter
9
+ raise "Another JS Context is active" unless nil == Context.getCurrentContext()
10
+ @cx = Context.enter();
11
+ @cx.setLanguageVersion(Context::VERSION_1_7)
12
+ @javascript_scope = @cx.initStandardObjects()
13
+ ['js_min.js','uglifyjs/parse-js.js','uglifyjs/process.js','uglifyjs/squeeze-more.js'].each do |filename|
14
+ js = File.open("#{File.dirname(__FILE__)}/#{filename}") { |f| f.read }
15
+ @cx.evaluateString(@javascript_scope, js, "<#{filename}>", 1, nil);
16
+ end
17
+ @js_min = @javascript_scope.get("js_min", @javascript_scope);
18
+ end
19
+
20
+ def process(data, filename)
21
+ if filename =~ /\.js\Z/
22
+ # JavaScript - use UglifyJS loaded into the JavaScript interpreter
23
+ @js_min.call(@cx, @javascript_scope, @javascript_scope, [data])
24
+
25
+ elsif filename =~ /\.html\Z/
26
+ # Simple processing of HTML
27
+ # Remove HTML comments
28
+ html = data.gsub(/\<\!\-\-.+?\-\-\>/m,'')
29
+ # Remove indents
30
+ html.gsub!(/^\s+/,'')
31
+ # Remove any unnecessary line breaks (fairly conservative)
32
+ html.gsub!(/\>[\r\n]+\</m,'><')
33
+ html.gsub!(/([\>}])[\r\n]+([\<{])/m,'\1\2')
34
+ html
35
+
36
+ elsif filename =~ /\.css\Z/
37
+ # Simple processing of CSS
38
+ css = data.gsub(/(^|\s)\/\*.+?\*\/($|\s)/m,'') # remove C style comments
39
+ out = []
40
+ css.split(/[\r\n]+/).each do |line|
41
+ line.chomp!; line.gsub!(/^\s+/,''); line.gsub!(/\s+$/,'')
42
+ line.gsub!(/\s+/,' ') # contract spaces
43
+ line.gsub!(/\s*:\s*/,':') # remove unnecessary spaces
44
+ if line =~ /\S/
45
+ out << line
46
+ end
47
+ end
48
+ css = out.join("\n")
49
+ # Remove unnecessary line endings
50
+ css.gsub!(/[\r\n]*(\{[^\}]+\})/m) do |m|
51
+ $1.gsub(/[\r\n]/m,'')
52
+ end
53
+ css
54
+
55
+ else
56
+ # No processing
57
+ data
58
+ end
59
+ end
60
+
61
+ def finish
62
+ Context.exit()
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
data/lib/misc.rb ADDED
@@ -0,0 +1,8 @@
1
+
2
+ module PluginTool
3
+
4
+ def self.beep
5
+ $stdout.write("\07")
6
+ end
7
+
8
+ end
data/lib/new_plugin.rb ADDED
@@ -0,0 +1,91 @@
1
+
2
+ module PluginTool
3
+
4
+ def self.make_new_plugin(plugin_name)
5
+ unless plugin_name =~ /\A[a-z0-9_]+\Z/ && plugin_name.length > 8
6
+ end_on_error "Bad plugin name - must use a-z0-9_ only, and more than 8 characters."
7
+ end
8
+ if File.exist?(plugin_name)
9
+ end_on_error "File or directory #{plugin_name} already exists"
10
+ end
11
+ FileUtils.mkdir(plugin_name)
12
+ ['js', 'static', 'template', 'test'].each do |dir|
13
+ FileUtils.mkdir("#{plugin_name}/#{dir}")
14
+ end
15
+ random = java.security.SecureRandom.new()
16
+ rbytes = Java::byte[20].new
17
+ random.nextBytes(rbytes)
18
+ install_secret = String.from_java_bytes(rbytes).unpack('H*').join
19
+ File.open("#{plugin_name}/plugin.json",'w') do |file|
20
+ file.write(<<__E)
21
+ {
22
+ "pluginName": "#{plugin_name}",
23
+ "pluginAuthor": "TODO Your Company",
24
+ "pluginVersion": 1,
25
+ "displayName": "#{plugin_name.split('_').map {|e| e.capitalize} .join(' ')}",
26
+ "displayDescription": "TODO Longer description of plugin",
27
+ "installSecret": "#{install_secret}",
28
+ "apiVersion": 0,
29
+ "load": ["js/#{plugin_name}.js"],
30
+ "respond": ["/do/#{plugin_name}"]
31
+ }
32
+ __E
33
+ end
34
+ File.open("#{plugin_name}/js/#{plugin_name}.js",'w') do |file|
35
+ file.write(<<__E)
36
+
37
+ var #{plugin_name} = O.plugin("#{plugin_name}", {
38
+ });
39
+
40
+ (function() {
41
+
42
+ #{plugin_name}.respond("GET", "/do/#{plugin_name}/example", [
43
+ ], function(E) {
44
+ E.render({
45
+ pageTitle: "Example page"
46
+ });
47
+ });
48
+
49
+ })();
50
+
51
+ __E
52
+ end
53
+ File.open("#{plugin_name}/template/example.html",'w') do |file|
54
+ file.write(<<__E)
55
+ <p>This is an example template.</p>
56
+ __E
57
+ end
58
+ File.open("#{plugin_name}/test/#{plugin_name}_test1.js",'w') do |file|
59
+ file.write(<<__E)
60
+
61
+ T.test(function() {
62
+
63
+ // For documentation, see
64
+ // http://docs.oneis.co.uk/dev/plugin/tests
65
+
66
+ T.assert(true);
67
+
68
+ });
69
+
70
+ __E
71
+ end
72
+ puts <<__E
73
+
74
+ Plugin #{plugin_name} has been created. Run
75
+
76
+ oneis-plugin -p #{plugin_name}
77
+
78
+ to upload it to the server, then visit
79
+
80
+ https://<HOSTNAME>/do/#{plugin_name}/example
81
+
82
+ to see a sample page.
83
+
84
+ Add additional hook handlers in the js/#{plugin_name}.js file.
85
+
86
+ See http://docs.oneis.co.uk/dev/plugin for more information.
87
+
88
+ __E
89
+ end
90
+
91
+ end
@@ -0,0 +1,69 @@
1
+
2
+ module PluginTool
3
+
4
+ @@notification_announce_reconnect = false
5
+
6
+ def self.start_notifications
7
+ @@notification_queue_name = nil
8
+ Thread.new do
9
+ while true
10
+ begin
11
+ self.do_notifications
12
+ rescue => e
13
+ puts "NOTICE: Lost notification connection, will attempt to reconnect soon."
14
+ @@notification_announce_reconnect = true
15
+ sleep(5) # throttle errors
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.do_notifications
22
+ http = make_http_connection
23
+ puts "NOTICE: Notification connection established." if @@notification_announce_reconnect
24
+ while true
25
+ sleep(0.25) # small throttle of requests
26
+ path = '/api/development_plugin_loader/get_notifications'
27
+ path << "?queue=#{@@notification_queue_name}" if @@notification_queue_name
28
+ request = Net::HTTP::Get.new(path)
29
+ setup_request(request)
30
+ # Server uses long-polling, and will respond after a long timeout, or when a notification
31
+ # has been queued for sending to this process.
32
+ response = http.request(request)
33
+ if response.kind_of?(Net::HTTPOK)
34
+ @@notification_queue_name = response['X-Queue-Name']
35
+ begin
36
+ decode_and_handle_notifications response.body
37
+ rescue
38
+ puts "NOTICE: Error handling notification from server."
39
+ end
40
+ else
41
+ raise "Bad response"
42
+ end
43
+ end
44
+ end
45
+
46
+ def self.decode_and_handle_notifications(encoded)
47
+ size = encoded.length
48
+ pos = 0
49
+ while pos < (size - 12)
50
+ type = encoded[pos, 4]
51
+ data_size = encoded[pos + 4, 8].to_i(16)
52
+ data = encoded[pos + 12, data_size]
53
+ pos += 12 + data_size
54
+ handle_notification(type, data)
55
+ end
56
+ end
57
+
58
+ def self.handle_notification(type, data)
59
+ case type
60
+ when 'log '
61
+ # Output from console.log()
62
+ puts "LOG:#{data}"
63
+ else
64
+ puts "WARNING: Unknown notification received from server. Upgrade the plugin tool."
65
+ sleep(5) # throttle problematic responses
66
+ end
67
+ end
68
+
69
+ end
data/lib/packing.rb ADDED
@@ -0,0 +1,59 @@
1
+
2
+ module PluginTool
3
+
4
+ PACKING_ACCEPTABLE_FILENAME = /\A(plugin\.json|(js|static|template)\/[a-z0-9_-]+\.[a-z0-9]+)\Z/
5
+ PACKING_ACCEPTABLE_EXCEPTIONS = ['certificates-temp-http-api.pem']
6
+
7
+ def self.pack_plugin(plugin_name, output_directory)
8
+ # Get filenames and sort
9
+ files = Dir.glob("#{plugin_name}/**/*").map do |filename|
10
+ if File.file? filename
11
+ filename[plugin_name.length+1, filename.length]
12
+ else
13
+ nil
14
+ end
15
+ end .compact.sort
16
+ # Check each filename is acceptable
17
+ files.each do |filename|
18
+ unless filename =~ PACKING_ACCEPTABLE_FILENAME || PACKING_ACCEPTABLE_EXCEPTIONS.include?(filename)
19
+ puts "File '#{filename}' has an unacceptable filename"
20
+ exit 1
21
+ end
22
+ end
23
+ # Clean output directory
24
+ output_plugin_dir = "#{output_directory}/#{plugin_name}"
25
+ puts "Output directory: #{output_plugin_dir}"
26
+ if File.exist? output_plugin_dir
27
+ puts "Removing old output directory #{output_plugin_dir}"
28
+ FileUtils.rm_r(output_plugin_dir)
29
+ end
30
+ # Make file structure
31
+ FileUtils.mkdir(output_plugin_dir)
32
+ ['js','static','template'].each do |subdir|
33
+ FileUtils.mkdir("#{output_plugin_dir}/#{subdir}")
34
+ end
35
+ # Process each file, building a manifest
36
+ puts "Processing files:"
37
+ manifest = ''
38
+ minimiser = PluginTool::Minimiser.new
39
+ files.each do |filename|
40
+ puts " #{filename}"
41
+ data = File.open("#{plugin_name}/#{filename}") { |f| f.read }
42
+ # Minimise file?
43
+ unless filename =~ /\Ajs\//
44
+ data = minimiser.process(data, filename)
45
+ end
46
+ hash = Digest::SHA1.hexdigest(data)
47
+ File.open("#{output_plugin_dir}/#{filename}", "w") { |f| f.write data }
48
+ manifest << "F #{hash} #{filename}\n"
49
+ end
50
+ minimiser.finish
51
+ # Write manifest and version
52
+ File.open("#{output_plugin_dir}/manifest", "w") { |f| f.write manifest }
53
+ version = Digest::SHA1.hexdigest(manifest)
54
+ File.open("#{output_plugin_dir}/version", "w") { |f| f.write "#{version}\n" }
55
+ # All done
56
+ puts "Version: #{version}\nPlugin packed."
57
+ end
58
+
59
+ end
data/lib/plugin.rb ADDED
@@ -0,0 +1,207 @@
1
+
2
+ module PluginTool
3
+
4
+ class Plugin
5
+ def initialize(name, options)
6
+ @name = name
7
+ @options = options
8
+ end
9
+ attr_accessor :name
10
+ attr_accessor :plugin_dir
11
+
12
+ # ---------------------------------------------------------------------------------------------------------
13
+
14
+ def start
15
+ # Check to see if the plugin is valid
16
+ unless File.file?("#{@name}/plugin.json")
17
+ end_on_error "Plugin #{@name} does not exist (no plugin.json file)"
18
+ end
19
+ # Setup for using the plugin
20
+ @plugin_dir = @name
21
+ end_on_error "logic error" unless @plugin_dir =~ /\A[a-zA-Z0-9_-]+\Z/
22
+ @registration_file = "registration.#{@plugin_dir}.yaml"
23
+ puts "Plugin: #{@plugin_dir}"
24
+ end
25
+
26
+ def setup_for_server
27
+ # Make the first empty manifest (may be replaced from server)
28
+ @current_manifest = {}
29
+
30
+ # Get info about the current plugin
31
+ @loaded_plugin_id = nil
32
+ if File.exists? @registration_file
33
+ reg_info = YAML::load(File.open(@registration_file) { |f| f.read })
34
+ @loaded_plugin_id = reg_info[:plugin_id]
35
+ if @loaded_plugin_id != nil
36
+ # Check with server to see if this is still registered, and if so, what files it has on the server
37
+ s_reg_info = PluginTool.get_yaml("/api/development_plugin_loader/manifest/#{@loaded_plugin_id}")
38
+ end_on_error "Couldn't communicate successfully with server." if s_reg_info[:protocol_error]
39
+ if s_reg_info[:result] == 'success'
40
+ @current_manifest = s_reg_info[:manifest]
41
+ else
42
+ puts "NOTICE: Discarding registration information from #{@registration_file}"
43
+ @loaded_plugin_id = nil
44
+ end
45
+ end
46
+ end
47
+
48
+ # If minimisation is active, clear the current manifest so all files are uploaded again.
49
+ if @options.minimiser != nil
50
+ @current_manifest = {}
51
+ end
52
+
53
+ # Flag for registration file save
54
+ registration_file_needs_update = false
55
+
56
+ # If the current plugin registration isn't known, see if the server knows about it
57
+ if @loaded_plugin_id == nil
58
+ s_found_info = PluginTool.post_yaml("/api/development_plugin_loader/find_registration", {:name => @name})
59
+ if s_found_info[:found]
60
+ # Store info returned by the server
61
+ puts "NOTICE: Found existing registration for #{@name} on server. Using it."
62
+ @loaded_plugin_id = s_found_info[:plugin_id]
63
+ @current_manifest = s_found_info[:manifest]
64
+ registration_file_needs_update = true
65
+ end
66
+ end
67
+
68
+ # If there isn't an existing plugin registered, create a new one
69
+ if @loaded_plugin_id == nil
70
+ s_create_info = PluginTool.post_yaml("/api/development_plugin_loader/create")
71
+ end_on_error "Couldn't communicate successfully with server." if s_create_info[:protocol_error]
72
+ end_on_error "Failed to create plugin on server" unless s_create_info[:plugin_id] != nil
73
+ @loaded_plugin_id = s_create_info[:plugin_id]
74
+ registration_file_needs_update = true
75
+ end
76
+
77
+ # Update registration file for next run?
78
+ if registration_file_needs_update
79
+ File.open(@registration_file, 'w') { |f| f.write YAML::dump(:plugin_id => @loaded_plugin_id) }
80
+ end
81
+ end
82
+
83
+ # ---------------------------------------------------------------------------------------------------------
84
+
85
+ def command(cmd)
86
+ case cmd
87
+ when 'license-key'
88
+ application_id = @options.args.first
89
+ if application_id == nil || application_id !~ /\A\d+\Z/
90
+ end_on_error "Numeric application ID must be specified"
91
+ end
92
+ generate_license_key(application_id)
93
+
94
+ when 'pack'
95
+ PluginTool.pack_plugin(@name, @options.output)
96
+
97
+ when 'reset-db'
98
+ puts "Resetting database on server for #{@name}..."
99
+ reset_result = PluginTool.post_yaml("/api/development_plugin_loader/resetdb/#{@loaded_plugin_id}")
100
+ end_on_error "Couldn't remove old database tables" unless reset_result[:result] == 'success'
101
+ apply_result = PluginTool.get_yaml("/api/development_plugin_loader/apply/#{@loaded_plugin_id}")
102
+ end_on_error "Couldn't apply changes" unless apply_result[:result] == 'success'
103
+ puts "Done."
104
+
105
+ when 'uninstall'
106
+ puts "Uninstalling plugin #{@name} from server..."
107
+ reset_result = PluginTool.post_yaml("/api/development_plugin_loader/uninstall/#{@loaded_plugin_id}")
108
+ end_on_error "Couldn't uninstall plugin" unless reset_result[:result] == 'success'
109
+ puts "Done."
110
+
111
+ when 'test'
112
+ puts "Running tests..."
113
+ params = {}
114
+ params[:test] = @options.args.first unless @options.args.empty?
115
+ test_result = PluginTool.post_yaml("/api/development_plugin_loader/run_tests/#{@loaded_plugin_id}", params)
116
+ end_on_error "Couldn't run tests" unless test_result[:result] == 'success'
117
+ puts
118
+ puts test_result[:output] || ''
119
+ puts test_result[:summary] || "(unknown results)"
120
+
121
+ when 'develop'
122
+ # do nothing here
123
+
124
+ else
125
+ end_on_error "Unknown command '#{cmd}'"
126
+
127
+ end
128
+ end
129
+
130
+ # ---------------------------------------------------------------------------------------------------------
131
+
132
+ def develop_setup
133
+ end
134
+
135
+ def develop_scan_and_upload(first_run)
136
+ next_manifest = PluginTool.generate_manifest(@plugin_dir)
137
+ changes = PluginTool.determine_manifest_changes(@current_manifest, next_manifest)
138
+ upload_failed = false
139
+ changes.each do |filename, action|
140
+ filename =~ /\A(.*?\/)?([^\/]+)\Z/
141
+ params = {:filename => $2}
142
+ params[:directory] = $1.gsub(/\/\Z/,'') if $1
143
+ if action == :delete
144
+ puts " #{@name}: Deleting #{filename}"
145
+ PluginTool.post_yaml("/api/development_plugin_loader/delete_file/#{@loaded_plugin_id}", params)
146
+ else
147
+ puts " #{@name}: Uploading #{filename}"
148
+ data = File.open("#{@plugin_dir}/#{filename}") { |f| f.read }
149
+ hash = action
150
+ # Minimise file before uploading?
151
+ if @options.minimiser != nil && filename =~ /\A(static|template)\//
152
+ size_before = data.length
153
+ data = @options.minimiser.process(data, filename)
154
+ size_after = data.length
155
+ hash = Digest::SHA1.hexdigest(data)
156
+ puts " minimisation: #{size_before} -> #{size_after} (#{(size_after * 100) / size_before}%)"
157
+ end
158
+ r = PluginTool.post_yaml("/api/development_plugin_loader/put_file/#{@loaded_plugin_id}", params, {:file => [filename, data]})
159
+ if r[:result] == 'success'
160
+ # If the file was uploaded successfully, but the hash didn't match, abort now
161
+ end_on_error "#{@name}: Disagreed with server about uploaded file hash: local=#{hash}, remote=#{r[:hash]}" unless hash == r[:hash]
162
+ else
163
+ # Otherwise mark as a failed upload to stop an apply operation which will fail
164
+ upload_failed = true
165
+ end
166
+ PluginTool.syntax_check(self, filename) if filename =~ /\.js\Z/
167
+ end
168
+ end
169
+ if upload_failed
170
+ puts "\n#{@name}: Not applying changes due to failure\n\n"
171
+ else
172
+ if !(changes.empty?) || first_run
173
+ puts " #{@name}: Applying changes on server"
174
+ r = PluginTool.get_yaml("/api/development_plugin_loader/apply/#{@loaded_plugin_id}")
175
+ unless r[:result] == 'success'
176
+ puts "\n\n#{@name}: Didn't apply changes on server\n\n"
177
+ PluginTool.beep
178
+ end
179
+ end
180
+ end
181
+ PluginTool.finish_with_connection
182
+ @current_manifest = next_manifest
183
+ end
184
+
185
+ # ---------------------------------------------------------------------------------------------------------
186
+
187
+ def generate_license_key(application_id)
188
+ info = File.open("#{@plugin_dir}/plugin.json") { |f| JSON.parse(f.read) }
189
+ if info["installSecret"] == nil
190
+ end_on_error "#{@name}: No installSecret specified in plugin.json"
191
+ end
192
+ license_key = HMAC::SHA1.sign(info["installSecret"], "application:#{ARGV[1]}")
193
+ puts <<__E
194
+
195
+ Plugin: #{@name}
196
+ Application: #{application_id}
197
+ License key: #{license_key}
198
+ __E
199
+ end
200
+
201
+ def end_on_error(err)
202
+ puts err
203
+ exit 1
204
+ end
205
+ end
206
+
207
+ end