caterpillar 1.4.4 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/ChangeLog +27 -0
- data/MIT-LICENSE +0 -4
- data/README.rdoc +3 -3
- data/Rakefile +6 -22
- data/caterpillar.gemspec +28 -0
- data/generators/caterpillar/caterpillar_generator.rb +12 -4
- data/generators/caterpillar/templates/config/portlets.rb +10 -2
- data/generators/caterpillar/templates/stylesheets/portlet_test_bench/main.css +2 -2
- data/init.rb +20 -8
- data/lib/caterpillar.rb +33 -27
- data/lib/caterpillar/config.rb +3 -1
- data/lib/caterpillar/helpers/liferay.rb +129 -132
- data/lib/caterpillar/helpers/portlet.rb +41 -0
- data/lib/caterpillar/liferay.rb +2 -3
- data/lib/caterpillar/navigation.rb +2 -4
- data/lib/caterpillar/parser.rb +1 -5
- data/lib/caterpillar/portlet.rb +20 -19
- data/lib/caterpillar/portlet_support.rb +9 -22
- data/lib/caterpillar/security.rb +20 -42
- data/lib/caterpillar/task.rb +17 -36
- data/lib/caterpillar/usage.rb +14 -10
- data/lib/caterpillar/util.rb +1 -1
- data/lib/java/rails-portlet-0.12.0.jar +0 -0
- data/lib/rails_gem_chooser.rb +14 -7
- data/lib/web/portlet.rb +1 -1
- data/portlet_test_bench/controllers/caterpillar/application.rb +0 -5
- data/portlet_test_bench/controllers/caterpillar/junit_controller.rb +30 -9
- data/portlet_test_bench/controllers/caterpillar/liferay_controller.rb +13 -3
- data/portlet_test_bench/controllers/caterpillar/session_controller.rb +3 -0
- data/portlet_test_bench/controllers/caterpillar/xhr_controller.rb +12 -1
- data/portlet_test_bench/views/caterpillar/application/_back_to_menu.html.erb +1 -1
- data/portlet_test_bench/views/caterpillar/application/index.html.erb +8 -1
- data/portlet_test_bench/views/caterpillar/application/portlet_test_bench.html.erb +26 -8
- data/portlet_test_bench/views/caterpillar/css/background.html.erb +4 -1
- data/portlet_test_bench/views/caterpillar/css/simple.html.erb +13 -9
- data/portlet_test_bench/views/caterpillar/liferay/session_variables.html.erb +3 -12
- data/portlet_test_bench/views/caterpillar/session/cookies.html.erb +5 -0
- data/portlet_test_bench/views/caterpillar/session/namespace.html.erb +14 -0
- data/portlet_test_bench/views/caterpillar/xhr/resource.html.erb +15 -0
- data/portlet_test_bench/views/caterpillar/xhr/time.html.erb +7 -6
- data/spec/app1/config/routes.rb +3 -0
- data/spec/app2/config/routes.rb +7 -0
- data/spec/app3/config/routes.rb +5 -0
- data/spec/caterpillar/helper_spec.rb +69 -0
- data/spec/caterpillar/task_spec.rb +192 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +44 -0
- data/test/README +4 -0
- data/test/dtd/liferay-display_5_1_0.dtd +44 -44
- data/test/dtd/liferay-display_5_2_0.dtd +44 -44
- data/test/dtd/liferay-display_6_0_0.dtd +44 -44
- data/test/dtd/liferay-portlet-app_5_1_0.dtd +582 -582
- data/test/dtd/liferay-portlet-app_5_2_0.dtd +642 -642
- data/test/dtd/liferay-portlet-app_6_0_0.dtd +730 -730
- data/test/dtd/portlet-app_2_0.xsd +830 -830
- data/test/liferay_helpers_test.rb +94 -7
- data/test/portlet_support_test.rb +0 -18
- data/test/portlets_test.rb +6 -11
- data/test/xml_test.rb +4 -14
- metadata +53 -31
- data/lib/java/rails-portlet-0.10.1.jar +0 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
<% if @resource_url %>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<%= link_to 'resource url (with params)', liferay_resource_url(@r_u_params) %>
|
5
|
+
stored to cookie:<br />
|
6
|
+
<span style="font-size:1.2em; text-style: italic;">
|
7
|
+
<%= @resource_url %>
|
8
|
+
</span>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<% else %>
|
12
|
+
|
13
|
+
Sorry, the cookie "Liferay_resourceUrl" is not set.
|
14
|
+
|
15
|
+
<% end %>
|
@@ -1,15 +1,14 @@
|
|
1
1
|
<% if @javascripts.include? 'prototype' %>
|
2
2
|
<h2>Prototype</h2>
|
3
3
|
<p>
|
4
|
-
<%= link_to_remote 'What time it
|
4
|
+
<%= link_to_remote 'What time is it?', :update => 'result', :url => {:action => 'get_time'} %>
|
5
5
|
</p>
|
6
|
-
<div id="result1"></div>
|
7
6
|
|
8
7
|
|
9
8
|
<% else %>
|
10
9
|
<h2>jQuery</h2>
|
11
10
|
<p>
|
12
|
-
<a href="#" id="jq_link">What time it
|
11
|
+
<a href="#" id="jq_link">What time is it?</a>
|
13
12
|
<script type="text/javascript">
|
14
13
|
$('#jq_link').click(function(){
|
15
14
|
$.ajax({
|
@@ -17,13 +16,15 @@
|
|
17
16
|
type: 'POST',
|
18
17
|
data: '',
|
19
18
|
success: function(data) {
|
20
|
-
$('#
|
19
|
+
$('#result').html(data);
|
21
20
|
}
|
22
21
|
});
|
23
22
|
});
|
24
23
|
</script>
|
25
24
|
</p>
|
26
|
-
|
25
|
+
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<div id="result"></div>
|
27
29
|
|
28
30
|
|
29
|
-
<% end %>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2010 Mikael Lammentausta
|
4
|
+
# This source code is available under the MIT license.
|
5
|
+
# See the file LICENSE.txt for details.
|
6
|
+
#++
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
9
|
+
require File.join(File.dirname(__FILE__),'..','..','lib','caterpillar')
|
10
|
+
require File.join(File.dirname(__FILE__),'..','..','lib','caterpillar','helpers','portlet')
|
11
|
+
require File.join(File.dirname(__FILE__),'..','..','lib','caterpillar','helpers','liferay')
|
12
|
+
|
13
|
+
class MockController
|
14
|
+
include Caterpillar::Helpers::Liferay
|
15
|
+
attr_accessor :cookies
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
describe Caterpillar::Task do
|
20
|
+
|
21
|
+
before(:all) do
|
22
|
+
end
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
@rake = Rake::Application.new
|
26
|
+
Rake.application = @rake
|
27
|
+
verbose(false)
|
28
|
+
@task = Caterpillar::Task.new
|
29
|
+
end
|
30
|
+
|
31
|
+
after(:each) do
|
32
|
+
@task = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should form resource url" do
|
36
|
+
mock = MockController.new
|
37
|
+
resource_url = 'http://liferay-resource.url:80/'
|
38
|
+
mock.cookies = {
|
39
|
+
:Liferay_resourceUrl => resource_url,
|
40
|
+
:Portlet_namespace => '_test_'
|
41
|
+
}
|
42
|
+
|
43
|
+
=begin
|
44
|
+
params = {:route => '/MockController/index'}
|
45
|
+
given_params = params.dup
|
46
|
+
url = mock.liferay_resource_url(params)
|
47
|
+
params.should == given_params # original params should not change
|
48
|
+
url.should == resource_url + '&__test__railsRoute=/MockController/index'
|
49
|
+
=end
|
50
|
+
|
51
|
+
# test action
|
52
|
+
params = {:route => '/MockController/moo_action'}
|
53
|
+
#params.update(:action => 'moo_action')
|
54
|
+
given_params = params.dup
|
55
|
+
url = mock.liferay_resource_url(params)
|
56
|
+
params.should == given_params # original params should not change
|
57
|
+
url.should == resource_url + '?__test__railsRoute=/MockController/moo_action'
|
58
|
+
|
59
|
+
# test extra keys
|
60
|
+
params.update(:foo => :bar, :baz => 3)
|
61
|
+
given_params = params.dup
|
62
|
+
url = mock.liferay_resource_url(params)
|
63
|
+
params.should == given_params # original params should not change
|
64
|
+
url.should == resource_url + '?__test__railsRoute=/MockController/moo_action&__test__foo=bar&__test__baz=3'
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2010 Mikael Lammentausta
|
4
|
+
# This source code is available under the MIT license.
|
5
|
+
# See the file LICENSE.txt for details.
|
6
|
+
#++
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
9
|
+
require File.join(File.dirname(__FILE__),'..','..','lib','caterpillar')
|
10
|
+
require 'tmpdir'
|
11
|
+
|
12
|
+
describe Caterpillar::Task do
|
13
|
+
|
14
|
+
before(:all) do
|
15
|
+
@liferay_xml_dir = File.dirname(File.expand_path(__FILE__)) + '/../xml'
|
16
|
+
end
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
@rake = Rake::Application.new
|
20
|
+
Rake.application = @rake
|
21
|
+
verbose(false)
|
22
|
+
@task = Caterpillar::Task.new
|
23
|
+
@pwd = Dir.pwd
|
24
|
+
@tmpdir = Dir.tmpdir + '/caterpillar'
|
25
|
+
Dir.mkdir(@tmpdir) unless File.exists?(@tmpdir)
|
26
|
+
end
|
27
|
+
|
28
|
+
after(:each) do
|
29
|
+
@task = nil
|
30
|
+
FileUtils.rm_rf @tmpdir
|
31
|
+
Dir.chdir @pwd
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should print version" do
|
35
|
+
capture { Rake::Task["version"].invoke }.should =~ /Caterpillar #{Caterpillar::VERSION}/
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should create a conf file" do
|
39
|
+
Dir.chdir(@tmpdir)
|
40
|
+
fn = "portlets-config.rb"
|
41
|
+
File.exist?(fn).should == false
|
42
|
+
silence { Rake::Task["generate"].invoke }
|
43
|
+
File.exist?(fn).should == true
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should print no route" do
|
47
|
+
portlet = {
|
48
|
+
:name => 'portlet_test_bench',
|
49
|
+
}
|
50
|
+
@task.config.instances << portlet
|
51
|
+
capture { Rake::Task["portlets"].invoke }.should =~ /no route for portlet_test_bench/
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should print routes" do
|
55
|
+
portlet = {
|
56
|
+
:name => 'portlet_test_bench',
|
57
|
+
}
|
58
|
+
@task.config.instances << portlet
|
59
|
+
@task.config.rails_root = File.join(File.dirname(__FILE__),'..','app1')
|
60
|
+
capture { Rake::Task["portlets"].invoke }.should =~ /\/caterpillar\/test_bench/
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should parse routes without config.rails_root" do
|
64
|
+
portlet = {
|
65
|
+
:name => 'portlet_test_bench',
|
66
|
+
:rails_root => File.join(File.dirname(__FILE__),'..','app1')
|
67
|
+
}
|
68
|
+
@task.config.instances << portlet
|
69
|
+
capture { Rake::Task["portlets"].invoke }.should =~ /\/caterpillar\/test_bench/
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should parse routes" do
|
73
|
+
config = Caterpillar::Config.new
|
74
|
+
config.rails_root = File.join(File.dirname(__FILE__),'..','app3')
|
75
|
+
config.instances = [
|
76
|
+
{
|
77
|
+
:name => 'portlet_test_bench',
|
78
|
+
:title => 'Rails-portlet test bench',
|
79
|
+
:category => 'Caterpillar',
|
80
|
+
:rails_root => File.join(File.dirname(__FILE__),'..','app1')
|
81
|
+
},
|
82
|
+
{
|
83
|
+
:name => 'hungry_bear',
|
84
|
+
:rails_root => File.join(File.dirname(__FILE__),'..','app2')
|
85
|
+
},
|
86
|
+
{
|
87
|
+
:name => 'adorabe_otters'
|
88
|
+
}
|
89
|
+
]
|
90
|
+
routes = Caterpillar::Util.parse_routes(config)
|
91
|
+
routes.size.should == 6 # "/caterpillar" and "/test_bench" twice
|
92
|
+
|
93
|
+
paths = routes.map {|r| r[:path]}
|
94
|
+
paths.each do |path|
|
95
|
+
route = routes.select {|r| r[:path] == path }.first
|
96
|
+
|
97
|
+
case path
|
98
|
+
when '/caterpillar'
|
99
|
+
route[:reqs][:controller].should == 'Caterpillar::Application'
|
100
|
+
route[:reqs][:action].should == 'index'
|
101
|
+
|
102
|
+
when '/caterpillar/test_bench'
|
103
|
+
route[:reqs][:controller].should == 'Caterpillar::Application'
|
104
|
+
route[:reqs][:action].should == 'portlet_test_bench'
|
105
|
+
|
106
|
+
when '/bear/hungry'
|
107
|
+
route[:reqs][:controller].should == 'Bear'
|
108
|
+
route[:reqs][:action].should == 'hungry'
|
109
|
+
|
110
|
+
when '/otters/adorable'
|
111
|
+
route[:reqs][:controller].should == 'Otter'
|
112
|
+
route[:reqs][:action].should == 'adorable'
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should define Tomcat WEB-INF location" do
|
119
|
+
container_root = @tmpdir
|
120
|
+
|
121
|
+
@task.config.container = Caterpillar::Liferay
|
122
|
+
@task.config.container.root = container_root
|
123
|
+
@task.config.container.root.should == container_root
|
124
|
+
|
125
|
+
@task.config.container.server = 'Tomcat'
|
126
|
+
@task.config.container.deploy_dir.should == container_root + '/webapps'
|
127
|
+
@task.config.container.WEB_INF.should == container_root + '/webapps/ROOT/WEB-INF'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should define JBoss/Tomcat WEB-INF location" do
|
131
|
+
container_root = @tmpdir
|
132
|
+
|
133
|
+
@task.config.container = Caterpillar::Liferay
|
134
|
+
@task.config.container.root = container_root
|
135
|
+
@task.config.container.root.should == container_root
|
136
|
+
|
137
|
+
@task.config.container.server = 'JBoss/Tomcat'
|
138
|
+
# no server_dir!
|
139
|
+
lambda { @task.config.container.WEB_INF }.should raise_error(RuntimeError, /Please configure server_dir/)
|
140
|
+
|
141
|
+
@task.config.container.server_dir = 'server/default/deploy/ROOT.war'
|
142
|
+
@task.config.container.WEB_INF.should == container_root + '/server/default/deploy/ROOT.war/WEB-INF'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should make XML" do
|
146
|
+
portlet = {:name => 'portlet_test_bench'}
|
147
|
+
@task.config.instances << portlet
|
148
|
+
@task.config.session_secret = {:key => 'test', :secret => 'test_secret'}
|
149
|
+
|
150
|
+
Dir.chdir(@tmpdir)
|
151
|
+
Dir.glob('*.xml').size.should == 0
|
152
|
+
|
153
|
+
silence { Rake::Task["makexml"].invoke }
|
154
|
+
|
155
|
+
File.exists?('portlet-ext.xml').should == true
|
156
|
+
File.exists?('liferay-portlet-ext.xml').should == true
|
157
|
+
File.exists?('liferay-display.xml').should == true
|
158
|
+
File.size('portlet-ext.xml').should > 0
|
159
|
+
File.size('liferay-portlet-ext.xml').should > 0
|
160
|
+
File.size('liferay-display.xml').should > 0
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should deploy XML on Tomcat" do
|
164
|
+
portlet = {:name => 'portlet_test_bench'}
|
165
|
+
@task.config.instances << portlet
|
166
|
+
@task.config.session_secret = {:key => 'test', :secret => 'test_secret'}
|
167
|
+
|
168
|
+
container_root = @tmpdir
|
169
|
+
@task.config.container.root = container_root
|
170
|
+
@task.config.container.server = 'Tomcat'
|
171
|
+
|
172
|
+
web_inf = container_root + '/webapps/ROOT/WEB-INF'
|
173
|
+
@task.config.container.WEB_INF.should == web_inf
|
174
|
+
|
175
|
+
File.exists?(web_inf).should == false
|
176
|
+
FileUtils.cp_r(
|
177
|
+
@liferay_xml_dir + '/liferay-portal-5.2.3/tomcat-6.0.18/webapps',
|
178
|
+
container_root)
|
179
|
+
File.exists?(web_inf).should == true
|
180
|
+
|
181
|
+
silence { Rake::Task["deploy:xml"].invoke }
|
182
|
+
|
183
|
+
Dir.chdir(web_inf)
|
184
|
+
File.exists?('portlet-ext.xml').should == true
|
185
|
+
File.exists?('liferay-portlet-ext.xml').should == true
|
186
|
+
File.exists?('liferay-display.xml').should == true
|
187
|
+
File.size('portlet-ext.xml').should > 0
|
188
|
+
File.size('liferay-portlet-ext.xml').should > 0
|
189
|
+
File.size('liferay-display.xml').should > 0
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2010 Engine Yard, Inc.
|
4
|
+
# Copyright (c) 2007-2009 Sun Microsystems, Inc.
|
5
|
+
# This source code is available under the MIT license.
|
6
|
+
# See the file LICENSE.txt for details.
|
7
|
+
#++
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'spec'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
13
|
+
|
14
|
+
raise %{Error: detected running specs in a Rails app;
|
15
|
+
Caterpillar specs are destructive to application directories.} if File.directory?("app")
|
16
|
+
|
17
|
+
def silence(io = nil)
|
18
|
+
require 'stringio'
|
19
|
+
old_stdout = $stdout
|
20
|
+
old_stderr = $stderr
|
21
|
+
$stdout = io || StringIO.new
|
22
|
+
$stderr = io || StringIO.new
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
$stdout = old_stdout
|
26
|
+
$stderr = old_stderr
|
27
|
+
end
|
28
|
+
|
29
|
+
def capture(&block)
|
30
|
+
require 'stringio'
|
31
|
+
io = StringIO.new
|
32
|
+
silence(io, &block)
|
33
|
+
io.string
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Runner.configure do |config|
|
37
|
+
config.after(:each) do
|
38
|
+
class << Object
|
39
|
+
public :remove_const
|
40
|
+
end
|
41
|
+
Object.remove_const("Rails") rescue nil
|
42
|
+
rm_rf "vendor"
|
43
|
+
end
|
44
|
+
end
|
data/test/README
ADDED
@@ -1,45 +1,45 @@
|
|
1
|
-
<!--
|
2
|
-
This is the DTD for the Display parameters for Liferay Portal.
|
3
|
-
|
4
|
-
<!DOCTYPE display PUBLIC
|
5
|
-
"-//Liferay//DTD Display 5.1.0//EN"
|
6
|
-
"http://www.liferay.com/dtd/liferay-display_5_1_0.dtd">
|
7
|
-
-->
|
8
|
-
|
9
|
-
<!--
|
10
|
-
The display element is the root of the deployment descriptor that describes how
|
11
|
-
portlets are categorized and displayed for users to choose when personalizing a
|
12
|
-
page in Liferay Portal.
|
13
|
-
-->
|
14
|
-
<!ELEMENT display (category*)>
|
15
|
-
|
16
|
-
<!--
|
17
|
-
The category element organizes a set of portlets. A portlet can exist in more
|
18
|
-
than one category.
|
19
|
-
-->
|
20
|
-
<!ELEMENT category (category*, portlet*)>
|
21
|
-
|
22
|
-
<!--
|
23
|
-
The name of a category is mapped to the portal's Language properties. If the
|
24
|
-
category name is "test", then the key in the portal's resource bundle will be
|
25
|
-
"category.test".
|
26
|
-
|
27
|
-
See:
|
28
|
-
|
29
|
-
http://www.liferay.com/page/guest/documentation/development/languages
|
30
|
-
-->
|
31
|
-
<!ATTLIST category
|
32
|
-
name CDATA #REQUIRED
|
33
|
-
>
|
34
|
-
|
35
|
-
<!--
|
36
|
-
The portlet element represents a portlet.
|
37
|
-
-->
|
38
|
-
<!ELEMENT portlet (#PCDATA)>
|
39
|
-
|
40
|
-
<!--
|
41
|
-
The id must match the unique portlet-name specified in portlet.xml.
|
42
|
-
-->
|
43
|
-
<!ATTLIST portlet
|
44
|
-
id CDATA #REQUIRED
|
1
|
+
<!--
|
2
|
+
This is the DTD for the Display parameters for Liferay Portal.
|
3
|
+
|
4
|
+
<!DOCTYPE display PUBLIC
|
5
|
+
"-//Liferay//DTD Display 5.1.0//EN"
|
6
|
+
"http://www.liferay.com/dtd/liferay-display_5_1_0.dtd">
|
7
|
+
-->
|
8
|
+
|
9
|
+
<!--
|
10
|
+
The display element is the root of the deployment descriptor that describes how
|
11
|
+
portlets are categorized and displayed for users to choose when personalizing a
|
12
|
+
page in Liferay Portal.
|
13
|
+
-->
|
14
|
+
<!ELEMENT display (category*)>
|
15
|
+
|
16
|
+
<!--
|
17
|
+
The category element organizes a set of portlets. A portlet can exist in more
|
18
|
+
than one category.
|
19
|
+
-->
|
20
|
+
<!ELEMENT category (category*, portlet*)>
|
21
|
+
|
22
|
+
<!--
|
23
|
+
The name of a category is mapped to the portal's Language properties. If the
|
24
|
+
category name is "test", then the key in the portal's resource bundle will be
|
25
|
+
"category.test".
|
26
|
+
|
27
|
+
See:
|
28
|
+
|
29
|
+
http://www.liferay.com/page/guest/documentation/development/languages
|
30
|
+
-->
|
31
|
+
<!ATTLIST category
|
32
|
+
name CDATA #REQUIRED
|
33
|
+
>
|
34
|
+
|
35
|
+
<!--
|
36
|
+
The portlet element represents a portlet.
|
37
|
+
-->
|
38
|
+
<!ELEMENT portlet (#PCDATA)>
|
39
|
+
|
40
|
+
<!--
|
41
|
+
The id must match the unique portlet-name specified in portlet.xml.
|
42
|
+
-->
|
43
|
+
<!ATTLIST portlet
|
44
|
+
id CDATA #REQUIRED
|
45
45
|
>
|