webgen 0.4.1 → 0.4.2
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/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
|