caterpillar 0.9.0
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/LICENSES.txt +25 -0
- data/README +21 -0
- data/Rakefile +26 -0
- data/bin/caterpillar +39 -0
- data/db/migrate/20081205000001_lportal_portlets.rb +12 -0
- data/db/migrate/20081205000002_lportal_sequences.rb +50 -0
- data/generators/caterpillar/USAGE +1 -0
- data/generators/caterpillar/caterpillar_generator.rb +52 -0
- data/generators/caterpillar/templates/config/portlets.rb +59 -0
- data/generators/caterpillar/templates/images/caterpillar/caterpillar.gif +0 -0
- data/generators/caterpillar/templates/javascripts/caterpillar/caterpillar.js +15 -0
- data/generators/caterpillar/templates/stylesheets/caterpillar/caterpillar.css +48 -0
- data/init.rb +8 -0
- data/install.rb +3 -0
- data/lib/caterpillar.rb +27 -0
- data/lib/caterpillar/config.rb +57 -0
- data/lib/caterpillar/liferay.rb +235 -0
- data/lib/caterpillar/liferay_portlet.rb +8 -0
- data/lib/caterpillar/navigation.rb +40 -0
- data/lib/caterpillar/parser.rb +110 -0
- data/lib/caterpillar/portlet.rb +98 -0
- data/lib/caterpillar/task.rb +285 -0
- data/lib/caterpillar/usage.rb +21 -0
- data/lib/caterpillar/util.rb +80 -0
- data/lib/caterpillar/version.rb +9 -0
- data/lib/web/portlet_name.rb +8 -0
- data/test/portlets_test.rb +43 -0
- data/test/xml_test.rb +24 -0
- data/views/caterpillar/_navigation.html.erb +45 -0
- metadata +116 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
#--
|
2
|
+
# (c) Copyright 2008 Mikael Lammentausta
|
3
|
+
# See the file LICENSES.txt included with the distribution for
|
4
|
+
# software license details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Caterpillar
|
8
|
+
# Creates liferay-portlet XML and liferay-display XML.
|
9
|
+
# The latter optionally combines your production portlet display configuration.
|
10
|
+
class Liferay
|
11
|
+
|
12
|
+
# Liferay version
|
13
|
+
attr_accessor :version
|
14
|
+
|
15
|
+
# The location of Liferay's WEB-INF folder for XML analyzation
|
16
|
+
attr_accessor :WEB_INF
|
17
|
+
|
18
|
+
def initialize(version='5.1.1')
|
19
|
+
@version = version
|
20
|
+
end
|
21
|
+
|
22
|
+
def analyze(type)
|
23
|
+
raise 'Configure WEB-INF' unless self.WEB_INF
|
24
|
+
require 'hpricot'
|
25
|
+
return nil unless type==:native
|
26
|
+
|
27
|
+
portlets = []
|
28
|
+
|
29
|
+
f=File.open(self.WEB_INF+'/portlet-custom.xml','r')
|
30
|
+
portlet_xml = Hpricot.XML(f.read)
|
31
|
+
f.close
|
32
|
+
|
33
|
+
f=File.open(self.WEB_INF+'/liferay-display.xml','r')
|
34
|
+
display_xml = Hpricot.XML(f.read)
|
35
|
+
f.close
|
36
|
+
|
37
|
+
(portlet_xml/'portlet').each do |portlet|
|
38
|
+
_p = {
|
39
|
+
:name => (portlet/'portlet-name').innerHTML,
|
40
|
+
:title => (portlet/'display-name').innerHTML
|
41
|
+
}
|
42
|
+
|
43
|
+
# horribly ineffective
|
44
|
+
display_xml.search("//category").each do |c|
|
45
|
+
_p.update(:category => c['name'] ) if (c/"//portlet[@id='#{_p[:name]}']").any?
|
46
|
+
end
|
47
|
+
|
48
|
+
portlets << _p
|
49
|
+
end
|
50
|
+
return portlets
|
51
|
+
end
|
52
|
+
|
53
|
+
# liferay-portlet XML
|
54
|
+
def portletapp_xml(portlets)
|
55
|
+
doctype = 'liferay-portlet-app'
|
56
|
+
xml = self.xml_header(doctype)
|
57
|
+
portlets.each do |p|
|
58
|
+
xml << self.portletapp_template(p)
|
59
|
+
end
|
60
|
+
xml << self.portlet_xml_footer(doctype)
|
61
|
+
return xml
|
62
|
+
end
|
63
|
+
|
64
|
+
# liferay-display XML
|
65
|
+
def display_xml(portlets)
|
66
|
+
xml = self.xml_header('display')
|
67
|
+
|
68
|
+
categories = []
|
69
|
+
Util.categorize(portlets).each_pair do |category,portlets|
|
70
|
+
categories << category
|
71
|
+
xml << self.display_template(category,portlets)
|
72
|
+
end
|
73
|
+
|
74
|
+
# include other native Liferay categories
|
75
|
+
if self.WEB_INF
|
76
|
+
require 'hpricot'
|
77
|
+
|
78
|
+
filename = self.WEB_INF+'/liferay-display.xml'
|
79
|
+
f=File.open(filename,'r')
|
80
|
+
doc = Hpricot.XML(f.read)
|
81
|
+
f.close
|
82
|
+
(doc/:category).each do |el|
|
83
|
+
unless categories.include?(el.attributes['name'])
|
84
|
+
xml << ' ' + el.to_original_html + "\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
xml << self.portlet_xml_footer('display')
|
90
|
+
return xml
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
# common XML header
|
96
|
+
def xml_header(doctype)
|
97
|
+
version = self.dtd_version(doctype)
|
98
|
+
xml = '<?xml version="1.0" encoding="UTF-8"?>'
|
99
|
+
xml << "\n"
|
100
|
+
xml << '<!DOCTYPE %s PUBLIC' % doctype
|
101
|
+
case doctype
|
102
|
+
when 'liferay-portlet-app'
|
103
|
+
xml << ' "-//Liferay//DTD Portlet Application %s//EN"' % version
|
104
|
+
xml << ' "http://www.liferay.com/dtd/%s_%s.dtd">' % [
|
105
|
+
doctype, version.gsub('.','_') ]
|
106
|
+
when 'display'
|
107
|
+
xml << ' "-//Liferay//DTD Display %s//EN"' % version
|
108
|
+
xml << ' "http://www.liferay.com/dtd/liferay-%s_%s.dtd">' % [
|
109
|
+
doctype, version.gsub('.','_') ]
|
110
|
+
end
|
111
|
+
xml << "\n\n"
|
112
|
+
xml << '<%s>' % doctype
|
113
|
+
xml << "\n"
|
114
|
+
return xml
|
115
|
+
end
|
116
|
+
|
117
|
+
# common XML footer
|
118
|
+
def portlet_xml_footer(doctype)
|
119
|
+
'</%s>' % doctype
|
120
|
+
end
|
121
|
+
|
122
|
+
# TODO: DTD version detection based on self.version
|
123
|
+
def dtd_version(type)
|
124
|
+
case type
|
125
|
+
when 'liferay-portlet-app'
|
126
|
+
'5.1.0'
|
127
|
+
when 'display'
|
128
|
+
'5.1.0'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def portletapp_template(portlet)
|
133
|
+
xml = " <portlet>\n"
|
134
|
+
xml << " <portlet-name>%s</portlet-name>\n" % portlet[:name]
|
135
|
+
xml << " <icon>/%s/images/icon.png</icon>\n" % portlet[:servlet]
|
136
|
+
# can there be several portlet instances on the same page?
|
137
|
+
xml << " <instanceable>true</instanceable>\n"
|
138
|
+
# include javascripts?
|
139
|
+
portlet[:javascripts].each do |js|
|
140
|
+
xml << " <header-portal-javascript>"
|
141
|
+
xml << "/%s/javascripts/%s" % [portlet[:servlet],js]
|
142
|
+
xml << "</header-portal-javascript>\n"
|
143
|
+
end
|
144
|
+
xml << " </portlet>\n\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
def display_template(category,portlets)
|
148
|
+
xml = ' <category name="%s">' % category +"\n"
|
149
|
+
|
150
|
+
portlets.each do |p|
|
151
|
+
xml << ' <portlet id="%s" />' % p[:name]
|
152
|
+
xml << "\n"
|
153
|
+
end
|
154
|
+
xml << " </category>\n\n"
|
155
|
+
|
156
|
+
return xml
|
157
|
+
end
|
158
|
+
|
159
|
+
public
|
160
|
+
|
161
|
+
# tables that are skipped when creating fixtures
|
162
|
+
def skip_fixture_tables
|
163
|
+
[
|
164
|
+
"country","cyrususer","cyrusvirtual",
|
165
|
+
"documentlibrary_fsentry","documentlibrary_binval","documentlibrary_node","documentlibrary_prop","documentlibrary_refs",
|
166
|
+
"expandocolumn",
|
167
|
+
"expandorow",
|
168
|
+
"expandotable",
|
169
|
+
"expandovalue",
|
170
|
+
"image",
|
171
|
+
"chat_entry",
|
172
|
+
"chat_status",
|
173
|
+
"journalcontentsearch",
|
174
|
+
"journalfeed",
|
175
|
+
"journalstructure",
|
176
|
+
"journaltemplate",
|
177
|
+
"listtype",
|
178
|
+
"mbban",
|
179
|
+
"mbmessageflag",
|
180
|
+
"mbstatsuser",
|
181
|
+
"membershiprequest",
|
182
|
+
"orglabor",
|
183
|
+
"passwordpolicyrel",
|
184
|
+
"passwordpolicy",
|
185
|
+
"passwordtracker",
|
186
|
+
"pluginsetting",
|
187
|
+
"pollschoice",
|
188
|
+
"pollsquestion",
|
189
|
+
"pollsvote",
|
190
|
+
"quartz_blob_triggers",
|
191
|
+
"quartz_calendars",
|
192
|
+
"quartz_cron_triggers",
|
193
|
+
"quartz_fired_triggers",
|
194
|
+
"quartz_job_details",
|
195
|
+
"quartz_job_listeners",
|
196
|
+
"quartz_locks",
|
197
|
+
"quartz_paused_trigger_grps",
|
198
|
+
"quartz_scheduler_state",
|
199
|
+
"quartz_simple_triggers",
|
200
|
+
"quartz_trigger_listeners",
|
201
|
+
"quartz_triggers",
|
202
|
+
"ratingsentry",
|
203
|
+
"ratingsstats",
|
204
|
+
"region",
|
205
|
+
"release_",
|
206
|
+
"scframeworkversion",
|
207
|
+
"scframeworkversi_scproductvers",
|
208
|
+
"schema_migrations",
|
209
|
+
"sclicenses_scproductentries",
|
210
|
+
"sclicense",
|
211
|
+
"scproductentry",
|
212
|
+
"scproductscreenshot",
|
213
|
+
"scproductversion",
|
214
|
+
"servicecomponent",
|
215
|
+
"sessions",
|
216
|
+
"shoppingcart",
|
217
|
+
"shoppingcategory",
|
218
|
+
"shoppingcoupon",
|
219
|
+
"shoppingitemfield",
|
220
|
+
"shoppingitemprice",
|
221
|
+
"shoppingitem",
|
222
|
+
"shoppingorderitem",
|
223
|
+
"shoppingorder",
|
224
|
+
"socialactivity",
|
225
|
+
"socialrelation",
|
226
|
+
"subscription",
|
227
|
+
"tasksproposal",
|
228
|
+
"tasksreview",
|
229
|
+
"webdavprops",
|
230
|
+
"website"
|
231
|
+
]
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Caterpillar
|
4
|
+
# This model does not appear in the lportal database. This is created by a migration and contains the portlet id => name mappings.
|
5
|
+
class LiferayPortlet < ActiveRecord::Base
|
6
|
+
set_table_name :lportal_portlets
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Caterpillar
|
2
|
+
# Portlet navigation on Rails.
|
3
|
+
#
|
4
|
+
# Caterpillar installs a partial 'caterpillar/navigation' into your views,
|
5
|
+
# along with an image and a CSS file.
|
6
|
+
# You need to add a filter 'caterpillar' which you will only load in
|
7
|
+
# development environment - in production the portlet container will be the
|
8
|
+
# 'window manager'. This partial helps you to navigate between your portlets.
|
9
|
+
#
|
10
|
+
# This will go to your ApplicationController:
|
11
|
+
# if RAILS_ENV=='development'
|
12
|
+
# before_filter :caterpillar
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def caterpillar # :nodoc:
|
16
|
+
# @caterpillar_navigation = Caterpillar::Navigation.rails
|
17
|
+
# @caterpillar_navigation_defaults = {
|
18
|
+
# :uid => 13904,
|
19
|
+
# :gid => 13912
|
20
|
+
# }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# This will go the body of your layout:
|
24
|
+
# <% if @caterpillar_navigation -%>
|
25
|
+
# <%= stylesheet_link_tag 'caterpillar/caterpillar' %>
|
26
|
+
# <%= javascript_include_tag 'caterpillar/caterpillar' %>
|
27
|
+
# <%= render :partial => "caterpillar/navigation" %>
|
28
|
+
# <% end -%>
|
29
|
+
#
|
30
|
+
class Navigation
|
31
|
+
|
32
|
+
# Method for formulating the portlets hash in Rails environment
|
33
|
+
def self.rails
|
34
|
+
config = Util.eval_configuration
|
35
|
+
config.routes = Util.parse_routes(config)
|
36
|
+
return Util.categorize(Parser.new(config).portlets)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#--
|
2
|
+
# (c) Copyright 2008 Mikael Lammentausta
|
3
|
+
# See the file LICENSES.txt included with the distribution for
|
4
|
+
# software license details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Caterpillar
|
8
|
+
# Portlet configuration and route parser.
|
9
|
+
class Parser
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
@routes = config.routes
|
14
|
+
end
|
15
|
+
|
16
|
+
# Updates the portlets hash from the routes and configuration options.
|
17
|
+
# Changes the path variables to a format supported by the Rails-portlet.
|
18
|
+
def portlets(routes=@routes)
|
19
|
+
raise 'No configuration' unless @config
|
20
|
+
raise 'No routes provided' unless routes
|
21
|
+
portlets = []
|
22
|
+
|
23
|
+
@config.instances.flatten.each do |portlet|
|
24
|
+
|
25
|
+
### route to path
|
26
|
+
if portlet[:path]
|
27
|
+
# take user-given path & do not parse routes
|
28
|
+
path = portlet[:path]
|
29
|
+
#
|
30
|
+
# parse the requirements - controller & action
|
31
|
+
# ( this is too difficult -- no navigation for user-given paths )
|
32
|
+
#
|
33
|
+
# builder = ActionController::Routing::RouteBuilder.new
|
34
|
+
# req_path = builder.segments_for_route_path(path)
|
35
|
+
# r = ActionController::Routing::Routes.recognize_path(req_path, { :method => :get })
|
36
|
+
# puts r.inspect
|
37
|
+
|
38
|
+
portlet.update( :reqs => {} )
|
39
|
+
portlet.update( :vars => [] )
|
40
|
+
|
41
|
+
else
|
42
|
+
begin
|
43
|
+
_r = routes.select{
|
44
|
+
|route| route[:name]==portlet[:name].to_sym
|
45
|
+
}
|
46
|
+
path = _r.first[:path] # take only the first segments
|
47
|
+
raise if path.nil?
|
48
|
+
rescue
|
49
|
+
STDERR.puts ' !! no route for %s' % portlet[:name]
|
50
|
+
next
|
51
|
+
end
|
52
|
+
|
53
|
+
### requirements - controller & action
|
54
|
+
portlet.update( :reqs => _r.first[:reqs] )
|
55
|
+
|
56
|
+
### variables
|
57
|
+
portlet.update( :vars => _r.first[:vars] )
|
58
|
+
|
59
|
+
# delete the route from routes
|
60
|
+
_r.each do |r|
|
61
|
+
routes.delete(r)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
# fix path variables to be replaced by rails-portlet at runtime
|
65
|
+
path.gsub!(/:uid/,'%UID%')
|
66
|
+
path.gsub!(/:gid/,'%GID%')
|
67
|
+
# TODO: notify user of unsupported variables
|
68
|
+
portlet.update( :path => path )
|
69
|
+
|
70
|
+
### javascripts
|
71
|
+
# append portlet's javascripts to global javascripts
|
72
|
+
javascripts = (portlet[:javascripts].nil? ?
|
73
|
+
@config.javascripts : @config.javascripts + portlet[:javascripts].to_a)
|
74
|
+
portlet.update( :javascripts => javascripts.flatten )
|
75
|
+
|
76
|
+
portlets << portlet
|
77
|
+
end
|
78
|
+
|
79
|
+
# leftover named routes
|
80
|
+
if @config.include_all_named_routes==true
|
81
|
+
portlets << routes
|
82
|
+
portlets.flatten!
|
83
|
+
end
|
84
|
+
|
85
|
+
# sanity check
|
86
|
+
portlets.each do |portlet|
|
87
|
+
### hostname
|
88
|
+
portlet.update( :host => @config.host ) unless portlet[:host]
|
89
|
+
|
90
|
+
### servlet
|
91
|
+
portlet.update( :servlet => @config.servlet ) unless portlet[:servlet]
|
92
|
+
|
93
|
+
### category
|
94
|
+
portlet.update( :category => @config.category ) unless portlet[:category]
|
95
|
+
|
96
|
+
### title
|
97
|
+
_title = portlet[:title] || portlet[:name].to_s.gsub('_',' ').capitalize
|
98
|
+
# strip illegal characters
|
99
|
+
title = _title.gsub(/ä/,'a').gsub(/ö/,'o').gsub(/Ä/,'A').gsub(/Ö/,'O')
|
100
|
+
portlet.update( :title => title )
|
101
|
+
|
102
|
+
### javascripts
|
103
|
+
portlet.update( :javascripts => @config.javascripts ) unless portlet[:javascripts]
|
104
|
+
end
|
105
|
+
|
106
|
+
return portlets
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#--
|
2
|
+
# (c) Copyright 2008 Mikael Lammentausta
|
3
|
+
# See the file LICENSES.txt included with the distribution for
|
4
|
+
# software license details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module Caterpillar
|
8
|
+
# Formulates generic JSR286 portlet XML
|
9
|
+
class Portlet
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Creates portlet XML
|
13
|
+
def xml(portlets)
|
14
|
+
xml = self.header
|
15
|
+
portlets.each do |p|
|
16
|
+
xml << self.template(p)
|
17
|
+
end
|
18
|
+
xml << self.footer
|
19
|
+
return xml
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug(config,routes) # :nodoc:
|
23
|
+
routes.select{|r| !r[:name].empty?}.each do |route|
|
24
|
+
puts '%s: %s' % [route[:name], route[:path]]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Rails-portlet Java class
|
29
|
+
def portlet_class
|
30
|
+
'com.celamanzi.liferay.portlets.rails286.Rails286Portlet'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Rails-portlet Java class
|
34
|
+
def portlet_filter_class
|
35
|
+
'com.celamanzi.liferay.portlets.rails286.Rails286PortletRenderFilter'
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def header
|
41
|
+
xml = '<?xml version="1.0" encoding="UTF-8"?>'
|
42
|
+
xml << "\n"
|
43
|
+
xml << '<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
|
44
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
45
|
+
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">'
|
46
|
+
xml << "\n\n"
|
47
|
+
return xml
|
48
|
+
end
|
49
|
+
|
50
|
+
def footer
|
51
|
+
'</portlet-app>' + "\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
# portlet.xml template.
|
55
|
+
def template(portlet)
|
56
|
+
xml = " <!-- %s -->\n" % portlet[:title]
|
57
|
+
xml << " <portlet>\n"
|
58
|
+
xml << " <portlet-name>%s</portlet-name>\n" % portlet[:name]
|
59
|
+
xml << " <portlet-class>%s</portlet-class>\n" % self.portlet_class
|
60
|
+
xml << " <supports>\n"
|
61
|
+
xml << " <mime-type>text/html</mime-type>\n"
|
62
|
+
xml << " <portlet-mode>view</portlet-mode>\n"
|
63
|
+
### edit mode
|
64
|
+
xml << " <portlet-mode>edit</portlet-mode>\n" if portlet[:edit]==true
|
65
|
+
xml << " </supports>\n"
|
66
|
+
xml << " <portlet-info>\n"
|
67
|
+
xml << " <title>%s</title>\n" % portlet[:title]
|
68
|
+
xml << " </portlet-info>\n"
|
69
|
+
xml << " </portlet>\n"
|
70
|
+
xml << ""
|
71
|
+
xml << " <filter>\n"
|
72
|
+
xml << " <filter-name>%s_filter</filter-name>\n" % portlet[:name]
|
73
|
+
xml << " <filter-class>%s</filter-class>\n" % self.portlet_filter_class
|
74
|
+
xml << " <lifecycle>RENDER_PHASE</lifecycle>\n"
|
75
|
+
xml << " <init-param>\n"
|
76
|
+
xml << " <name>host</name>\n"
|
77
|
+
xml << " <value>%s</value>\n" % portlet[:host] || ""
|
78
|
+
xml << " </init-param>\n"
|
79
|
+
xml << " <init-param>\n"
|
80
|
+
xml << " <name>servlet</name>\n"
|
81
|
+
xml << " <value>%s</value>\n" % portlet[:servlet]
|
82
|
+
xml << " </init-param>\n"
|
83
|
+
xml << " <init-param>\n"
|
84
|
+
xml << " <name>route</name>\n"
|
85
|
+
xml << " <value>%s</value>\n" % portlet[:path].gsub(/&/,"&")
|
86
|
+
xml << " </init-param>\n"
|
87
|
+
xml << " </filter>\n"
|
88
|
+
xml << ""
|
89
|
+
xml << " <filter-mapping>\n"
|
90
|
+
xml << " <filter-name>%s_filter</filter-name>\n" % portlet[:name]
|
91
|
+
xml << " <portlet-name>%s</portlet-name>\n" % portlet[:name]
|
92
|
+
xml << " </filter-mapping>\n"
|
93
|
+
xml << "\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
end # static methods
|
97
|
+
end
|
98
|
+
end
|