zarchitect 1.0.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.
@@ -0,0 +1,34 @@
1
+ module CMD
2
+
3
+ class Update < Zarchitect
4
+
5
+ def initialize
6
+ if GPI::CLU.check_option('r')
7
+ Zarchitect.rebuild
8
+ Zarchitect.setup_html_tree
9
+ @assets = Assets.new
10
+ @assets.cpdirs
11
+ @assets.update
12
+ SCSS.run
13
+ end
14
+ @files = FileManager.new
15
+ @files.run
16
+ end
17
+
18
+ def run
19
+ Zarchitect.sconf.each { |s| Zarchitect.add_section(s) }
20
+ Zarchitect.add_section(Zarchitect.iconf)
21
+ Zarchitect.sections.sort_by! { |v| v.conf.id }
22
+ Zarchitect.sections.push Zarchitect.sections.shift
23
+ Zarchitect.sections.each do |s|
24
+ s.build_html
25
+ s.write_html
26
+ end
27
+ rss.build
28
+ end
29
+
30
+ private
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,285 @@
1
+ class Config
2
+
3
+ # Constructor
4
+ # requires yaml file
5
+ def initialize(file, key = nil)
6
+ @file = file
7
+ GPI.print "Initializing config from #{file}.", GPI::CLU.check_option('v')
8
+ @hash = Hash.new
9
+ begin
10
+ YAML.load_stream(File.open(file) { |f| f.read }) do |doc|
11
+ #key = file.sub(File.extname(file), '').sub('_config/', '')
12
+ #@hash[:key] = doc
13
+ @hash = doc
14
+ break
15
+ end
16
+ rescue StandardError
17
+ GPI.print "Failed to load #{@file}."
18
+ GPI.quit
19
+ end
20
+ unless key.nil?
21
+ @hash["key"] = key
22
+ unless @file == "_config/_index.yaml"
23
+ @hash["index"] = false
24
+ else
25
+ @hash["index"] = true
26
+ end
27
+ end
28
+ end
29
+
30
+ def has_option?(str)
31
+ @hash.has_key?(str)
32
+ end
33
+
34
+ def setup
35
+ instance_eval {
36
+ @hash.each_key do |k|
37
+ define_singleton_method k do
38
+ @hash[k]
39
+ end
40
+ end
41
+ }
42
+ end
43
+
44
+ def read(key)
45
+ if has_option?(key)
46
+ @hash[key]
47
+ else
48
+ GPI.print "Option #{key} missing in config #{@file}."
49
+ GPI.quit
50
+ end
51
+ end
52
+
53
+ def validate_post
54
+ #TODO
55
+ end
56
+
57
+ def validate
58
+ GPI.print "Validating #{@file}."
59
+ unless @hash.has_key?("hidden")
60
+ @hash["hidden"] = false
61
+ end
62
+ if @hash.has_key?("sort_type")
63
+ unless ["alphanum", "date"].include?(@hash["sort_type"])
64
+ GPI.print "Value of [sort_type] has to be 'date' or 'alphanum'."
65
+ GPI.quit
66
+ end
67
+ else
68
+ @hash["sort_type"] = "alphanum"
69
+ end
70
+ if @hash.has_key?("sort_order")
71
+ unless ["default", "reverse"].include?(@hash["sort_order"])
72
+ GPI.print "Value of [sort_order] has to be 'default' or 'reverse'."
73
+ GPI.quit
74
+ end
75
+ else
76
+ @hash["sort_order"] = "default"
77
+ end
78
+ unless @hash.has_key?("collection")
79
+ @hash["collection"] = false
80
+ @hash["categorize"] = false
81
+ end
82
+ if @hash["collection"] == true
83
+ unless @hash.has_key?("index_layout")
84
+ GPI.print "The [index_layout] option is required."
85
+ GPI.quit
86
+ else
87
+ unless @hash["index_layout"].class == String
88
+ GPI.print "Value of [index_layout] is not a string."
89
+ GPI.quit
90
+ end
91
+ end
92
+ unless @hash.has_key?("index_view")
93
+ GPI.print "The [index_view] option is required."
94
+ GPI.quit
95
+ else
96
+ unless @hash["index_view"].class == String
97
+ GPI.print "Value of [index_view] is not a string."
98
+ GPI.quit
99
+ end
100
+ end
101
+ unless @file == "_config/_index.yaml"
102
+ if @hash.has_key?("directory")
103
+ unless @hash["directory"].class == String
104
+ GPI.print "Value of [directory] has to be a string."
105
+ GPI.quit
106
+ end
107
+ else
108
+ GPI.print "Collections require the [directory] option."
109
+ GPI.quit
110
+ end
111
+ end
112
+ unless @hash.has_key?("categorize")
113
+ GPI.print ("Collections require the [categorize] option.")
114
+ GPI.quit
115
+ end
116
+ if @hash["categorize"] == true
117
+ unless @hash.has_key?("tags")
118
+ GPI.print ("Collections with categories require the [tags] option.")
119
+ GPI.quit
120
+ end
121
+ unless @hash.has_key?("categories")
122
+ GPI.print ("Collections with categories require" +
123
+ " the [categories] option.")
124
+ GPI.quit
125
+ else
126
+ unless @hash["categories"].class == Hash
127
+ GPI.print "Categories option is required to be a hash."
128
+ GPI.quit
129
+ else
130
+ @hash["categories"].each do |k,v|
131
+ if k.class == String && v.class == String
132
+ if k.match(/\A[a-zA-Z0-9_-]*\z/).nil?
133
+ GPI.print "Invalid category key: #{k}. Only alphanumerics,"+
134
+ " dashes and underscores allowed!"
135
+ GPI.quit
136
+ end
137
+ else
138
+ GPI.print "Keys and values of [categories] option have to be" +
139
+ " strings."
140
+ GPI.quit
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ else
147
+ @hash["categorize"] = false
148
+ @hash["tags"] = false
149
+ end
150
+ unless @file == "_config/_index.yaml"
151
+ if @hash.has_key?("name")
152
+ unless @hash["name"].class == String
153
+ GPI.print "[name] is required to be a string."
154
+ GPI.quit
155
+ end
156
+ else
157
+ GPI.print "[name] option is missing."
158
+ GPI.quit
159
+ end
160
+ else
161
+ if @hash.has_key?("uses")
162
+ unless @hash["uses"].class == String
163
+ GPI.print "[uses] should be a comma separated list of sections."
164
+ GPI.quit
165
+ end
166
+ end
167
+ end
168
+ =begin
169
+ unless @hash.has_key?("layout")
170
+ GPI.print "The [layout] option is required."
171
+ GPI.quit
172
+ else
173
+ unless @hash["layout"].class == String
174
+ GPI.print "Value of [layout] is not a string."
175
+ GPI.quit
176
+ end
177
+ end
178
+ unless @hash.has_key?("view")
179
+ GPI.print "The [view] option is required."
180
+ GPI.quit
181
+ else
182
+ unless @hash["view"].class == String
183
+ GPI.print "Value of [view] is not a string."
184
+ GPI.quit
185
+ end
186
+ end
187
+ =end
188
+ if @hash.has_key?("paginate")
189
+ unless @hash["paginate"].class == Integer
190
+ GPI.print "Value of [paginate] can only be an integer."
191
+ GPI.quit
192
+ else
193
+ if @hash["paginate"] < 0
194
+ GPI.print "Valur of [paginate] has to be equal or greater than 0."
195
+ GPI.quit
196
+ end
197
+ end
198
+ else
199
+ if @hash["collection"] == true
200
+ GPI.print "[paginate] option is required for collections."
201
+ GPI.quit
202
+ end
203
+ end
204
+ end
205
+
206
+ def validate_zrconf
207
+ GPI.print "Validating #{@file}."
208
+ unless @hash.has_key?("url")
209
+ GPI.print "config key [url] missing in _config/_zarchitect.yaml."
210
+ GPI.quit
211
+ else
212
+ unless @hash["url"].class == String
213
+ GPI.print "Value of [url] is not a string."
214
+ GPI.quit
215
+ end
216
+ end
217
+ unless @hash.has_key?("site_name")
218
+ GPI.print "config key [site_name] missing in _config/_zarchitect.yaml."
219
+ GPI.quit
220
+ else
221
+ unless @hash["site_name"].class == String
222
+ GPI.print "Value of key [site_name] is not a string."
223
+ GPI.quit
224
+ end
225
+ end
226
+ unless @hash.has_key?("thumbl")
227
+ GPI.print "config key [thumbl] missing in _config/_zarchitect.yaml."
228
+ GPI.quit
229
+ else
230
+ unless @hash["thumbl"].class == Array
231
+ GPI.print "Value of key [thumbl] is not an Array."
232
+ GPI.quit
233
+ end
234
+ unless @hash["thumbl"].count == 2
235
+ GPI.print "Array [thumbl] requires two values."
236
+ GPI.quit
237
+ end
238
+ unless @hash["thumbl"][0].class == Integer
239
+ GPI.print "First value in [thumbl] ist not an integer."
240
+ GPI.quit
241
+ end
242
+ unless @hash["thumbl"][1].class == Integer
243
+ GPI.print "Second value in [thumbl] ist not an integer."
244
+ GPI.quit
245
+ end
246
+ end
247
+ unless @hash.has_key?("thumbs")
248
+ GPI.print "config key [thumbs] missing in _config/_zarchitect.yaml."
249
+ GPI.quit
250
+ else
251
+ unless @hash["thumbs"].class == Array
252
+ GPI.print "Value of key [thumbs] is not an Array."
253
+ GPI.quit
254
+ end
255
+ unless @hash["thumbs"].count == 2
256
+ GPI.print "Array [thumbs] requires two values."
257
+ GPI.quit
258
+ end
259
+ unless @hash["thumbs"][0].class == Integer
260
+ GPI.print "First value in [thumbs] ist not an integer."
261
+ GPI.quit
262
+ end
263
+ unless @hash["thumbs"][1].class == Integer
264
+ GPI.print "Second value in [thumbs] ist not an integer."
265
+ GPI.quit
266
+ end
267
+ end
268
+ unless @hash.has_key?("rss_size")
269
+ GPI.print "config key [rss_size] missing in _config/_zarchitect.yaml."
270
+ GPI.quit
271
+ else
272
+ unless @hash["rss_size"].class == Integer
273
+ GPI.print "Value of [rss_size] is not an integer."
274
+ GPI.quit
275
+ end
276
+ end
277
+ if @hash.has_key?("exclude_assets")
278
+ unless @hash["exclude_assets"].class == Array
279
+ GPI.print "Value of [rss_size] is not an array."
280
+ GPI.quit
281
+ end
282
+ end
283
+ end
284
+
285
+ end
@@ -0,0 +1,286 @@
1
+ class Content < Zarchitect
2
+ attr_reader :nodes
3
+
4
+ def initialize(post)
5
+ @post = post
6
+ @source = @post.source_path.clone
7
+ @source.gsub!('/', '_')
8
+ @source.sub!('.md', '')
9
+ @nodes = Array.new
10
+ return if @post.conf.has_option?("script")
11
+ @raw = File.open(@post.source_path) { |f| f.read }
12
+ @raw = @raw.lines
13
+ i = 0
14
+ z = 0
15
+ @raw.each do |l|
16
+ if l.start_with?("---")
17
+ z += 1
18
+ end
19
+ break if z == 2
20
+ i += 1
21
+ end
22
+ @raw = @raw.drop(i+1)
23
+ @raw = @raw.join
24
+ end
25
+
26
+ def markup
27
+ from_script if @post.conf.has_option?("script")
28
+ return if @post.conf.has_option?("script")
29
+ GPI.print "Processing markdown", GPI::CLU.check_option('v')
30
+ chtml = @raw
31
+ @img_id = 0
32
+ @img_id_inc = 1
33
+ new_string = ""
34
+ regexp = /
35
+ \A
36
+ MEDIA:(?<filetype>img|img_full|video|yt|audio):
37
+ (?<id>[a-zA-Z0-9|._\-\/]+):"(?<caption>.*)":?(?<width>[0-9px%]*)
38
+ /x
39
+ chtml.each_line do |str|
40
+ if str.include?('MEDIA')
41
+ GPI.print "media processor: #{str}", GPI::CLU.check_option('v')
42
+ end
43
+ @m = regexp.match(str)
44
+ if @m
45
+ GPI.print "matched regex", GPI::CLU.check_option('v')
46
+ # file tag found
47
+ # replace with corresponding html :)
48
+ # m[0] whole tag
49
+ @caption = @m[:caption]
50
+ new_html = ""
51
+ case @m[:filetype]
52
+ when 'img'
53
+ html = media_img
54
+ when 'img_full'
55
+ html = media_img_full
56
+ when 'video'
57
+ html = media_video
58
+ when 'audio'
59
+ html = media_audio
60
+ when 'yt'
61
+ html = media_youtube
62
+ else
63
+ html = "[failed to render media]"
64
+ end
65
+ html.each_line do |substr|
66
+ if substr.lstrip
67
+ new_html << substr.lstrip
68
+ else
69
+ new_html << substr
70
+ end
71
+ end
72
+ if new_html.include?('\n')
73
+ str.sub!(@m[0], new_html.chomp!)
74
+ else
75
+ str.sub!(@m[0], new_html)
76
+ end
77
+ end
78
+ new_string << str
79
+ end
80
+
81
+ # process tables
82
+ tfound = false
83
+ tables = Array.new
84
+ ar = new_string.split("\n")
85
+ ar.each_with_index do |l,i|
86
+ if l[0] == "|" && l[-1] == "|"
87
+ if tfound # part of current table
88
+ tables.last.add_line l
89
+ else # first line of a table
90
+ tables.push HTMLTable.new
91
+ tables.last.add_line l
92
+ tables.last.starts_at i
93
+ tfound = true
94
+ end
95
+ else
96
+ if tfound # first line after a table!
97
+ tables.last.ends_at i-1
98
+ tfound = false
99
+ tables.last.process
100
+ end
101
+ end
102
+ end
103
+
104
+ tables.each do |t|
105
+ ar = t.replace(ar)
106
+ end
107
+
108
+ ar.delete_if { |x| x.nil? }
109
+
110
+ markdown = Redcarpet::Markdown.new(RougeHTML,
111
+ autolink: true)
112
+ chtml = markdown.render(ar.join("\n"))
113
+
114
+ parse(chtml)
115
+
116
+ end
117
+
118
+ def html
119
+ str = String.new
120
+ @nodes.each do |n|
121
+ str << n.html
122
+ end
123
+ str
124
+ end
125
+
126
+ def preview(n)
127
+ if full_preview?(n)
128
+ html
129
+ else
130
+ str = String.new
131
+ @nodes.each_with_index do |node,i|
132
+ break if i == n
133
+ str << node.html
134
+ end
135
+ str
136
+ end
137
+ end
138
+
139
+ def full_preview?(n)
140
+ (@nodes.count <= n)
141
+ end
142
+
143
+ private
144
+
145
+ def from_script
146
+ html = %x{ ./#{@post.conf.script} }
147
+ parse(html)
148
+ end
149
+
150
+ def parse(html)
151
+ debug_dir = File.join(File.join(BUILDIR, DEBUGSDIR), @source)
152
+ if GPI::CLU.check_option('d')
153
+ debug_dir = Util.mkdir(debug_dir)
154
+ end
155
+
156
+ node = Nokogiri::HTML.fragment(html) do |config|
157
+ config.strict.noblanks
158
+ end
159
+
160
+ nodes = node.children.select { |c| c.class == Nokogiri::XML::Element }
161
+
162
+ nodes.each_with_index do |n,i|
163
+ @nodes.push ContentNode.new(n)
164
+
165
+ if GPI::CLU.check_option('d') # debug
166
+ f = File.join(debug_dir, "#{i}.txt")
167
+ File.open(f, "w") { |f| f.write(@nodes.last.html) }
168
+ end
169
+
170
+ end
171
+ end
172
+
173
+ def media_img
174
+ GPI.print "Processing media: img", GPI::CLU.check_option('v')
175
+ @img_id += @img_id_inc
176
+ @imgset = Array.new
177
+ if @m[:id].count('|') == @caption.count('|')
178
+ @m[:id].split('|').each do |id|
179
+ img = Image.find("url", id)
180
+ @imgset.push img unless img.nil?
181
+ end
182
+ @img_id_inc = @imgset.size
183
+ #@images = ImageFile.find(m[:id].split('|'))
184
+ unless @m[:width].empty?
185
+ @max_width = @m[:width]
186
+ else
187
+ @max_width = '100%'
188
+ end
189
+ if @imgset.count > 0
190
+ hash = Hash.new
191
+ a = ZERB.new("_layouts/_image.html.erb")
192
+ if @post.conf.has_option?('sthumb')
193
+ hash["fst"] = @post.conf.sthumb
194
+ else
195
+ hash["fst"] = false
196
+ end
197
+ hash["imgid"] = @img_id
198
+ hash["imgset"] = @imgset
199
+ hash["max_width"] = @max_width
200
+ hash["caption"] = @caption
201
+ a.handle_data(hash)
202
+ a.prepare
203
+ a.render
204
+ html = a.output
205
+ else
206
+ html = "[img not found]"
207
+ end
208
+ else
209
+ html = "[failed to render media]"
210
+ end
211
+ end
212
+
213
+ def media_img_full
214
+ GPI.print "Processing media: img_full", GPI::CLU.check_option('v')
215
+ @image = Image.find("url", @m[:id])
216
+ unless @image.nil?
217
+ hash = Hash.new
218
+ a = ZERB.new("_layouts/_image_full.html.erb")
219
+ hash["image"] = @image
220
+ hash["caption"] = @caption
221
+ a.handle_data(hash)
222
+ a.prepare
223
+ a.render
224
+ html = a.output
225
+ else
226
+ html = "[img not found]"
227
+ end
228
+ end
229
+
230
+ def media_video
231
+ GPI.print "Processing media: video", GPI::CLU.check_option('v')
232
+ @video = Video.find("url", @m[:id])
233
+ unless @video.nil?
234
+ hash = Hash.new
235
+ a = ZERB.new("_layouts/_video.html.erb")
236
+ hash["video"] = @video
237
+ hash["caption"] = @caption
238
+ a.handle_data(hash)
239
+ a.prepare
240
+ a.render
241
+ html = a.output
242
+ else
243
+ html = "[video not found]"
244
+ end
245
+ end
246
+
247
+ def media_youtube
248
+ @yt_id = @m[:id]
249
+ hash = Hash.new
250
+ GPI.print "Processing media: youtube", GPI::CLU.check_option('v')
251
+ a = ZERB.new("_layouts/_youtube.html.erb")
252
+ hash["yt_id"] = @yt_id
253
+ hash["caption"] = @caption
254
+ a.handle_data(hash)
255
+ a.prepare
256
+ a.render
257
+ html = a.output
258
+ end
259
+
260
+ def media_audio
261
+ GPI.print "Processing media: audio", GPI::CLU.check_option('v')
262
+ @audio = AudioFile.find(@m[:id])
263
+ unless @audio.nil?
264
+ hash = Hash.new
265
+ hash["audio"] = @audio
266
+ hash["caption"] = @caption
267
+ a = ZERB.new("_layouts/_audio.html.erb")
268
+ a.handle_data(hash)
269
+ a.prepare
270
+ a.render
271
+ html = a.output
272
+ else
273
+ html = "[audio not found]"
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ class ContentNode
280
+ attr_reader :type, :html
281
+
282
+ def initialize(node)
283
+ @type = node.name
284
+ @html = node.to_html
285
+ end
286
+ end