rbbt-views 1.0.0 → 1.0.1

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.
@@ -12,6 +12,7 @@ require 'rbbt/workflow/rest/render.rb'
12
12
  require 'rbbt/workflow/rest/auth.rb'
13
13
  require 'rbbt/workflow/rest/email.rb'
14
14
  require 'rbbt/workflow/rest/helpers.rb'
15
+ require 'rbbt/workflow/rest/entity.rb'
15
16
 
16
17
  module WorkflowREST
17
18
 
@@ -29,9 +30,18 @@ module WorkflowREST
29
30
 
30
31
  Sinatra.module_eval do
31
32
  helpers Sinatra::SinatraRbbt
32
- enable :sessions
33
33
  enable :static
34
34
 
35
+ # This would make random secrets that would make the server hang after reload by old cookies.
36
+ # enable :sessions
37
+ # Util fix...
38
+ use Rack::Session::Cookie, :key => 'rack.session',
39
+ :path => '/',
40
+ :expire_after => 2592000,
41
+ :secret => 'rbbt_default_secret. Please CHANGE!'
42
+
43
+ set :cache_dir, "cache"
44
+
35
45
  before do
36
46
  @file_cache = {}
37
47
  @result_cache = {}
@@ -53,6 +63,7 @@ module WorkflowREST
53
63
  request.env['REMOTE_USER'] == user || throw(:halt, [ 401, 'Authorization Required' ])
54
64
  end
55
65
  end
66
+
56
67
  end
57
68
 
58
69
  set :public_folder, RbbtHTMLHelpers.locate_file(nil, nil, 'public/').find
@@ -69,6 +80,8 @@ module WorkflowREST
69
80
 
70
81
  if production?
71
82
  before do
83
+ params.delete "_"
84
+ params.delete :_
72
85
  cache_control :public, :must_revalidate, :max_age => 60
73
86
  end
74
87
  Log.severity = 0
@@ -78,7 +91,7 @@ module WorkflowREST
78
91
  file = locate_file(WorkflowREST.workflows.last, nil, File.join("compass", params[:name] + '.sass'))
79
92
 
80
93
  content_type 'text/css', :charset => 'utf-8'
81
- cache('css', params) do
94
+ cache('css', params.merge(:cache_type => :sync)) do
82
95
  renderer = Sass::Engine.new(file.read)
83
96
  renderer.render
84
97
  end
@@ -93,27 +106,155 @@ module WorkflowREST
93
106
  type = params[:type]
94
107
 
95
108
  visualization_parameters = get_visualization_parameters(params)
109
+
110
+ cache_type = params.delete(:_cache_type) || params.delete("_cache_type") || :sync
111
+
96
112
  cache("Entity:", :params => params, :visualization_params => visualization_parameters, :user => user) do
97
113
  entity_render(type, params.merge(visualization_parameters))
98
114
  end
99
115
  end
100
116
 
117
+ #{{{ entity_list
118
+
119
+ get '/entity_list/:user' do
120
+ list_user = params.delete(:user) || params.delete("user")
121
+
122
+ list_user = user if list_user.to_s == "user"
123
+
124
+ if list_user != "public"
125
+ raise "Not allowed to view entity lists for: #{ user }" unless authorized? and user.to_s == list_user
126
+ end
127
+
128
+ lists = Entity::REST.list_files(list_user)
129
+ workflow_render('user_lists', params.merge(:lists => lists))
130
+ end
131
+
101
132
  get '/entity_list/:type/:id' do
102
133
  type = params.delete "type"
103
134
  visualization_parameters = get_visualization_parameters(params)
104
135
 
105
- cache("Entity List:", :params => params, :type => type, :visualization_params => visualization_parameters, :user => user) do
106
- entity_list_render(type, params.merge(visualization_parameters))
136
+ cache_type = params.delete(:_cache_type) || params.delete("_cache_type") || :sync
137
+ update = params.delete(:_update) || params.delete("_update") || nil
138
+
139
+ check = Entity::REST.list_file(type, params[:id], user)
140
+ check = Entity::REST.list_file(type, params[:id]) unless not check.nil? and File.exists? check
141
+
142
+ raise "List of type #{ type } not found for #{params[:id]}: #{ check.inspect }" if check.nil? or not File.exists?(check)
143
+
144
+ cache("Entity List:", :check => [check], :update => update, :cache_type => cache_type, :params => params, :type => type, :visualization_params => visualization_parameters, :user => user) do
145
+ if visualization_parameters[:_format] == 'json'
146
+ list = Entity::REST.load_list(type, params[:id], user)
147
+ list.collect{|elem| [elem.clean_annotations, elem.info]}.to_json
148
+ else
149
+ entity_list_render(type, params.merge(visualization_parameters))
150
+ end
107
151
  end
108
152
  end
109
153
 
154
+ post '/entity_list/:type/copy/:id' do
155
+ type = params.delete "type"
156
+
157
+ list = Entity::REST.load_list(type, params[:id], user)
158
+ Entity::REST.save_list(type, params[:new_id], list, user)
159
+
160
+ redirect "/entity_list/#{ type }/#{params[:new_id]}"
161
+ end
162
+
163
+ post '/entity_list/:type/add/:id' do
164
+ type = params.delete "type"
165
+
166
+ list = Entity::REST.load_list(type, params[:id], user)
167
+ other_list = Entity::REST.load_list(type, params[:other_list], user)
168
+ list.concat other_list
169
+ Entity::REST.save_list(type, params[:id], list, user)
170
+
171
+ redirect back
172
+ end
173
+
174
+ post '/entity_list/:type/remove/:id' do
175
+ type = params.delete "type"
176
+
177
+ list = Entity::REST.load_list(type, params[:id], user)
178
+ other_list = Entity::REST.load_list(type, params[:other_list], user)
179
+ Entity::REST.save_list(type, params[:id], list.remove(other_list), user)
180
+
181
+ redirect back
182
+ end
183
+
184
+ post '/entity_list/:type/subset/:id' do
185
+ type = params.delete "type"
186
+
187
+ list = Entity::REST.load_list(type, params[:id], user)
188
+ other_list = Entity::REST.load_list(type, params[:other_list], user)
189
+ Entity::REST.save_list(type, params[:id], list.subset(other_list), user)
190
+
191
+ redirect back
192
+ end
193
+
194
+ get '/entity_list/:type/edit/:id' do
195
+ type = params[:type]
196
+ visualization_parameters = get_visualization_parameters(params)
197
+
198
+ workflow = WorkflowREST.workflows.last
199
+ id = params[:id]
200
+ list = Entity::REST.load_list(type, id, user)
201
+ workflow_render('edit_list', workflow, params.merge(:list => list))
202
+ end
203
+
204
+ delete '/entity_list/:type/:id' do
205
+ Entity::REST.delete_list(params[:type], params[:id], user)
206
+ redirect '/'
207
+ end
208
+
209
+ # entities
210
+ delete '/entity_list/:type/:entity/:id' do
211
+ id = params[:id]
212
+ Log.debug "Deleting #{params[:entity] } from #{params[:id]}"
213
+
214
+ list = Entity::REST.load_list(params[:type], id, user)
215
+
216
+ list.delete params[:entity]
217
+
218
+ Entity::REST.save_list(params[:type], id, list, user)
219
+
220
+ redirect back
221
+ end
222
+
223
+ post '/entity_list/:type/:entity/:id' do
224
+ id = params.delete(:id) || params.delete("id")
225
+ entity = params.delete(:entity) || params.delete("entity")
226
+ type = params.delete(:type) || params.delete("type")
227
+
228
+ info = {}
229
+ params.each{|k,v| info[k] = begin JSON.parse(v) rescue v end}
230
+ entity = Annotated.load_tsv_values(entity, [[info.to_json]], "JSON")
231
+
232
+ list = Entity::REST.load_list(type, id, user)
233
+
234
+
235
+ different = entity.info.select{|k,v| not v.nil? and v != list_info[k]}.any?
236
+
237
+
238
+ new = list.collect{|e| e} if different
239
+ new.delete entity
240
+ new << entity
241
+ Entity::REST.save_list(type, id, new, user)
242
+
243
+ redirect back
244
+ end
245
+
246
+ #{{{ entity_action
247
+
110
248
  get '/entity_action/:type/:action/:entity' do
111
249
  type = params.delete "type"
112
250
  action = params.delete "action"
113
251
 
114
252
  visualization_parameters = get_visualization_parameters(params)
115
253
 
116
- cache("Entity Action [#{action}]:", :params => params, :type => type, :action => action, :visualization_params => visualization_parameters, :user => user) do
254
+ cache_type = params.delete(:_cache_type) || params.delete("_cache_type") || :async
255
+ update = params.delete(:_update) || params.delete("_update") || nil
256
+
257
+ cache("Entity Action [#{action}][#{ params[:entity] }]", :update => update, :cache_type => cache_type, :params => params, :type => type, :action => action, :visualization_params => visualization_parameters, :user => user) do
117
258
  entity_action_render(type, action, params.merge(visualization_parameters))
118
259
  end
119
260
  end
@@ -124,7 +265,10 @@ module WorkflowREST
124
265
 
125
266
  visualization_parameters = get_visualization_parameters(params)
126
267
 
127
- cache("Entity List Action [#{action}]:", :params => params, :type => type, :action => action, :visualization_params => visualization_parameters, :user => user) do
268
+ cache_type = params.delete(:_cache_type) || params.delete("_cache_type") || :async
269
+ update = params.delete(:_update) || params.delete("_update") || nil
270
+
271
+ cache("Entity List Action [#{action}] [#{ params[:id]}]", :update => update, :cache_type => cache_type, :params => params, :type => type, :action => action, :visualization_params => visualization_parameters, :user => user) do
128
272
  entity_list_action_render(type, action, params.merge(visualization_parameters))
129
273
  end
130
274
  end
@@ -518,5 +662,4 @@ module WorkflowREST
518
662
  end
519
663
 
520
664
  end
521
-
522
665
  end
@@ -38,15 +38,15 @@ module Sinatra
38
38
  end
39
39
 
40
40
  app.post '/login' do
41
- Log.debug "Loggin: #{params.inspect}"
42
- if options.users.include? params[:user] and options.users[params[:user]] == params[:pass]
43
- Log.debug "Loggin Successful"
41
+ Log.debug "Login: #{params.inspect}"
42
+ if settings.users.include? params[:user] and settings.users[params[:user]] == params[:pass]
43
+ Log.debug "Login Successful"
44
44
  session[:authorized] = true
45
45
  session[:user] = params[:user]
46
46
  session[:context] = nil
47
47
  redirect '/'
48
48
  else
49
- Log.debug "Loggin Failed"
49
+ Log.debug "Login Failed"
50
50
  session[:authorized] = false
51
51
  session[:user] = nil
52
52
  session[:context] = nil
@@ -1,7 +1,16 @@
1
1
  module Entity
2
2
 
3
3
  module REST
4
- LIST_REPO = {}
4
+ include RbbtHTMLHelpers
5
+
6
+ def rbbt_id
7
+ return "" unless self.annotations.include? :format and String === self.format
8
+ ([self.format.downcase, self] * "=").gsub(/[^\w]/,'_')
9
+ end
10
+
11
+ class << self
12
+ attr_accessor :list_repo
13
+ end
5
14
 
6
15
  def link(text = nil, url = nil, options = {})
7
16
  return nil if self.nil?
@@ -19,7 +28,7 @@ module Entity
19
28
  text = self if text.nil? or text == :literal or text.to_s.strip.empty?
20
29
 
21
30
  klass = options[:class]
22
- klass ||= "entity " + (self.respond_to?(:format)? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " ")
31
+ klass ||= "entity " + ((self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " ")
23
32
 
24
33
  klass += " " << options[:extra_classes] || "" if options.include? :extra_classes
25
34
  klass += " " << html_class if self.respond_to? :html_class
@@ -38,31 +47,102 @@ module Entity
38
47
  end
39
48
 
40
49
  annotations_str = params.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
41
- "<a class='#{klass}' href='#{url + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
50
+ "<a class='#{klass}' #{rbbt_id} href='#{url + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
42
51
  end
43
52
  end
44
53
 
45
- def self.save_list(id, list)
46
- Entity::REST::LIST_REPO[id] = list
54
+ def self.list_file(type, id, user = nil)
55
+ type = type.split(":").first if not type.nil? and type.index ':'
56
+ if production?
57
+ if user.nil?
58
+ File.join(Entity.entity_list_cache, type.to_s, id)
59
+ else
60
+ File.join(Entity.entity_list_cache, user.to_s, type.to_s, id)
61
+ end
62
+ else
63
+ nil
64
+ end
47
65
  end
48
66
 
49
- def self.load_list(id)
50
- Entity::REST::LIST_REPO[id]
67
+ def self.list_files(user)
68
+ if production?
69
+ lists = {}
70
+ Dir.glob(File.join(Entity.entity_list_cache, user, '*', '*')).each do |file|
71
+ user, type, list = file.split("/")[-3..-1]
72
+ lists[type] ||= []
73
+ lists[type] << list
74
+ end
75
+ lists
76
+ else
77
+ {}
78
+ end
79
+ end
80
+
81
+ def self.load_list(type, id, user = nil)
82
+ if production?
83
+ path = list_file(type, id, user)
84
+ path = list_file(type, id, :public) unless File.exists? path
85
+ path = list_file(type, id) unless File.exists? path
86
+
87
+ Misc.lock path do
88
+ Annotated.load_tsv TSV.open(path)
89
+ end
90
+ else
91
+ @@list_cache ||= {}
92
+ @@list_cache[id]
93
+ end
94
+ end
95
+
96
+ def self.save_list(type, id, list, user = nil)
97
+ if production?
98
+ path = list_file(type, id, user)
99
+
100
+ Misc.lock path do
101
+ begin
102
+ Open.write(path, Annotated.tsv(list, :all).to_s)
103
+ rescue
104
+ FileUtils.rm path if File.exists? path
105
+ raise $!
106
+ end
107
+ end
108
+ else
109
+ @@list_cache ||= {}
110
+ @@list_cache[id] = list
111
+ end
112
+ end
113
+
114
+
115
+ def self.delete_list(type, id, user)
116
+ if production?
117
+ path = list_file(type, id, user)
118
+ "This list does not belong to #{ user }: #{[type, id] * ": "}" unless File.exists? path
119
+ Misc.lock path do
120
+ begin
121
+ FileUtils.rm path if File.exists? path
122
+ rescue
123
+ raise $!
124
+ end
125
+ end
126
+ else
127
+ @@list_cache.delete(id) if @@list_cache and @@list_cache.include? id
128
+ end
51
129
  end
52
130
 
53
131
  def list_link(text = nil, id = nil, options = {})
54
- text = self.length if text.nil? or (String === text and text.strip.empty?)
132
+ reuse = options.delete("reuse") || options.delete(:reuse)
133
+ text = self.length if text.nil? or (String === text and text.strip.empty?) or text == :length
55
134
 
56
135
  id ||= options[:id] || Misc.digest((self * "|").inspect)
57
136
 
58
- Entity::REST.save_list(id, self)
59
137
 
60
- klass = self.respond_to?(:format)? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
138
+ klass = (self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
61
139
 
62
140
  klass += " " << options[:extra_classes] if options.include? :extra_classes
63
141
  klass += " " << html_class if self.respond_to? :html_class
64
142
 
65
- type = self.respond_to?(:format)? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
143
+ type = (self.respond_to?(:format) and not self.format.nil?) ? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
144
+
145
+ Entity::REST.save_list(annotation_types.last.to_s, id, self) unless reuse
66
146
 
67
147
  annotations_str = self.info.reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
68
148
 
@@ -71,32 +151,50 @@ module Entity
71
151
 
72
152
  def action_link(action, text = nil, inputs = {})
73
153
  options = inputs.delete(:link_options) || {}
154
+ options[:extra_classes] = inputs.delete(:extra_classes) || inputs.delete("extra_class") if inputs.include?(:extra_classes) or inputs.include?("extra_classes")
74
155
  clean_inputs = {}
75
156
  inputs.each do |k, v|
76
157
  clean_inputs[k] = Array === v ? v * "," : v
77
158
  end
78
- link(text, nil, options.merge(:extra_params => clean_inputs, :action => action))
159
+
160
+ klass ||= "entity_action " + (self.respond_to?(:format)? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " ")
161
+ klass += " " << options[:extra_classes] if options.include? :extra_classes
162
+ klass += " " << html_class if self.respond_to? :html_class
163
+
164
+ link(text, nil, options.merge(:extra_params => clean_inputs, :action => action, :class => klass))
79
165
  end
80
166
 
81
167
  def list_action_link(action, text = nil, id = nil, options = {})
168
+ reuse = options.delete("reuse") || options.delete(:reuse)
169
+
82
170
  text = self.length if text.nil? or (String === text and text.strip.empty?)
83
171
  id ||= options[:id] || Misc.digest((self * "|").inspect)
84
172
 
85
173
  text = id if text == :id
86
174
 
87
- Entity::REST.save_list(id, self)
88
175
 
89
- klass = self.respond_to?(:format)? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
176
+ klass = (self.respond_to?(:format) and not self.format.nil?) ? self.format.gsub(/\s/,'_') : annotation_types.collect{|t| t.to_s.gsub(/\s/,'_')} * " "
90
177
 
91
- klass += " " << options[:extra_classes] if options.include? :extra_classes
178
+ klass += " " << options.delete(:extra_classes) if options.include? :extra_classes
92
179
  klass += " " << html_class if self.respond_to? :html_class
93
180
 
94
- type = self.respond_to?(:format)? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
181
+ type = (self.respond_to?(:format) and not self.format.nil?)? annotation_types.last.to_s + ":" + self.format : annotation_types.last.to_s
95
182
 
96
- annotations_str = self.info.reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
183
+ Entity::REST.save_list(annotation_types.last.to_s, id, self) unless reuse
184
+
185
+ annotations_str = self.info.merge(options).reject{|k,v| [:annotation_types, :format].include?(k)}.collect{|k,v| [k, CGI.escape(v.to_s)] * "="} * "&"
97
186
 
98
187
  "<a class='entity_list_action #{klass}' href='#{File.join("/entity_list_action", type, action, id) + (annotations_str.any? ? '?' + annotations_str : "")}'>#{text}</a>"
99
188
  end
100
189
 
190
+ def html_table(fields, workflow = nil, field_names = nil)
191
+ raise "Must be an Array to produce html table: #{self.inspect}" unless Array === self
192
+ rows = self.collect{|entity|
193
+ [entity.id, entity.tsv_values(fields)]
194
+ }
195
+
196
+ workflow_partial('partials/_table', workflow = nil, :rows => rows, :header => field_names || fields)
197
+ end
198
+
101
199
  end
102
200
  end
@@ -35,4 +35,53 @@ module RbbtHTMLHelpers
35
35
  str
36
36
  end
37
37
  end
38
+
39
+ def input(type, name, options = {})
40
+ id, hide, value, klass, default, description, select_options = Misc.process_options options, :id, :hide, :value, :class, :default, :description, :select_options
41
+ str = "<div class='form_input' id='#{ id }'>"
42
+
43
+ if hide
44
+ str << "<input #{"id='#{ id }'" if id} type='hidden' name='#{ name }' #{"class='#{ klass }'" if klass} #{"value='#{ value }'" if value}/>" << '</div>' << "\n"
45
+ return str
46
+ end
47
+
48
+ case type
49
+ when :select
50
+ str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
51
+ str << "<select id='input_#{id}_true' name='#{ name }'>" << "\n"
52
+ if not select_options.nil?
53
+ select_options.each do |option|
54
+ option, option_name = option if Array === option
55
+ option_name ||= option
56
+ str << "<option id='input_#{id}_option_#{option.to_s}' value='#{option.to_s}' #{option.to_s == value.to_s ? "selected=selected" : "" }>" << option_name.to_s << "</option>" << "\n"
57
+ end
58
+ end
59
+ str << "</select>" << "\n"
60
+ str << "</div>"
61
+ str
62
+ when :boolean
63
+ not_value = ! value
64
+ str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}</label>" << "\n"
65
+ str << "<input type='radio' id='input_#{id}_true' name='#{ name }' value='true' #{"checked='true'" if value}>" << "\n"
66
+ str << "<label type='radio_option radio_true' for='input_#{id}_true' name='#{ name }'>true</label>" << "\n"
67
+ str << "<input type='radio' id='input_#{id}_false' name='#{ name }' value='false' #{"checked='false'" if not value}>" << "\n"
68
+ str << "<label type='radio_option radio_false' for='input_#{id}_false' name='#{ name }'>false</label>" << "\n"
69
+ str << "</div>"
70
+ str
71
+ when :string, :float, :integer
72
+ str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
73
+ str << "<input #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='#{ id }'" if id} name='#{ name }' #{"value='#{ value }'" if value}/>" << "\n"
74
+ str << "</div>"
75
+ str
76
+ when :tsv, :array, :text
77
+ str << "<label #{"for='#{ id }" if id}'><span class='input_name'>#{ name }</span>#{": <span class='input_description'>#{ description }</span>" if description}#{": <span class='input_default'>(Default: #{ default })</span>" if default}</label>" << "\n"
78
+ str << "<p>"
79
+ str << "<input type='file' #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} #{"id='input_#{ id }_tsv_file'" if id} name='#{ name }_param_file' #{"value='#{ value }'" if value}/>" << "\n"
80
+ str << "<span class='tsv_textarea_note note'>(or use text area bellow)</span>"
81
+ str << "</p>"
82
+ str << "<textarea #{"class='#{klass ? klass : "#{ name } #{ type }" }'"} name='#{ name }' #{"id='input_#{ id }'" if id}></textarea>"
83
+ str << "</div>"
84
+ str
85
+ end
86
+ end
38
87
  end