webgen 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +300 -0
- data/Rakefile +149 -27
- data/TODO +3 -4
- data/VERSION +1 -1
- data/data/webgen/gallery_styles/slides/collage.rb +7 -4
- data/data/webgen/sipttra_styles/default/README +7 -0
- data/data/webgen/sipttra_styles/default/css/sipttra.rcss +71 -0
- data/data/webgen/sipttra_styles/default/js/sipttra.js +7 -0
- data/data/webgen/sipttra_styles/default/sipttra.template +105 -0
- data/data/webgen/website_styles/1024px/README +3 -0
- data/data/webgen/website_styles/1024px/default.css +3 -3
- data/data/webgen/website_styles/andreas00/README +4 -1
- data/data/webgen/website_styles/andreas00/default.css +2 -2
- data/data/webgen/website_styles/andreas01/README +3 -0
- data/data/webgen/website_styles/andreas01/default.css +3 -3
- data/data/webgen/website_styles/andreas03/README +4 -1
- data/data/webgen/website_styles/andreas03/default.css +2 -2
- data/data/webgen/website_styles/andreas04/README +3 -0
- data/data/webgen/website_styles/andreas04/default.css +2 -2
- data/data/webgen/website_styles/andreas05/README +3 -0
- data/data/webgen/website_styles/andreas05/default.css +2 -2
- data/data/webgen/website_styles/andreas06/README +3 -0
- data/data/webgen/website_styles/andreas06/default.css +3 -2
- data/data/webgen/website_styles/andreas07/README +3 -0
- data/data/webgen/website_styles/andreas07/default.css +4 -4
- data/data/webgen/website_styles/andreas08/README +4 -1
- data/data/webgen/website_styles/andreas08/default.css +2 -2
- data/data/webgen/website_styles/andreas08/default.template +1 -1
- data/data/webgen/website_styles/andreas09/README +3 -0
- data/data/webgen/website_styles/andreas09/default.css +2 -2
- data/data/webgen/website_styles/default/README +1 -1
- data/data/webgen/website_styles/default/default.css +1 -1
- data/data/webgen/website_styles/plain/README +7 -2
- data/data/webgen/website_styles/plain/default.css +2 -2
- data/doc/config.yaml +3 -0
- data/doc/metainfo.yaml +20 -3
- data/doc/src/css/sipttra.rcss +71 -0
- data/doc/src/default.template +10 -5
- data/doc/src/documentation/howto.page +221 -0
- data/doc/src/documentation/plugins/contentconverter/default.page +1 -0
- data/doc/src/documentation/plugins/contentconverter/xmlbuilder.page +2 -2
- data/doc/src/documentation/plugins/core/configuration.page +6 -0
- data/doc/src/documentation/plugins/core/resourcemanager.page +4 -10
- data/doc/src/documentation/plugins/file/defaulthandler.page +1 -0
- data/doc/src/documentation/plugins/file/sipttrahandler.page +18 -0
- data/doc/src/documentation/plugins/file/templatehandler.page +27 -10
- data/doc/src/documentation/plugins/file/thumbnailwriter.page +5 -2
- data/doc/src/documentation/plugins/htmlvalidator/default.page +1 -0
- data/doc/src/documentation/plugins/index.page +2 -2
- data/doc/src/documentation/plugins/menustyle/default.page +1 -0
- data/doc/src/documentation/plugins/tag/breadcrumbtrail.page +12 -0
- data/doc/src/documentation/plugins/tag/customvar.page +11 -0
- data/doc/src/documentation/plugins/tag/default.page +1 -0
- data/doc/src/documentation/references/index.page +8 -0
- data/doc/src/documentation/references/meta_info_reference.page +35 -16
- data/doc/src/documentation/references/resource_reference.page +18 -0
- data/doc/src/documentation/references/sipttra_format.page +241 -0
- data/doc/src/documentation/references/webpage_format.page +4 -0
- data/doc/src/examples/example_sites/index.page +41 -0
- data/doc/src/examples/example_sites/personal.zip +0 -0
- data/doc/src/examples/example_sites/photo_gallery.zip +0 -0
- data/doc/src/examples/index.page +2 -1
- data/doc/src/js/sipttra.js +7 -0
- data/doc/src/news.page +25 -0
- data/doc/src/project.todo +91 -0
- data/doc/src/sipttra.template +105 -0
- data/lib/webgen/cli.rb +31 -1
- data/lib/webgen/config.rb +2 -2
- data/lib/webgen/plugin.rb +7 -3
- data/lib/webgen/plugins/coreplugins/configuration.rb +3 -1
- data/lib/webgen/plugins/filehandlers/filehandler.rb +6 -5
- data/lib/webgen/plugins/filehandlers/sipttra.rb +73 -0
- data/lib/webgen/plugins/filehandlers/template.rb +2 -1
- data/lib/webgen/plugins/miscplugins/treewalker.rb +40 -4
- data/lib/webgen/plugins/tags/breadcrumbtrail.rb +14 -1
- data/lib/webgen/plugins/tags/customvar.rb +54 -0
- data/lib/webgen/sipttra_format.rb +343 -0
- data/lib/webgen/website.rb +25 -1
- data/man/man1/webgen.1 +75 -0
- data/setup.rb +861 -607
- data/test/fixtures/sample_site/src/test.todo +7 -0
- data/test/fixtures/tc_plugin/plugin1.rb +2 -0
- data/test/fixtures/tc_sipttra_format/test.sipttra +46 -0
- data/test/unittests/tc_filehandler_sipttra.rb +26 -0
- data/test/unittests/tc_filehandler_template.rb +2 -1
- data/test/unittests/tc_plugin.rb +8 -1
- data/test/unittests/tc_sipttra_format.rb +58 -0
- data/test/unittests/tc_tags_breadcrumbtrail.rb +34 -0
- data/test/unittests/tc_tags_customvar.rb +24 -0
- data/test/unittests/tc_tags_date.rb +1 -0
- data/test/unittests/tc_tags_includefile.rb +6 -13
- data/test/unittests/tc_treewalker.rb +8 -1
- metadata +37 -3
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
#
|
4
|
+
# $Id: gallery.rb 569 2006-12-29 20:11:21Z thomas $
|
5
|
+
#
|
6
|
+
# webgen: template based static website generator
|
7
|
+
# Copyright (C) 2004 Thomas Leitner
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
10
|
+
# General Public License as published by the Free Software Foundation; either version 2 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
14
|
+
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along with this program; if not,
|
18
|
+
# write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
|
23
|
+
require 'yaml'
|
24
|
+
require 'erb'
|
25
|
+
require 'webgen/sipttra_format'
|
26
|
+
|
27
|
+
load_plugin 'webgen/plugins/filehandlers/filehandler'
|
28
|
+
load_plugin 'webgen/plugins/filehandlers/page'
|
29
|
+
|
30
|
+
|
31
|
+
module FileHandlers
|
32
|
+
|
33
|
+
# Handles sipttra (Simple Plain Text Tracker) files.
|
34
|
+
class SipttraHandler < DefaultHandler
|
35
|
+
|
36
|
+
infos( :name => 'File/SipttraHandler',
|
37
|
+
:author => Webgen::AUTHOR,
|
38
|
+
:summary => "Handles sipttra (Simple Plain Text Tracker) files"
|
39
|
+
)
|
40
|
+
|
41
|
+
register_extension 'todo'
|
42
|
+
|
43
|
+
default_meta_info( 'template' => '/sipttra.template' )
|
44
|
+
|
45
|
+
def create_node( file, parent, meta_info )
|
46
|
+
begin
|
47
|
+
data = File.read( file )
|
48
|
+
s = Sipttra::Tracker.new( data )
|
49
|
+
rescue
|
50
|
+
log(:error) { "Could not parse sipttra file <#{file}>, not creating an output page: #{$!.message}" }
|
51
|
+
return
|
52
|
+
end
|
53
|
+
meta_info.update( s.info['webgen-metainfo'] || {} )
|
54
|
+
|
55
|
+
filename = File.basename( file, '.todo' ) + '.page'
|
56
|
+
filehandler = @plugin_manager['Core/FileHandler']
|
57
|
+
pagehandler = @plugin_manager['File/PageHandler']
|
58
|
+
node = filehandler.create_node( filename, parent, pagehandler ) do |filename, parent, handler, mi|
|
59
|
+
pagehandler.create_node_from_data( filename, parent, "Forgotten to specify a sipttra template?! ;-)", mi.merge( meta_info ) )
|
60
|
+
end
|
61
|
+
node.node_info[:sipttra] = s if node
|
62
|
+
node.node_info[:src] = file if node
|
63
|
+
|
64
|
+
node
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_node( node )
|
68
|
+
# do nothing
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
#--
|
3
3
|
#
|
4
|
-
# $Id: template.rb
|
4
|
+
# $Id: template.rb 593 2007-02-13 19:33:44Z thomas $
|
5
5
|
#
|
6
6
|
# webgen: template based static website generator
|
7
7
|
# Copyright (C) 2004 Thomas Leitner
|
@@ -36,6 +36,7 @@ module FileHandlers
|
|
36
36
|
|
37
37
|
register_extension 'template'
|
38
38
|
|
39
|
+
default_meta_info( 'blocks' => [['content', 'html']] )
|
39
40
|
|
40
41
|
def create_node( src_name, parent, meta_info )
|
41
42
|
begin
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
#--
|
3
3
|
#
|
4
|
-
# $Id: treewalker.rb
|
4
|
+
# $Id: treewalker.rb 620 2007-02-28 09:19:15Z thomas $
|
5
5
|
#
|
6
6
|
# webgen: template based static website generator
|
7
7
|
# Copyright (C) 2004 Thomas Leitner
|
@@ -28,13 +28,25 @@ module TreeWalkers
|
|
28
28
|
# so that it is called when the main class' #execute method is called.
|
29
29
|
class TreeWalker < Webgen::Plugin
|
30
30
|
|
31
|
-
infos( :
|
31
|
+
infos( :name => 'Misc/TreeWalker',
|
32
|
+
:author => Webgen::AUTHOR,
|
32
33
|
:summary => "Super plugin for traversing the node tree"
|
33
34
|
)
|
34
35
|
|
36
|
+
attr_reader :walkers
|
37
|
+
|
38
|
+
def initialize( plugin_manager )
|
39
|
+
super
|
40
|
+
@walkers = []
|
41
|
+
end
|
42
|
+
|
35
43
|
# Walks the +tree+ for the +walker+ in the +direction+, either +:forward+ or +:backward+.
|
36
|
-
|
37
|
-
|
44
|
+
# If +walker+ is +nil+, then all registered walkers are used!
|
45
|
+
def execute( tree, walker = nil, direction = :forward )
|
46
|
+
walkers = ( walker.nil? ? @walkers : [walker] )
|
47
|
+
walkers.each do |walker|
|
48
|
+
walk_tree( tree, walker, 0, direction )
|
49
|
+
end
|
38
50
|
end
|
39
51
|
|
40
52
|
#######
|
@@ -52,4 +64,28 @@ module TreeWalkers
|
|
52
64
|
|
53
65
|
end
|
54
66
|
|
67
|
+
|
68
|
+
# Prints the whole tree of read files if the log level is at least DEBUG.
|
69
|
+
class DebugTreePrinter < Webgen::Plugin
|
70
|
+
|
71
|
+
infos( :name => 'Misc/DebugTreePrinter',
|
72
|
+
:author => Webgen::AUTHOR,
|
73
|
+
:summary => "Prints out the information in the tree for debug purposes."
|
74
|
+
)
|
75
|
+
|
76
|
+
depends_on 'Misc/TreeWalker'
|
77
|
+
|
78
|
+
def initialize( plugin_manager )
|
79
|
+
super
|
80
|
+
@plugin_manager['Misc/TreeWalker'].walkers << self
|
81
|
+
end
|
82
|
+
|
83
|
+
def call( node, level )
|
84
|
+
log(:debug) do
|
85
|
+
" "*level << "\\_ "*(level > 0 ? 1 : 0) << "#{node['title']}: #{node.node_info[:src]} -> #{node.path}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
55
91
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
#--
|
3
3
|
#
|
4
|
-
# $Id: breadcrumbtrail.rb
|
4
|
+
# $Id: breadcrumbtrail.rb 595 2007-02-14 07:43:28Z thomas $
|
5
5
|
#
|
6
6
|
# webgen: template based static website generator
|
7
7
|
# Copyright (C) 2004 Thomas Leitner
|
@@ -40,6 +40,8 @@ module Tags
|
|
40
40
|
)
|
41
41
|
|
42
42
|
param 'separator', ' / ', 'Separates the hierachy entries from each other.'
|
43
|
+
param 'omitLast', false, 'Omits the last path component.'
|
44
|
+
param 'omitIndexFile', false, 'Omits the last path component if it is an index file.'
|
43
45
|
|
44
46
|
register_tag 'breadcrumbTrail'
|
45
47
|
|
@@ -47,11 +49,22 @@ module Tags
|
|
47
49
|
out = []
|
48
50
|
node = chain.last
|
49
51
|
|
52
|
+
omitIndexFile = if node.meta_info.has_key?( 'omitIndexFileInBreadcrumbTrail' )
|
53
|
+
node['omitIndexFileInBreadcrumbTrail']
|
54
|
+
else
|
55
|
+
param( 'omitIndexFile' )
|
56
|
+
end
|
57
|
+
omitIndexFile = omitIndexFile && node.parent['indexFile'] &&
|
58
|
+
node.parent['indexFile'].node_for_lang( node['lang'] ) == node
|
59
|
+
|
60
|
+
node = node.parent if omitIndexFile
|
61
|
+
|
50
62
|
until node.nil?
|
51
63
|
out.push( node.link_from( chain.last ) )
|
52
64
|
node = node.parent
|
53
65
|
end
|
54
66
|
|
67
|
+
out[0] = '' if param( 'omitLast' ) && !omitIndexFile
|
55
68
|
out = out.reverse.join( param( 'separator' ) )
|
56
69
|
log(:debug) { "Breadcrumb trail for <#{chain.last.node_info[:src]}>: #{out}" }
|
57
70
|
out
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
#
|
4
|
+
# $Id: meta.rb 563 2006-12-29 08:59:41Z thomas $
|
5
|
+
#
|
6
|
+
# webgen: template based static website generator
|
7
|
+
# Copyright (C) 2004 Thomas Leitner
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
10
|
+
# General Public License as published by the Free Software Foundation; either version 2 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
14
|
+
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along with this program; if not,
|
18
|
+
# write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
|
23
|
+
load_plugin 'webgen/plugins/tags/tag_processor'
|
24
|
+
|
25
|
+
module Tags
|
26
|
+
|
27
|
+
class CustomVarTag < DefaultTag
|
28
|
+
|
29
|
+
infos( :name => 'Tag/CustomVar',
|
30
|
+
:author => Webgen::AUTHOR,
|
31
|
+
:summary => "Used to output custom variables defined in Core/Configuration:customVars."
|
32
|
+
)
|
33
|
+
|
34
|
+
param 'var', nil, 'The variable of which the value should be retrieved.'
|
35
|
+
set_mandatory 'var', true
|
36
|
+
|
37
|
+
register_tag 'customVar'
|
38
|
+
|
39
|
+
def process_tag( tag, chain )
|
40
|
+
output = ''
|
41
|
+
customVars = param( 'customVars', 'Core/Configuration' )
|
42
|
+
var = param( 'var' )
|
43
|
+
|
44
|
+
if customVars.kind_of?( Hash ) && customVars.has_key?( var )
|
45
|
+
output = customVars[var]
|
46
|
+
else
|
47
|
+
log(:warn) { "No custom variable called '#{var}' found in Core/Configuration:customVars (file <#{chain.first.node_info[:src]}>)" }
|
48
|
+
end
|
49
|
+
output
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
#
|
4
|
+
# $Id: gallery.rb 569 2006-12-29 20:11:21Z thomas $
|
5
|
+
#
|
6
|
+
# webgen: template based static website generator
|
7
|
+
# Copyright (C) 2004 Thomas Leitner
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
10
|
+
# General Public License as published by the Free Software Foundation; either version 2 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
14
|
+
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along with this program; if not,
|
18
|
+
# write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
+
#
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
|
23
|
+
require 'yaml'
|
24
|
+
require 'cgi'
|
25
|
+
|
26
|
+
|
27
|
+
module Sipttra
|
28
|
+
|
29
|
+
# A simple node belonging to a Tracker. One node represents exactly one line of a sipttra file.
|
30
|
+
class Node
|
31
|
+
|
32
|
+
attr_accessor :tracker
|
33
|
+
|
34
|
+
# Returns the line representation of this node.
|
35
|
+
def to_line
|
36
|
+
''
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Base class for all nodes which deal with simple text lines.
|
43
|
+
class TextNode < Node
|
44
|
+
|
45
|
+
# The text of the node/line.
|
46
|
+
attr_accessor :text
|
47
|
+
|
48
|
+
def initialize( text )
|
49
|
+
@text = text
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_line
|
53
|
+
@text
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
@text
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# A comment is just a special text node.
|
64
|
+
class Comment < TextNode; end
|
65
|
+
|
66
|
+
|
67
|
+
# Represents a category line.
|
68
|
+
class Category < Node
|
69
|
+
|
70
|
+
# The name of the category.
|
71
|
+
attr_accessor :name
|
72
|
+
|
73
|
+
# The type of the category.
|
74
|
+
attr_accessor :type
|
75
|
+
|
76
|
+
def initialize( name, type = nil )
|
77
|
+
@name = name
|
78
|
+
@type = type
|
79
|
+
raise "Category must have a name" if name.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns all tickets belonging to this category.
|
83
|
+
def tickets
|
84
|
+
tickets = []
|
85
|
+
i = @tracker.nodes.index( self ) + 1
|
86
|
+
while i < @tracker.nodes.length
|
87
|
+
line = @tracker.nodes[i]
|
88
|
+
break if line.kind_of?( Category )
|
89
|
+
tickets << line if line.kind_of?( Ticket )
|
90
|
+
i += 1
|
91
|
+
end
|
92
|
+
tickets
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_line
|
96
|
+
'### ' + to_s + ' ###'
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
name + (type.nil? ? '' : ' (' + type + ')')
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Represents a ticket line.
|
107
|
+
class Ticket < Node
|
108
|
+
|
109
|
+
attr_accessor :name, :due_date, :belongs_to, :summary
|
110
|
+
|
111
|
+
def initialize( name, due_date, belongs_to, text = '' )
|
112
|
+
@name = name
|
113
|
+
@due_date = due_date
|
114
|
+
@belongs_to = belongs_to
|
115
|
+
@summary = text.strip
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the category to which this ticket belongs.
|
119
|
+
def category
|
120
|
+
i = @tracker.nodes.index( self ) - 1
|
121
|
+
i -= 1 while i >= 0 && !@tracker.nodes[i].kind_of?( Category )
|
122
|
+
(i < 0 ? nil : @tracker.nodes[i])
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns +true+ if this ticket is closed, ie. if it belongs to a category with type closed.
|
126
|
+
def closed?
|
127
|
+
category.type == 'closed'
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the tickets assigned to this ticket, ie. all sub-tickets. The +type+ parameter can be
|
131
|
+
# one of:
|
132
|
+
# :open :: all tickets with a type different from +closed+
|
133
|
+
# :closed :: all tickets with type +closed+
|
134
|
+
# :all :: all tickets independent from type
|
135
|
+
def assigned_tickets( type = :all )
|
136
|
+
if @name.nil?
|
137
|
+
[]
|
138
|
+
else
|
139
|
+
@tracker.tickets.select do |t|
|
140
|
+
t.belongs_to == @name &&
|
141
|
+
(type == :all || (type == :closed ? t.category.type == 'closed' : t.category.type != 'closed' ))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the detailed description for this ticket.
|
147
|
+
def description
|
148
|
+
text = []
|
149
|
+
line = nil
|
150
|
+
i = @tracker.nodes.index( self ) + 1
|
151
|
+
while i < @tracker.nodes.length && (line = @tracker.nodes[i]).kind_of?( AdditionalText )
|
152
|
+
text << line
|
153
|
+
i += 1
|
154
|
+
end
|
155
|
+
text.join( "\n" ).strip
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns the whole text for the ticket, ie. the summary and ticket joined by a line separator.
|
159
|
+
def all_text
|
160
|
+
[@summary, description].join( "\n" )
|
161
|
+
end
|
162
|
+
alias_method :to_s, :all_text
|
163
|
+
|
164
|
+
def to_line
|
165
|
+
s = '*'
|
166
|
+
s << ' ' + name unless name.nil?
|
167
|
+
s << ' (' + due_date + ')' unless due_date.nil?
|
168
|
+
s << ' [' + belongs_to + ']' unless belongs_to.nil?
|
169
|
+
s << (!name.nil? && due_date.nil? && belongs_to.nil? ? ':' : '' ) + (summary.empty? ? '' : ' ' + summary.to_s)
|
170
|
+
s
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Represents a milestone which is a special ticket.
|
177
|
+
class Milestone < Ticket
|
178
|
+
|
179
|
+
def initialize( *args )
|
180
|
+
super( *args )
|
181
|
+
raise "Milestone must have a name" if @name.nil?
|
182
|
+
end
|
183
|
+
|
184
|
+
# Like assigned_tickets but includes tickets in sub milestones.
|
185
|
+
def all_assigned_tickets( type = :all )
|
186
|
+
(assigned_tickets( type ) + sub_milestones.collect {|sm| sm.all_assigned_tickets( type )}).flatten
|
187
|
+
end
|
188
|
+
|
189
|
+
# A milestone is closed if all assigned tickets are closed, including the ones from the sub
|
190
|
+
# milestones.
|
191
|
+
def closed?
|
192
|
+
assigned_tickets( :open ).empty? && sub_milestones.all? {|sm| sm.closed?}
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns all direct sub milestones.
|
196
|
+
def sub_milestones
|
197
|
+
(@name.nil? ? [] : @tracker.milestones.select {|m| m.belongs_to == @name})
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
# Represents additional text lines for tickets. All additional text lines for one ticket are the
|
204
|
+
# ticket's description.
|
205
|
+
class AdditionalText < TextNode
|
206
|
+
|
207
|
+
def initialize( text )
|
208
|
+
super( text.sub( /^ /, '' ) )
|
209
|
+
end
|
210
|
+
|
211
|
+
def to_line
|
212
|
+
(@text.strip.empty? ? '' : ' ' + @text)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# The tracker is used to parse sipttra files and to change the sipttra data in memory.
|
219
|
+
class Tracker
|
220
|
+
|
221
|
+
IDENT_REGEXP=/\w[-.\w\d]*/
|
222
|
+
|
223
|
+
DATE_REGEXP=/\((\d\d\d\d-\d\d-\d\d)\)/
|
224
|
+
BELONGS_REGEXP=/\[(#{IDENT_REGEXP})\]/
|
225
|
+
|
226
|
+
TICKET_REGEXP=/^\*(?:\s(#{IDENT_REGEXP})(?=:|\s\[|\s\():?)?(?:\s#{DATE_REGEXP})?(?:\s#{BELONGS_REGEXP})?(?:$|\s(.*)$)/
|
227
|
+
CONTENT_REGEXP=/^\s\s(.*)$/
|
228
|
+
CATEGORY_REGEXP=/^(#+)\s{1,}([^(]*?)(?:\s*\((\w+)\))?\s{1,}\1$/
|
229
|
+
|
230
|
+
attr_reader :nodes, :info
|
231
|
+
|
232
|
+
def initialize( data = nil )
|
233
|
+
@nodes = []
|
234
|
+
@info = {}
|
235
|
+
parse( data ) if data
|
236
|
+
end
|
237
|
+
|
238
|
+
# Parses the given +data+ and fills the tracker with information.
|
239
|
+
def parse( data )
|
240
|
+
@nodes = []
|
241
|
+
@info = {}
|
242
|
+
level = 0
|
243
|
+
|
244
|
+
if data =~ /\A---\n/m
|
245
|
+
begin
|
246
|
+
index = data.index( "---\n", 4 ) || 0
|
247
|
+
@info = YAML.load( data[0...index] )
|
248
|
+
data = data[index..-1]
|
249
|
+
rescue
|
250
|
+
ensure
|
251
|
+
@info = {} unless @info.kind_of?( Hash )
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
data.split(/\n/).each do |line|
|
256
|
+
case
|
257
|
+
when (m = CATEGORY_REGEXP.match( line )) && category( m[2], m[3] ).nil?
|
258
|
+
@nodes << Category.new( m[2], m[3] )
|
259
|
+
level = 1
|
260
|
+
|
261
|
+
when level == 0
|
262
|
+
@nodes << Comment.new( line )
|
263
|
+
|
264
|
+
when (m = TICKET_REGEXP.match( line )) && (milestone( m[1] ).nil? && ticket( m[1] ).nil?)
|
265
|
+
if @nodes.find_all {|child| child.kind_of?( Category )}.last.type.nil?
|
266
|
+
@nodes << Milestone.new( m[1], m[2], m[3], m[4] || '' )
|
267
|
+
else
|
268
|
+
@nodes << Ticket.new( m[1], m[2], m[3], m[4] || '' )
|
269
|
+
end
|
270
|
+
|
271
|
+
when (@nodes.last.kind_of?( Ticket ) || @nodes.last.kind_of?( AdditionalText )) &&
|
272
|
+
(line.empty? || (m = CONTENT_REGEXP.match( line )))
|
273
|
+
@nodes << AdditionalText.new( line )
|
274
|
+
|
275
|
+
else
|
276
|
+
@nodes << Comment.new( line )
|
277
|
+
end
|
278
|
+
@nodes.last.tracker = self
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
def check_consistency
|
284
|
+
# TODO what to check?
|
285
|
+
end
|
286
|
+
|
287
|
+
# If Bluecloth is available +text+ is considered to be in Markdown format and converted
|
288
|
+
# to HTML. Otherwise the unchanged text is returned.
|
289
|
+
def htmlize( text )
|
290
|
+
require 'bluecloth'
|
291
|
+
BlueCloth.new( text ).to_html
|
292
|
+
rescue
|
293
|
+
text
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns all categories.
|
297
|
+
def categories
|
298
|
+
@nodes.find_all {|child| child.kind_of?( Category ) && !child.type.nil? }
|
299
|
+
end
|
300
|
+
|
301
|
+
# Returns the category with the given +name+ and +type+.
|
302
|
+
def category( name, type )
|
303
|
+
categories.find {|cat| cat.name == name && cat.type == type}
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns all category names.
|
307
|
+
def category_names
|
308
|
+
categories.collect {|cat| cat.name}.uniq
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns all milestones.
|
312
|
+
def milestones
|
313
|
+
@nodes.find_all {|child| child.instance_of?( Milestone ) }
|
314
|
+
end
|
315
|
+
|
316
|
+
# Returns the milestone with the given +name+.
|
317
|
+
def milestone( name )
|
318
|
+
(name.nil? ? nil : milestones.find {|ms| ms.name == name})
|
319
|
+
end
|
320
|
+
|
321
|
+
# Returns all tickets independent from their categories.
|
322
|
+
def tickets
|
323
|
+
@nodes.find_all {|child| child.instance_of?( Ticket ) }
|
324
|
+
end
|
325
|
+
|
326
|
+
# Returns the ticket with the given +name+.
|
327
|
+
def ticket( name )
|
328
|
+
(name.nil? ? nil : tickets.find {|ticket| ticket.name == name})
|
329
|
+
end
|
330
|
+
|
331
|
+
# Returns all tickets for the category +name+ independent from the category type.
|
332
|
+
def tickets_for_category( name )
|
333
|
+
tickets.select {|t| t.category.name == name}
|
334
|
+
end
|
335
|
+
|
336
|
+
# Returns a string representation of the tracker which can later be used by #parse .
|
337
|
+
def to_s
|
338
|
+
@info.to_yaml.sub( /^---\s*\n/m, '' ) + "\n\n" + @nodes.collect {|line| line.to_line}.join( "\n" )
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|