openwferu-extras 0.9.15 → 0.9.16
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/openwfe/extras/engine/db_persisted_engine.rb +100 -0
- data/lib/openwfe/extras/expool/dberrorjournal.rb +190 -0
- data/lib/openwfe/extras/expool/dbexpstorage.rb +267 -0
- data/lib/openwfe/extras/listeners/sqslisteners.rb +1 -3
- data/lib/openwfe/extras/misc/activityfeed.rb +275 -0
- data/lib/openwfe/extras/participants/activeparticipants.rb +87 -2
- data/lib/openwfe/extras/participants/{atomparticipants.rb → atomfeed_participants.rb} +20 -29
- data/lib/openwfe/extras/participants/atompub_participants.rb +286 -0
- data/lib/openwfe/extras/participants/csvparticipants.rb +5 -9
- data/lib/openwfe/extras/util/csvtable.rb +147 -14
- metadata +13 -5
@@ -0,0 +1,275 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2007, John Mettraux, OpenWFE.org
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# . Redistributions of source code must retain the above copyright notice, this
|
10
|
+
# list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# . Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
14
|
+
# and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# . Neither the name of the "OpenWFE" nor the names of its contributors may be
|
17
|
+
# used to endorse or promote products derived from this software without
|
18
|
+
# specific prior written permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
#++
|
32
|
+
#
|
33
|
+
|
34
|
+
#
|
35
|
+
# "made in Japan"
|
36
|
+
#
|
37
|
+
# John Mettraux at openwfe.org
|
38
|
+
#
|
39
|
+
|
40
|
+
#
|
41
|
+
# this participant requires atom-tools from
|
42
|
+
#
|
43
|
+
# http://code.necronomicorp.com/trac/atom-tools
|
44
|
+
#
|
45
|
+
# atom-tools' license is X11/MIT
|
46
|
+
#
|
47
|
+
|
48
|
+
#require 'monitor'
|
49
|
+
|
50
|
+
require 'rubygems'
|
51
|
+
|
52
|
+
begin
|
53
|
+
require 'atom/collection'
|
54
|
+
rescue LoadError
|
55
|
+
#
|
56
|
+
# soft dependency on 'atom-tools'
|
57
|
+
#
|
58
|
+
puts
|
59
|
+
puts
|
60
|
+
puts "'atom/collection' is missing. You can install with :"
|
61
|
+
puts
|
62
|
+
puts " [sudo] gem install atom-tools"
|
63
|
+
puts
|
64
|
+
puts
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
require 'openwfe/service'
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
module OpenWFE::Extras
|
73
|
+
|
74
|
+
#
|
75
|
+
# A workitem event as kept
|
76
|
+
#
|
77
|
+
class Entry
|
78
|
+
|
79
|
+
attr_accessor :id
|
80
|
+
attr_accessor :updated
|
81
|
+
|
82
|
+
attr_accessor :participant_name
|
83
|
+
attr_accessor :upon
|
84
|
+
attr_accessor :workitem
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
|
88
|
+
@update = Time.now
|
89
|
+
end
|
90
|
+
|
91
|
+
def as_atom_entry
|
92
|
+
|
93
|
+
e = Atom::Entry.new
|
94
|
+
|
95
|
+
e.id = @id
|
96
|
+
e.updated = @updated
|
97
|
+
|
98
|
+
e
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# This feed registers as an observer to the ParticipantMap.
|
104
|
+
# Each time a workitem is delivered to a participant or comes back from/for
|
105
|
+
# it, an AtomEntry is generated.
|
106
|
+
# The get_feed() method produces an atom feed of participant activity.
|
107
|
+
#
|
108
|
+
class ActivityFeedService
|
109
|
+
#include MonitorMixin
|
110
|
+
include ServiceMixin
|
111
|
+
include OwfeServiceLocator
|
112
|
+
|
113
|
+
|
114
|
+
attr_accessor :max_item_count
|
115
|
+
|
116
|
+
|
117
|
+
#
|
118
|
+
# This service is generally tied to an engine by doing :
|
119
|
+
#
|
120
|
+
# engine.init_service 'activityFeed', ActivityFeedService
|
121
|
+
#
|
122
|
+
# The init_service() will take care of calling the constructor
|
123
|
+
# implemented here.
|
124
|
+
#
|
125
|
+
def initialize (service_name, application_context)
|
126
|
+
|
127
|
+
super()
|
128
|
+
|
129
|
+
service_init service_name, application_context
|
130
|
+
|
131
|
+
@entries = []
|
132
|
+
@max_item_count = 100
|
133
|
+
|
134
|
+
get_participant_map.add_observer ".*", self
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# This is the method call by the expression pool each time a
|
139
|
+
# workitem reaches a participant.
|
140
|
+
#
|
141
|
+
def call (channel, *args)
|
142
|
+
|
143
|
+
e = Entry.new
|
144
|
+
|
145
|
+
e.participant_name = channel
|
146
|
+
e.upon = args[0]
|
147
|
+
e.workitem = args[1].dup
|
148
|
+
|
149
|
+
e.id = \
|
150
|
+
"#{e.workitem.participant_name} - #{e.upon} " +
|
151
|
+
"#{e.workitem.fei.workflow_instance_id}--" +
|
152
|
+
"#{e.workitem.fei.expression_id}"
|
153
|
+
|
154
|
+
@entries << e
|
155
|
+
|
156
|
+
@entries = @entries[0, @max_item_count] \
|
157
|
+
if @entries.length > @max_item_count
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# Returns an Atom feed of all the workitem activity for the
|
162
|
+
# participants whose name matches the given regular expression.
|
163
|
+
#
|
164
|
+
# Options :
|
165
|
+
#
|
166
|
+
# [:upon] can be set to either nil, :apply, :reply. :apply states
|
167
|
+
# that the returned feed should only contain entries about
|
168
|
+
# participant getting applied, :reply only about
|
169
|
+
# participant replying.
|
170
|
+
# [:feed_uri] the URI for the feed. Defaults to
|
171
|
+
# 'http://localhost/feed'
|
172
|
+
# [:feed_title] the title for the feed. Defaults to
|
173
|
+
# 'OpenWFEru engine activity feed'
|
174
|
+
# [:format] yaml|text|json
|
175
|
+
#
|
176
|
+
#
|
177
|
+
def get_feed (participant_regex, options={})
|
178
|
+
|
179
|
+
participant_regex = Regexp.compile(participant_regex) \
|
180
|
+
if participant_regex.is_a?(String)
|
181
|
+
|
182
|
+
upon = options[:upon]
|
183
|
+
feed_uri = options[:feed_uri] || "http://localhost/feed"
|
184
|
+
title = options[:feed_title] || "OpenWFEru engine activity feed"
|
185
|
+
|
186
|
+
feed = Atom::Collection.new feed_uri
|
187
|
+
feed.title = title
|
188
|
+
|
189
|
+
format = options[:format]
|
190
|
+
format = validate_format(format)
|
191
|
+
|
192
|
+
@entries.each do |e|
|
193
|
+
|
194
|
+
next unless participant_regex.match(e.participant_name)
|
195
|
+
next if upon and upon != e.upon
|
196
|
+
|
197
|
+
feed.updated = e.updated \
|
198
|
+
if feed.updated == nil or e.updated > feed.updated
|
199
|
+
|
200
|
+
feed << as_atom_entry(format, e)
|
201
|
+
end
|
202
|
+
|
203
|
+
feed
|
204
|
+
end
|
205
|
+
|
206
|
+
protected
|
207
|
+
|
208
|
+
#
|
209
|
+
# Makes sure the required 'render' is valid. Returns it
|
210
|
+
# as a Symbol.
|
211
|
+
#
|
212
|
+
def validate_format (f)
|
213
|
+
|
214
|
+
f = "as_#{f}"
|
215
|
+
return :as_yaml unless methods.include?(f)
|
216
|
+
f.to_sym
|
217
|
+
end
|
218
|
+
|
219
|
+
def as_atom_entry (render, entry)
|
220
|
+
|
221
|
+
send(
|
222
|
+
render,
|
223
|
+
entry.as_atom_entry,
|
224
|
+
entry.participant_name,
|
225
|
+
entry.upon,
|
226
|
+
entry.workitem)
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# A basic rendition of a workitem as a YAML string
|
231
|
+
#
|
232
|
+
def as_yaml (atom_entry, participant_name, upon, workitem)
|
233
|
+
|
234
|
+
atom_entry.title = "#{participant_name} - #{upon}"
|
235
|
+
atom_entry.content = workitem.to_yaml
|
236
|
+
atom_entry.content['type'] = "text/plain"
|
237
|
+
|
238
|
+
atom_entry
|
239
|
+
end
|
240
|
+
|
241
|
+
#class Atom::Content
|
242
|
+
# def convert_contents e
|
243
|
+
# REXML::CData.new(@content.to_s)
|
244
|
+
# end
|
245
|
+
#end
|
246
|
+
|
247
|
+
#
|
248
|
+
# A basic rendition of a workitem as text.
|
249
|
+
#
|
250
|
+
def as_text (atom_entry, participant_name, upon, workitem)
|
251
|
+
|
252
|
+
atom_entry.title = "#{participant_name} - #{upon}"
|
253
|
+
|
254
|
+
atom_entry.content = workitem.to_s
|
255
|
+
atom_entry.content['type'] = "text/plain"
|
256
|
+
|
257
|
+
atom_entry
|
258
|
+
end
|
259
|
+
|
260
|
+
#
|
261
|
+
# Renders the workitem as a JSON string.
|
262
|
+
#
|
263
|
+
def as_json (atom_entry, participant_name, upon, workitem)
|
264
|
+
|
265
|
+
atom_entry.title = "#{participant_name} - #{upon}"
|
266
|
+
|
267
|
+
atom_entry.content = workitem.to_json
|
268
|
+
atom_entry.content['type'] = "text/plain"
|
269
|
+
|
270
|
+
atom_entry
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
@@ -68,7 +68,7 @@ module Extras
|
|
68
68
|
# in the same source file. It should be quite easy for the Rails hackers
|
69
69
|
# among you to sort that out for a Rails based usage.
|
70
70
|
#
|
71
|
-
class
|
71
|
+
class WorkitemTables < ActiveRecord::Migration
|
72
72
|
|
73
73
|
def self.up
|
74
74
|
|
@@ -106,6 +106,14 @@ module Extras
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
+
#
|
110
|
+
# Reopening InFlowWorkItem to add a 'db_id' attribute.
|
111
|
+
#
|
112
|
+
class InFlowWorkItem
|
113
|
+
|
114
|
+
attr_accessor :db_id
|
115
|
+
end
|
116
|
+
|
109
117
|
#
|
110
118
|
# The ActiveRecord version of an OpenWFEru workitem (InFlowWorkItem).
|
111
119
|
#
|
@@ -179,7 +187,7 @@ module Extras
|
|
179
187
|
end
|
180
188
|
|
181
189
|
i.save!
|
182
|
-
|
190
|
+
# making sure to throw an exception in case of trouble
|
183
191
|
end
|
184
192
|
|
185
193
|
i
|
@@ -191,10 +199,14 @@ module Extras
|
|
191
199
|
def as_owfe_workitem
|
192
200
|
|
193
201
|
wi = OpenWFE::InFlowWorkItem.new
|
202
|
+
|
194
203
|
wi.fei = full_fei
|
195
204
|
wi.participant_name = participant_name
|
196
205
|
wi.attributes = fields_hash
|
197
206
|
# don't care about dispatch_time and last_modified
|
207
|
+
|
208
|
+
wi.db_id = self.id
|
209
|
+
|
198
210
|
wi
|
199
211
|
end
|
200
212
|
|
@@ -230,6 +242,7 @@ module Extras
|
|
230
242
|
# an old trick for backward compatibility with OpenWFEja
|
231
243
|
|
232
244
|
save!
|
245
|
+
# making sure to throw an exception in case of trouble
|
233
246
|
end
|
234
247
|
|
235
248
|
#
|
@@ -302,6 +315,78 @@ module Extras
|
|
302
315
|
|
303
316
|
result
|
304
317
|
end
|
318
|
+
|
319
|
+
#
|
320
|
+
# A kind of 'google search' among workitems
|
321
|
+
#
|
322
|
+
def Workitem.search (search_string, storename_list=nil)
|
323
|
+
|
324
|
+
storename_list = Array(storename_list) if storename_list
|
325
|
+
|
326
|
+
# participant_name
|
327
|
+
|
328
|
+
result = find(
|
329
|
+
:all,
|
330
|
+
:conditions => conditions(
|
331
|
+
"participant_name", search_string, storename_list),
|
332
|
+
:order => "participant_name")
|
333
|
+
# :limit => 10)
|
334
|
+
|
335
|
+
ids = result.collect { |wi| wi.id }
|
336
|
+
|
337
|
+
# svalue
|
338
|
+
|
339
|
+
fields = Field.find(
|
340
|
+
:all,
|
341
|
+
:conditions => conditions(
|
342
|
+
"svalue", search_string, storename_list),
|
343
|
+
:include => :workitem)
|
344
|
+
|
345
|
+
merge_search_results(ids, result, fields)
|
346
|
+
|
347
|
+
# fkey
|
348
|
+
|
349
|
+
fields = Field.find(
|
350
|
+
:all,
|
351
|
+
:conditions => conditions(
|
352
|
+
"fkey", search_string, storename_list),
|
353
|
+
:include => :workitem)
|
354
|
+
|
355
|
+
merge_search_results(ids, result, fields)
|
356
|
+
|
357
|
+
# over.
|
358
|
+
|
359
|
+
result
|
360
|
+
end
|
361
|
+
|
362
|
+
protected
|
363
|
+
|
364
|
+
#
|
365
|
+
# builds the condition (the WHERE clause) for the
|
366
|
+
# search.
|
367
|
+
#
|
368
|
+
def Workitem.conditions (keyname, search_string, storename_list)
|
369
|
+
|
370
|
+
if storename_list
|
371
|
+
[ "#{keyname} LIKE ? AND workitems.store_name IN (?)",
|
372
|
+
search_string, storename_list ]
|
373
|
+
else
|
374
|
+
[ "#{keyname} LIKE ?",
|
375
|
+
search_string ]
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def Workitem.merge_search_results (ids, wis, new_wis)
|
380
|
+
|
381
|
+
return if new_wis.size < 1
|
382
|
+
|
383
|
+
new_wis.each do |wi|
|
384
|
+
wi = wi.workitem if wi.kind_of?(Field)
|
385
|
+
next if ids.include? wi.id
|
386
|
+
ids << wi.id
|
387
|
+
wis << wi
|
388
|
+
end
|
389
|
+
end
|
305
390
|
end
|
306
391
|
|
307
392
|
#
|
@@ -30,8 +30,6 @@
|
|
30
30
|
# POSSIBILITY OF SUCH DAMAGE.
|
31
31
|
#++
|
32
32
|
#
|
33
|
-
# $Id$
|
34
|
-
#
|
35
33
|
|
36
34
|
#
|
37
35
|
# "made in Japan"
|
@@ -70,15 +68,14 @@ end
|
|
70
68
|
require 'openwfe/participants/participant'
|
71
69
|
|
72
70
|
|
73
|
-
module OpenWFE
|
74
|
-
module Extras
|
71
|
+
module OpenWFE::Extras
|
75
72
|
|
76
73
|
#
|
77
74
|
# Stores the incoming workitem into an 'atom feed'
|
78
75
|
#
|
79
76
|
# An example :
|
80
77
|
#
|
81
|
-
# feed0 =
|
78
|
+
# feed0 = AtomFeedParticipant.new(
|
82
79
|
# 20, # no more than 20 entries are kept
|
83
80
|
# """
|
84
81
|
# <p>
|
@@ -89,13 +86,13 @@ module Extras
|
|
89
86
|
# The 'template' parameter may contain an instance of File instead of
|
90
87
|
# an instance of String.
|
91
88
|
#
|
92
|
-
# feed0 =
|
89
|
+
# feed0 = AtomFeedParticipant.new(
|
93
90
|
# 20, File.new("path/to/my/atom/template.txt")
|
94
91
|
#
|
95
92
|
# The template can be passed as the second parameter (after the max entry
|
96
93
|
# count) or as a block :
|
97
94
|
#
|
98
|
-
# feed1 =
|
95
|
+
# feed1 = AtomFeedParticipant.new(20) do |flow_expression, atom_participant, workitem|
|
99
96
|
# #
|
100
97
|
# # usually only the workitem parameter is used
|
101
98
|
# # but the other two allow for advanced tricks...
|
@@ -117,15 +114,19 @@ module Extras
|
|
117
114
|
# "atom-tools" from http://code.necronomicorp.com/trac/atom-tools
|
118
115
|
#
|
119
116
|
#
|
120
|
-
class
|
121
|
-
include
|
117
|
+
class AtomFeedParticipant
|
118
|
+
include MonitorMixin
|
119
|
+
include LocalParticipant
|
120
|
+
include TemplateMixin
|
122
121
|
|
123
|
-
|
124
|
-
|
122
|
+
#
|
123
|
+
# Made accessible so that blocks may set it.
|
124
|
+
#
|
125
|
+
attr_accessor :content_type
|
125
126
|
|
126
127
|
def initialize (max_item_count, template=nil, &block)
|
127
128
|
|
128
|
-
super()
|
129
|
+
super() # very important as this class includes MonitorMixin
|
129
130
|
|
130
131
|
@template = template
|
131
132
|
@max_item_count = max_item_count
|
@@ -160,24 +161,15 @@ module Extras
|
|
160
161
|
|
161
162
|
protected
|
162
163
|
|
164
|
+
#
|
165
|
+
# This base implementation simply calls the eval_template()
|
166
|
+
# method of the TemplateMixin.
|
167
|
+
# Feel free to override this render() method for custom
|
168
|
+
# representations.
|
169
|
+
#
|
163
170
|
def render (workitem)
|
164
171
|
|
165
|
-
|
166
|
-
|
167
|
-
template = if @block_template
|
168
|
-
#@block_template.call(fe, self, workitem)
|
169
|
-
call_block @block_template, workitem
|
170
|
-
elsif @template
|
171
|
-
if @template.kind_of? File
|
172
|
-
@template.readlines
|
173
|
-
else
|
174
|
-
@template.to_s
|
175
|
-
end
|
176
|
-
else
|
177
|
-
"(no template given)"
|
178
|
-
end
|
179
|
-
|
180
|
-
OpenWFE::dosub(template, fe, workitem)
|
172
|
+
eval_template workitem
|
181
173
|
end
|
182
174
|
|
183
175
|
#
|
@@ -194,5 +186,4 @@ module Extras
|
|
194
186
|
end
|
195
187
|
|
196
188
|
end
|
197
|
-
end
|
198
189
|
|