rbbt-views 1.0.0 → 1.0.1

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