zenweb 2.18.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +426 -0
- data/Manifest.txt +54 -0
- data/README.txt +63 -0
- data/Rakefile +22 -0
- data/bin/zenweb +27 -0
- data/bin/zenwebpage +66 -0
- data/bin/zenwebsite +39 -0
- data/design/REQUIREMENTS.txt +52 -0
- data/design/ZENWEB_2.txt +69 -0
- data/design/heirarchy.png +0 -0
- data/design/heirarchy.tgif +311 -0
- data/docs/Customizing +76 -0
- data/docs/FAQ +12 -0
- data/docs/Features +128 -0
- data/docs/Presentation +88 -0
- data/docs/QuickStart +32 -0
- data/docs/Renderers +85 -0
- data/docs/SiteMap +13 -0
- data/docs/YourOwnWebsite +32 -0
- data/docs/index +14 -0
- data/docs/metadata.txt +10 -0
- data/lib/ZenWeb.rb +850 -0
- data/lib/ZenWeb/CalendarRenderer.rb +162 -0
- data/lib/ZenWeb/CompactRenderer.rb +45 -0
- data/lib/ZenWeb/CompositeRenderer.rb +63 -0
- data/lib/ZenWeb/FileAttachmentRenderer.rb +57 -0
- data/lib/ZenWeb/FooterRenderer.rb +38 -0
- data/lib/ZenWeb/GenericRenderer.rb +143 -0
- data/lib/ZenWeb/HeaderRenderer.rb +52 -0
- data/lib/ZenWeb/HtmlRenderer.rb +81 -0
- data/lib/ZenWeb/HtmlTableRenderer.rb +94 -0
- data/lib/ZenWeb/HtmlTemplateRenderer.rb +173 -0
- data/lib/ZenWeb/MetadataRenderer.rb +83 -0
- data/lib/ZenWeb/RelativeRenderer.rb +98 -0
- data/lib/ZenWeb/RubyCodeRenderer.rb +56 -0
- data/lib/ZenWeb/SitemapRenderer.rb +56 -0
- data/lib/ZenWeb/StandardRenderer.rb +40 -0
- data/lib/ZenWeb/StupidRenderer.rb +88 -0
- data/lib/ZenWeb/SubpageRenderer.rb +45 -0
- data/lib/ZenWeb/TextToHtmlRenderer.rb +219 -0
- data/lib/ZenWeb/TocRenderer.rb +61 -0
- data/lib/ZenWeb/XXXRenderer.rb +32 -0
- data/test/SiteMap +14 -0
- data/test/Something +4 -0
- data/test/include.txt +3 -0
- data/test/index +8 -0
- data/test/metadata.txt +10 -0
- data/test/ryand/SiteMap +10 -0
- data/test/ryand/blah +4 -0
- data/test/ryand/blah-blah +4 -0
- data/test/ryand/index +52 -0
- data/test/ryand/metadata.txt +2 -0
- data/test/ryand/stuff/index +4 -0
- data/test/test_zenweb.rb +1624 -0
- metadata +161 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'ZenWeb/GenericRenderer'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
= Class HeaderRenderer
|
6
|
+
|
7
|
+
Inserts a header based on metadata.
|
8
|
+
|
9
|
+
=== Methods
|
10
|
+
|
11
|
+
=end
|
12
|
+
|
13
|
+
class HeaderRenderer < GenericRenderer
|
14
|
+
|
15
|
+
=begin
|
16
|
+
|
17
|
+
--- HeaderRenderer#render(content)
|
18
|
+
|
19
|
+
Adds a header if the ((|header|)) metadata item exists. If the
|
20
|
+
document contains a BODY HTML tag, then the header immediately
|
21
|
+
follows it, otherwise it is simply at the top.
|
22
|
+
|
23
|
+
=end
|
24
|
+
|
25
|
+
def render(content)
|
26
|
+
|
27
|
+
header = @document['header'] || nil
|
28
|
+
|
29
|
+
if header then
|
30
|
+
placed = false
|
31
|
+
|
32
|
+
content.each { | line |
|
33
|
+
|
34
|
+
push(line)
|
35
|
+
|
36
|
+
if (line =~ /<BODY/i) then
|
37
|
+
push(header)
|
38
|
+
placed = true
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
42
|
+
unless placed then
|
43
|
+
unshift(header) unless placed
|
44
|
+
end
|
45
|
+
else
|
46
|
+
push(content)
|
47
|
+
end
|
48
|
+
|
49
|
+
return self.result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'ZenWeb/GenericRenderer'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
= Class HtmlRenderer
|
6
|
+
|
7
|
+
Abstract superclass that provides common functionality for those
|
8
|
+
renderers that produce HTML.
|
9
|
+
|
10
|
+
=== Methods
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
class HtmlRenderer < GenericRenderer
|
15
|
+
|
16
|
+
=begin
|
17
|
+
|
18
|
+
--- HtmlRenderer#render(content)
|
19
|
+
|
20
|
+
Raises an exception. This is subclass responsibility as this is an
|
21
|
+
abstract class.
|
22
|
+
|
23
|
+
=end
|
24
|
+
|
25
|
+
def render(content)
|
26
|
+
raise "Subclass Responsibility"
|
27
|
+
end
|
28
|
+
|
29
|
+
=begin
|
30
|
+
|
31
|
+
--- HtmlRenderer#array2html
|
32
|
+
|
33
|
+
Converts an array (of arrays, potentially) into an unordered list.
|
34
|
+
|
35
|
+
=end
|
36
|
+
|
37
|
+
def array2html(list, ordered=false, indent=0)
|
38
|
+
result = []
|
39
|
+
|
40
|
+
indent1 = " " * indent
|
41
|
+
indent2 = " " * (indent + 1)
|
42
|
+
|
43
|
+
tag = ordered ? "OL" : "UL"
|
44
|
+
|
45
|
+
result << "#{indent1}<#{tag}>\n"
|
46
|
+
list.each { | l |
|
47
|
+
if (l.is_a?(Array)) then
|
48
|
+
x = result.pop
|
49
|
+
result.push "\n"
|
50
|
+
result.push self.array2html(l, ordered, indent+2)
|
51
|
+
result.push indent2 unless x =~ /^\s+/
|
52
|
+
result.push x
|
53
|
+
else
|
54
|
+
result.push(indent2, "<LI>", l.to_s, "</LI>\n")
|
55
|
+
end
|
56
|
+
}
|
57
|
+
result << "#{indent1}</#{tag}>\n"
|
58
|
+
|
59
|
+
return result.join
|
60
|
+
end
|
61
|
+
|
62
|
+
def hash2html(hash, order)
|
63
|
+
result = ""
|
64
|
+
|
65
|
+
if (hash) then
|
66
|
+
result += "<DL>\n"
|
67
|
+
order.each { | key |
|
68
|
+
val = hash[key] or raise "Key '#{key}' is missing!"
|
69
|
+
result += " <DT>#{key}</DT>\n"
|
70
|
+
result += " <DD>#{val}</DD>\n\n"
|
71
|
+
}
|
72
|
+
result += "</DL>\n"
|
73
|
+
else
|
74
|
+
result = "not a hash"
|
75
|
+
end
|
76
|
+
|
77
|
+
return result
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'ZenWeb/GenericRenderer'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def %(style)
|
5
|
+
style.gsub(/%([-\d]*)\(([^\)]+)\)/) do |match|
|
6
|
+
result = ''
|
7
|
+
key = $2.intern
|
8
|
+
if self.has_key?(key) then
|
9
|
+
result = $1 ? sprintf("%*s", $1.to_i, self[key]) : self[key]
|
10
|
+
else
|
11
|
+
$stderr.puts " WARNING: missing data for '#{key}' in #{self.inspect}" unless $TESTING
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
=begin
|
19
|
+
|
20
|
+
= Class HtmlTableRenderer
|
21
|
+
|
22
|
+
Converts tab delimited data (tagged with <tabs>) into HTML tables
|
23
|
+
|
24
|
+
=== Methods
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
class HtmlTableRenderer < GenericRenderer
|
29
|
+
|
30
|
+
=begin
|
31
|
+
|
32
|
+
--- HtmlTableRenderer#render(content)
|
33
|
+
|
34
|
+
Converts tab delimited data (tagged with <tabs>) into HTML tables
|
35
|
+
|
36
|
+
=end
|
37
|
+
|
38
|
+
def render(content)
|
39
|
+
|
40
|
+
first=true
|
41
|
+
pre_style = post_style = head_style = body_style = column_titles = nil
|
42
|
+
|
43
|
+
self.scan_region(content, /^<tabs/i, /^<\/tabs>/i) do |line, context|
|
44
|
+
line.chomp!
|
45
|
+
case context
|
46
|
+
when :START then
|
47
|
+
first = true
|
48
|
+
if line =~ /style\s*=\s*\"?([\w\.-]+)\"?/i then
|
49
|
+
pre_style = @document["style_#{$1}_pre"] || ''
|
50
|
+
post_style = @document["style_#{$1}_post"] || ''
|
51
|
+
head_style = @document["style_#{$1}_head"] || ''
|
52
|
+
body_style = @document["style_#{$1}"] or
|
53
|
+
raise "You must specify a metadata entry for 'style_#{$1}'"
|
54
|
+
else
|
55
|
+
pre_style = "<table border=\"0\">\n"
|
56
|
+
post_style = "</table>\n"
|
57
|
+
head_style = nil
|
58
|
+
body_style = nil
|
59
|
+
column_titles = nil
|
60
|
+
end
|
61
|
+
line = pre_style
|
62
|
+
when :END then
|
63
|
+
line = post_style
|
64
|
+
else
|
65
|
+
columns = line.split(/\t+/)
|
66
|
+
|
67
|
+
if body_style then
|
68
|
+
if first then
|
69
|
+
line = head_style
|
70
|
+
# map the first row of data to column title positions
|
71
|
+
column_titles = columns.map {|x| x.intern}
|
72
|
+
else
|
73
|
+
data = {}
|
74
|
+
# use the column title positions to extract the data from the table
|
75
|
+
column_titles.each_with_index do |title, index|
|
76
|
+
data[title] = columns[index]
|
77
|
+
end
|
78
|
+
|
79
|
+
# use our extension to hash (see above) to format the data
|
80
|
+
line = data % body_style
|
81
|
+
end
|
82
|
+
else
|
83
|
+
type = first ? "th" : "td"
|
84
|
+
line = "<tr><#{type}>#{columns.join "</#{type}><#{type}>"}</#{type}></tr>\n"
|
85
|
+
end
|
86
|
+
first = false if first
|
87
|
+
end
|
88
|
+
push line
|
89
|
+
end
|
90
|
+
|
91
|
+
return self.result
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'ZenWeb/HtmlRenderer'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
= Class HtmlTemplateRenderer
|
6
|
+
|
7
|
+
Generates a consistant HTML page header and footer, including a
|
8
|
+
navigation bar, title, subtitle, and appropriate META tags.
|
9
|
+
|
10
|
+
=== Methods
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
class HtmlTemplateRenderer < HtmlRenderer
|
15
|
+
|
16
|
+
=begin
|
17
|
+
|
18
|
+
--- HtmlTemplateRenderer#render(content)
|
19
|
+
|
20
|
+
Renders a standardized HTML header and footer. This currently also
|
21
|
+
includes a navigation bar and a list of subpages, which will
|
22
|
+
probably be broken out to their own renderers soon.
|
23
|
+
|
24
|
+
Metadata variables used:
|
25
|
+
|
26
|
+
+ author
|
27
|
+
+ banner - graphic at the top of the page, usually a logo
|
28
|
+
+ bgcolor - defaults to not being defined
|
29
|
+
+ copyright
|
30
|
+
+ description
|
31
|
+
+ dtd (default: 'DTD HTML 4.0')
|
32
|
+
+ email - used in a mailto in metadata
|
33
|
+
+ keywords
|
34
|
+
+ rating (default: 'general')
|
35
|
+
+ stylesheet - reference to a CSS file
|
36
|
+
+ style - CSS code directly (for smaller snippets)
|
37
|
+
+ subtitle
|
38
|
+
+ title (default: 'Unknown Title')
|
39
|
+
+ icbm - longitude and latitude for geourl.org
|
40
|
+
+ icbm_title - defaults to the page title
|
41
|
+
|
42
|
+
=end
|
43
|
+
|
44
|
+
def render(content)
|
45
|
+
author = @document['author']
|
46
|
+
banner = @document['banner']
|
47
|
+
bgcolor = @document['bgcolor']
|
48
|
+
dtd = @document['dtd'] || 'DTD HTML 4.0'
|
49
|
+
copyright = @document['copyright']
|
50
|
+
description = @document['description']
|
51
|
+
email = @document['email']
|
52
|
+
keywords = @document['keywords']
|
53
|
+
rating = @document['rating'] || 'general'
|
54
|
+
stylesheet = @document['stylesheet']
|
55
|
+
subtitle = @document['subtitle']
|
56
|
+
title = @document['title'] || 'Unknown Title'
|
57
|
+
icbm = @document['icbm']
|
58
|
+
icbm_title = @document['icbm_title'] || @document['title']
|
59
|
+
charset = @document['charset']
|
60
|
+
style = @document['style']
|
61
|
+
naked_page = @document['naked_page']
|
62
|
+
head_extra = @document['head_extra'] || []
|
63
|
+
|
64
|
+
titletext = @document.fulltitle
|
65
|
+
|
66
|
+
# TODO: iterate over a list of metas and add them in one nicely organized block
|
67
|
+
|
68
|
+
if bgcolor then
|
69
|
+
style ||= ""
|
70
|
+
style = "body { background-color: #{bgcolor} }\n" + style
|
71
|
+
end
|
72
|
+
|
73
|
+
# header
|
74
|
+
push([
|
75
|
+
"<!DOCTYPE HTML PUBLIC \"-//W3C//#{dtd}//EN\">\n",
|
76
|
+
"<HTML>\n",
|
77
|
+
"<HEAD>\n",
|
78
|
+
"<TITLE>#{titletext}</TITLE>\n",
|
79
|
+
email ? "<LINK REV=\"MADE\" HREF=\"#{email}\">\n" : [],
|
80
|
+
stylesheet ? "<LINK REL=\"STYLESHEET\" HREF=\"#{stylesheet}\" TYPE=\"text/css\" title=\"#{stylesheet}\">\n" : [],
|
81
|
+
"<META NAME=\"rating\" CONTENT=\"#{rating}\">\n",
|
82
|
+
"<META NAME=\"GENERATOR\" CONTENT=\"#{ZenWebsite.banner}\">\n",
|
83
|
+
style ? "<STYLE TYPE=\"text/css\">\n#{style}\n</STYLE>" : [],
|
84
|
+
author ? "<META NAME=\"author\" CONTENT=\"#{author}\">\n" : [],
|
85
|
+
copyright ? "<META NAME=\"copyright\" CONTENT=\"#{copyright}\">\n" : [],
|
86
|
+
keywords ? "<META NAME=\"keywords\" CONTENT=\"#{keywords}\">\n" : [],
|
87
|
+
description ? "<META NAME=\"description\" CONTENT=\"#{description}\">\n" : [],
|
88
|
+
charset ? "<META HTTP-EQUIV=\"content-type\" CONTENT=\"text/html; charset=#{charset}\">" : [],
|
89
|
+
icbm ? "<meta name=\"ICBM\" content=\"#{icbm}\">\n<meta name=\"DC.title\" content=\"#{icbm_title}\">" : [],
|
90
|
+
|
91
|
+
"<link rel=\"up\" href=\"#{@document.parentURL}\" title=\"#{@document.parent.title}\">\n",
|
92
|
+
"<link rel=\"contents\" href=\"#{@sitemap.url}\" title=\"#{@sitemap.title}\">\n",
|
93
|
+
"<link rel=\"top\" href=\"#{@website.top.url}\" title=\"#{@website.top.title}\">\n",
|
94
|
+
|
95
|
+
head_extra.join("\n"),
|
96
|
+
|
97
|
+
"</HEAD>\n",
|
98
|
+
"<BODY>\n",
|
99
|
+
])
|
100
|
+
|
101
|
+
unless naked_page then
|
102
|
+
self.navbar
|
103
|
+
|
104
|
+
if banner then
|
105
|
+
push("<H1><IMG SRC=\"#{banner}\" ALT=\"#{File.basename banner}\"></H1>\n")
|
106
|
+
unless (subtitle) then
|
107
|
+
push("<H2>#{title}</H2>\n")
|
108
|
+
end
|
109
|
+
else
|
110
|
+
push("<H1>#{title}</H1>\n")
|
111
|
+
end
|
112
|
+
|
113
|
+
push([
|
114
|
+
subtitle ? "<H2>#{subtitle}</H2>\n" : [],
|
115
|
+
"<HR CLASS=\"thick\">\n\n",
|
116
|
+
])
|
117
|
+
end
|
118
|
+
|
119
|
+
push content
|
120
|
+
|
121
|
+
unless naked_page then
|
122
|
+
push([
|
123
|
+
"<HR CLASS=\"thick\">\n\n",
|
124
|
+
])
|
125
|
+
|
126
|
+
self.navbar
|
127
|
+
end
|
128
|
+
|
129
|
+
push("\n</BODY>\n</HTML>\n")
|
130
|
+
|
131
|
+
return self.result
|
132
|
+
end
|
133
|
+
|
134
|
+
=begin
|
135
|
+
|
136
|
+
--- HtmlTemplateRenderer#navbar
|
137
|
+
|
138
|
+
Generates a navbar that contains a link to the sitemap, search
|
139
|
+
page (if any), and a fake "breadcrumbs" trail which is really just
|
140
|
+
a list of all of the parent titles up the chain to the top.
|
141
|
+
|
142
|
+
=end
|
143
|
+
|
144
|
+
def navbar
|
145
|
+
|
146
|
+
sep = " / "
|
147
|
+
search = @website["/Search.html"]
|
148
|
+
|
149
|
+
push([
|
150
|
+
"<P class=\"navbar\">\n",
|
151
|
+
"<A HREF=\"#{@sitemap.url}\">Sitemap</A>",
|
152
|
+
search ? " | <A HREF=\"#{search.url}\"><EM>Search</EM></A>" : [],
|
153
|
+
" || ",
|
154
|
+
])
|
155
|
+
|
156
|
+
path = []
|
157
|
+
current = @document
|
158
|
+
while current and current != current.parent do
|
159
|
+
current = current.parent
|
160
|
+
path.unshift(current) if current
|
161
|
+
end
|
162
|
+
|
163
|
+
push([
|
164
|
+
path.map{|doc| ["<A HREF=\"#{doc.url}\">#{doc['title']}</A>\n", sep]},
|
165
|
+
@document['title'],
|
166
|
+
"</P>\n",
|
167
|
+
])
|
168
|
+
|
169
|
+
return []
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'ZenWeb/GenericRenderer'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
= Class MetadataRenderer
|
6
|
+
|
7
|
+
Converts all metadata references into their values.
|
8
|
+
|
9
|
+
=== Methods
|
10
|
+
|
11
|
+
=end
|
12
|
+
|
13
|
+
class MetadataRenderer < GenericRenderer
|
14
|
+
|
15
|
+
@@cache = {}
|
16
|
+
|
17
|
+
=begin
|
18
|
+
|
19
|
+
--- MetadataRenderer#render(content)
|
20
|
+
|
21
|
+
Converts all metadata references into their values.
|
22
|
+
|
23
|
+
=end
|
24
|
+
|
25
|
+
def render(content)
|
26
|
+
|
27
|
+
content = content.gsub(/\#\{([^\}]+)\}/) {
|
28
|
+
key = $1
|
29
|
+
|
30
|
+
# check to see if this is a metadata entry
|
31
|
+
val = @document[key] || nil
|
32
|
+
|
33
|
+
# otherwise try to eval it. If that fails, just give text.
|
34
|
+
unless (val) then
|
35
|
+
begin
|
36
|
+
# this allows evals that fail (expensive) to be cached,
|
37
|
+
# and good code to be eval'd every time.
|
38
|
+
# I think this is a good balance.
|
39
|
+
if @@cache[key] then
|
40
|
+
val = @@cache[key]
|
41
|
+
else
|
42
|
+
val = eval(key)
|
43
|
+
end
|
44
|
+
rescue NameError => err
|
45
|
+
val = key
|
46
|
+
@@cache[key] = key
|
47
|
+
rescue Exception => err
|
48
|
+
$stderr.puts "eval failed in MetadataRenderer for #{@document.datapath}: #{err}. Code = '#{key}'"
|
49
|
+
val = key
|
50
|
+
@@cache[key] = key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
val
|
55
|
+
}
|
56
|
+
|
57
|
+
return content
|
58
|
+
end
|
59
|
+
|
60
|
+
@@paths = {}
|
61
|
+
|
62
|
+
def include(path, remove_metadata=false, escape=false)
|
63
|
+
unless @@paths.include? path then
|
64
|
+
full_path = File.expand_path(File.join(File.dirname(@document.datapath), path))
|
65
|
+
@@paths[path] = full_path
|
66
|
+
else
|
67
|
+
full_path = @@paths[path]
|
68
|
+
end
|
69
|
+
|
70
|
+
content = File.new(full_path).readlines
|
71
|
+
|
72
|
+
if remove_metadata then
|
73
|
+
content = content.reject { |line| line =~ /^\s*\#/ }
|
74
|
+
end
|
75
|
+
|
76
|
+
content = self.render(content.join(''))
|
77
|
+
content.gsub!(/([<>&])/) { |x| '\\' + x } if escape
|
78
|
+
|
79
|
+
content
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|