instiki 0.10.0 → 0.10.1

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.
Files changed (88) hide show
  1. data/CHANGELOG +174 -165
  2. data/README +68 -68
  3. data/app/controllers/admin_controller.rb +94 -94
  4. data/app/controllers/application.rb +135 -131
  5. data/app/controllers/file_controller.rb +129 -129
  6. data/app/controllers/wiki_controller.rb +354 -354
  7. data/app/helpers/application_helper.rb +68 -68
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -33
  10. data/app/models/chunks/chunk.rb +86 -86
  11. data/app/models/chunks/engines.rb +61 -54
  12. data/app/models/chunks/include.rb +41 -41
  13. data/app/models/chunks/literal.rb +31 -31
  14. data/app/models/chunks/nowiki.rb +28 -28
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -182
  17. data/app/models/chunks/wiki.rb +141 -141
  18. data/app/models/file_yard.rb +58 -58
  19. data/app/models/page.rb +112 -112
  20. data/app/models/page_lock.rb +22 -22
  21. data/app/models/page_set.rb +89 -89
  22. data/app/models/revision.rb +123 -123
  23. data/app/models/web.rb +182 -176
  24. data/app/models/wiki_content.rb +207 -207
  25. data/app/models/wiki_service.rb +233 -233
  26. data/app/models/wiki_words.rb +23 -23
  27. data/app/views/admin/create_system.rhtml +83 -83
  28. data/app/views/admin/create_web.rhtml +69 -69
  29. data/app/views/admin/edit_web.rhtml +137 -136
  30. data/app/views/file/file.rhtml +18 -18
  31. data/app/views/file/import.rhtml +22 -22
  32. data/app/views/layouts/default.rhtml +86 -85
  33. data/app/views/markdown_help.rhtml +12 -12
  34. data/app/views/mixed_help.rhtml +6 -6
  35. data/app/views/navigation.rhtml +30 -30
  36. data/app/views/rdoc_help.rhtml +12 -12
  37. data/app/views/textile_help.rhtml +24 -24
  38. data/app/views/wiki/authors.rhtml +11 -11
  39. data/app/views/wiki/edit.rhtml +39 -39
  40. data/app/views/wiki/export.rhtml +12 -12
  41. data/app/views/wiki/feeds.rhtml +14 -14
  42. data/app/views/wiki/list.rhtml +64 -64
  43. data/app/views/wiki/locked.rhtml +23 -23
  44. data/app/views/wiki/login.rhtml +14 -14
  45. data/app/views/wiki/new.rhtml +31 -31
  46. data/app/views/wiki/page.rhtml +115 -115
  47. data/app/views/wiki/print.rhtml +14 -14
  48. data/app/views/wiki/published.rhtml +9 -9
  49. data/app/views/wiki/recently_revised.rhtml +26 -26
  50. data/app/views/wiki/revision.rhtml +103 -103
  51. data/app/views/wiki/rollback.rhtml +36 -36
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -38
  54. data/app/views/wiki/tex.rhtml +22 -22
  55. data/app/views/wiki/tex_web.rhtml +34 -34
  56. data/app/views/wiki/web_list.rhtml +18 -18
  57. data/app/views/wiki_words_help.rhtml +9 -9
  58. data/config/environment.rb +82 -82
  59. data/config/environments/development.rb +5 -5
  60. data/config/environments/production.rb +4 -4
  61. data/config/environments/test.rb +17 -17
  62. data/config/routes.rb +18 -18
  63. data/lib/active_record_stub.rb +31 -31
  64. data/lib/bluecloth_tweaked.rb +1127 -0
  65. data/lib/diff.rb +444 -444
  66. data/lib/instiki_errors.rb +14 -14
  67. data/lib/rdocsupport.rb +151 -151
  68. data/lib/redcloth_for_tex.rb +736 -736
  69. data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
  70. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
  71. data/natives/osx/desktop_launcher/Credits.html +15 -15
  72. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
  73. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
  74. data/natives/osx/desktop_launcher/Info.plist +12 -12
  75. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
  76. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
  77. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
  78. data/natives/osx/desktop_launcher/main.mm +14 -14
  79. data/natives/osx/desktop_launcher/version.plist +16 -16
  80. data/public/404.html +5 -5
  81. data/public/500.html +5 -5
  82. data/public/dispatch.rb +9 -9
  83. data/public/javascripts/edit_web.js +52 -52
  84. data/public/javascripts/prototype.js +336 -336
  85. data/public/stylesheets/instiki.css +222 -222
  86. data/script/breakpointer +4 -4
  87. data/script/server +93 -93
  88. metadata +4 -3
@@ -1,141 +1,141 @@
1
- require 'wiki_words'
2
- require 'chunks/chunk'
3
- require 'chunks/wiki'
4
- require 'cgi'
5
-
6
- # Contains all the methods for finding and replacing wiki related links.
7
- module WikiChunk
8
- include Chunk
9
-
10
- # A wiki reference is the top-level class for anything that refers to
11
- # another wiki page.
12
- class WikiReference < Chunk::Abstract
13
-
14
- # Name of the referenced page
15
- attr_reader :page_name
16
-
17
- # the referenced page
18
- def refpage
19
- @content.web.pages[@page_name]
20
- end
21
-
22
- end
23
-
24
- # A wiki link is the top-level class for links that refers to
25
- # another wiki page.
26
- class WikiLink < WikiReference
27
-
28
- attr_reader :link_text, :link_type
29
-
30
- def initialize(match_data, content)
31
- super
32
- @link_type = :show
33
- end
34
-
35
- def self.apply_to(content)
36
- content.gsub!( self.pattern ) do |matched_text|
37
- chunk = self.new($~, content)
38
- if chunk.textile_url?
39
- # do not substitute
40
- matched_text
41
- else
42
- content.add_chunk(chunk)
43
- chunk.mask
44
- end
45
- end
46
- end
47
-
48
- # the referenced page
49
- def refpage
50
- @content.web.pages[@page_name]
51
- end
52
-
53
- def textile_url?
54
- not @textile_link_suffix.nil?
55
- end
56
-
57
- end
58
-
59
- # This chunk matches a WikiWord. WikiWords can be escaped
60
- # by prepending a '\'. When this is the case, the +escaped_text+
61
- # method will return the WikiWord instead of the usual +nil+.
62
- # The +page_name+ method returns the matched WikiWord.
63
- class Word < WikiLink
64
-
65
- attr_reader :escaped_text
66
-
67
- unless defined? WIKI_WORD
68
- WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
69
- end
70
-
71
- def self.pattern
72
- WIKI_WORD
73
- end
74
-
75
- def initialize(match_data, content)
76
- super
77
- @textile_link_suffix, @escape, @page_name = match_data[1..3]
78
- if @escape
79
- @unmask_mode = :escape
80
- @escaped_text = @page_name
81
- else
82
- @escaped_text = nil
83
- end
84
- @link_text = WikiWords.separate(@page_name)
85
- @unmask_text = (@escaped_text || @content.page_link(@page_name, @link_text, @link_type))
86
- end
87
-
88
- end
89
-
90
- # This chunk handles [[bracketted wiki words]] and
91
- # [[AliasedWords|aliased wiki words]]. The first part of an
92
- # aliased wiki word must be a WikiWord. If the WikiWord
93
- # is aliased, the +link_text+ field will contain the
94
- # alias, otherwise +link_text+ will contain the entire
95
- # contents within the double brackets.
96
- #
97
- # NOTE: This chunk must be tested before WikiWord since
98
- # a WikiWords can be a substring of a WikiLink.
99
- class Link < WikiLink
100
-
101
- unless defined? WIKI_LINK
102
- WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
103
- LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
104
- ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
105
- end
106
-
107
- def self.pattern() WIKI_LINK end
108
-
109
- def initialize(match_data, content)
110
- super
111
- @textile_link_suffix, @page_name = match_data[1..2]
112
- @link_text = @page_name
113
- separate_link_type
114
- separate_alias
115
- @unmask_text = @content.page_link(@page_name, @link_text, @link_type)
116
- end
117
-
118
- private
119
-
120
- # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
121
- # this means a link to a picture or a file
122
- def separate_link_type
123
- link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
124
- if link_type_match
125
- @link_text = @page_name = link_type_match[1]
126
- @link_type = link_type_match[2..3].compact[0].to_sym
127
- end
128
- end
129
-
130
- # link text may be different from page name. this will look like [[actual page|link text]]
131
- def separate_alias
132
- alias_match = ALIAS_SEPARATION.match(@page_name)
133
- if alias_match
134
- @page_name, @link_text = alias_match[1..2]
135
- end
136
- # note that [[filename|link text:file]] is also supported
137
- end
138
-
139
- end
140
-
141
- end
1
+ require 'wiki_words'
2
+ require 'chunks/chunk'
3
+ require 'chunks/wiki'
4
+ require 'cgi'
5
+
6
+ # Contains all the methods for finding and replacing wiki related links.
7
+ module WikiChunk
8
+ include Chunk
9
+
10
+ # A wiki reference is the top-level class for anything that refers to
11
+ # another wiki page.
12
+ class WikiReference < Chunk::Abstract
13
+
14
+ # Name of the referenced page
15
+ attr_reader :page_name
16
+
17
+ # the referenced page
18
+ def refpage
19
+ @content.web.pages[@page_name]
20
+ end
21
+
22
+ end
23
+
24
+ # A wiki link is the top-level class for links that refers to
25
+ # another wiki page.
26
+ class WikiLink < WikiReference
27
+
28
+ attr_reader :link_text, :link_type
29
+
30
+ def initialize(match_data, content)
31
+ super
32
+ @link_type = :show
33
+ end
34
+
35
+ def self.apply_to(content)
36
+ content.gsub!( self.pattern ) do |matched_text|
37
+ chunk = self.new($~, content)
38
+ if chunk.textile_url?
39
+ # do not substitute
40
+ matched_text
41
+ else
42
+ content.add_chunk(chunk)
43
+ chunk.mask
44
+ end
45
+ end
46
+ end
47
+
48
+ # the referenced page
49
+ def refpage
50
+ @content.web.pages[@page_name]
51
+ end
52
+
53
+ def textile_url?
54
+ not @textile_link_suffix.nil?
55
+ end
56
+
57
+ end
58
+
59
+ # This chunk matches a WikiWord. WikiWords can be escaped
60
+ # by prepending a '\'. When this is the case, the +escaped_text+
61
+ # method will return the WikiWord instead of the usual +nil+.
62
+ # The +page_name+ method returns the matched WikiWord.
63
+ class Word < WikiLink
64
+
65
+ attr_reader :escaped_text
66
+
67
+ unless defined? WIKI_WORD
68
+ WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
69
+ end
70
+
71
+ def self.pattern
72
+ WIKI_WORD
73
+ end
74
+
75
+ def initialize(match_data, content)
76
+ super
77
+ @textile_link_suffix, @escape, @page_name = match_data[1..3]
78
+ if @escape
79
+ @unmask_mode = :escape
80
+ @escaped_text = @page_name
81
+ else
82
+ @escaped_text = nil
83
+ end
84
+ @link_text = WikiWords.separate(@page_name)
85
+ @unmask_text = (@escaped_text || @content.page_link(@page_name, @link_text, @link_type))
86
+ end
87
+
88
+ end
89
+
90
+ # This chunk handles [[bracketted wiki words]] and
91
+ # [[AliasedWords|aliased wiki words]]. The first part of an
92
+ # aliased wiki word must be a WikiWord. If the WikiWord
93
+ # is aliased, the +link_text+ field will contain the
94
+ # alias, otherwise +link_text+ will contain the entire
95
+ # contents within the double brackets.
96
+ #
97
+ # NOTE: This chunk must be tested before WikiWord since
98
+ # a WikiWords can be a substring of a WikiLink.
99
+ class Link < WikiLink
100
+
101
+ unless defined? WIKI_LINK
102
+ WIKI_LINK = /(":)?\[\[([^\]]+)\]\]/
103
+ LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8')
104
+ ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8')
105
+ end
106
+
107
+ def self.pattern() WIKI_LINK end
108
+
109
+ def initialize(match_data, content)
110
+ super
111
+ @textile_link_suffix, @page_name = match_data[1..2]
112
+ @link_text = @page_name
113
+ separate_link_type
114
+ separate_alias
115
+ @unmask_text = @content.page_link(@page_name, @link_text, @link_type)
116
+ end
117
+
118
+ private
119
+
120
+ # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]],
121
+ # this means a link to a picture or a file
122
+ def separate_link_type
123
+ link_type_match = LINK_TYPE_SEPARATION.match(@page_name)
124
+ if link_type_match
125
+ @link_text = @page_name = link_type_match[1]
126
+ @link_type = link_type_match[2..3].compact[0].to_sym
127
+ end
128
+ end
129
+
130
+ # link text may be different from page name. this will look like [[actual page|link text]]
131
+ def separate_alias
132
+ alias_match = ALIAS_SEPARATION.match(@page_name)
133
+ if alias_match
134
+ @page_name, @link_text = alias_match[1..2]
135
+ end
136
+ # note that [[filename|link text:file]] is also supported
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -1,58 +1,58 @@
1
- require 'fileutils'
2
- require 'instiki_errors'
3
-
4
- class FileYard
5
-
6
- attr_reader :files_path
7
-
8
- def initialize(files_path, max_upload_size)
9
- @files_path = files_path
10
- @max_upload_size = max_upload_size
11
- FileUtils.mkdir_p(files_path) unless File.exist?(files_path)
12
- @files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
13
- end
14
-
15
- def upload_file(name, io)
16
- sanitize_file_name(name)
17
- if io.kind_of?(Tempfile)
18
- io.close
19
- check_upload_size(io.size)
20
- FileUtils.mv(io.path, file_path(name))
21
- else
22
- content = io.read
23
- check_upload_size(content.length)
24
- File.open(file_path(name), 'wb') { |f| f.write(content) }
25
- end
26
- # just in case, restrict read access and prohibit write access to the uploaded file
27
- FileUtils.chmod(0440, file_path(name))
28
- end
29
-
30
- def files
31
- Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
32
- end
33
-
34
- def has_file?(name)
35
- files.include?(name)
36
- end
37
-
38
- def file_path(name)
39
- "#{files_path}/#{name}"
40
- end
41
-
42
- SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
43
-
44
- def sanitize_file_name(name)
45
- unless name =~ SANE_FILE_NAME
46
- raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
47
- "Only latin characters, digits, dots, underscores and dashes are accepted.")
48
- end
49
- end
50
-
51
- def check_upload_size(actual_upload_size)
52
- if actual_upload_size > @max_upload_size.kilobytes
53
- raise Instiki::ValidationError.new("Uploaded file size (#{actual_upload_size / 1024} " +
54
- "kbytes) exceeds the maximum (#{@max_upload_size} kbytes) set for this wiki")
55
- end
56
- end
57
-
58
- end
1
+ require 'fileutils'
2
+ require 'instiki_errors'
3
+
4
+ class FileYard
5
+
6
+ attr_reader :files_path
7
+
8
+ def initialize(files_path, max_upload_size)
9
+ @files_path = files_path
10
+ @max_upload_size = max_upload_size
11
+ FileUtils.mkdir_p(files_path) unless File.exist?(files_path)
12
+ @files = Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path) }.compact
13
+ end
14
+
15
+ def upload_file(name, io)
16
+ sanitize_file_name(name)
17
+ if io.kind_of?(Tempfile)
18
+ io.close
19
+ check_upload_size(io.size)
20
+ FileUtils.mv(io.path, file_path(name))
21
+ else
22
+ content = io.read
23
+ check_upload_size(content.length)
24
+ File.open(file_path(name), 'wb') { |f| f.write(content) }
25
+ end
26
+ # just in case, restrict read access and prohibit write access to the uploaded file
27
+ FileUtils.chmod(0440, file_path(name))
28
+ end
29
+
30
+ def files
31
+ Dir["#{files_path}/*"].collect{|path| File.basename(path) if File.file?(path)}.compact
32
+ end
33
+
34
+ def has_file?(name)
35
+ files.include?(name)
36
+ end
37
+
38
+ def file_path(name)
39
+ "#{files_path}/#{name}"
40
+ end
41
+
42
+ SANE_FILE_NAME = /[-_\.A-Za-z0-9]{1,255}/
43
+
44
+ def sanitize_file_name(name)
45
+ unless name =~ SANE_FILE_NAME
46
+ raise Instiki::ValidationError.new("Invalid file name: '#{name}'.\n" +
47
+ "Only latin characters, digits, dots, underscores and dashes are accepted.")
48
+ end
49
+ end
50
+
51
+ def check_upload_size(actual_upload_size)
52
+ if actual_upload_size > @max_upload_size.kilobytes
53
+ raise Instiki::ValidationError.new("Uploaded file size (#{actual_upload_size / 1024} " +
54
+ "kbytes) exceeds the maximum (#{@max_upload_size} kbytes) set for this wiki")
55
+ end
56
+ end
57
+
58
+ end
data/app/models/page.rb CHANGED
@@ -1,112 +1,112 @@
1
- require 'date'
2
- require 'page_lock'
3
- require 'revision'
4
- require 'wiki_words'
5
- require 'chunks/wiki'
6
-
7
- class Page
8
- include PageLock
9
-
10
- attr_reader :name, :web
11
- attr_accessor :revisions
12
-
13
- def initialize(web, name, content, created_at, author)
14
- @web, @name, @revisions = web, name, []
15
- revise(content, created_at, author)
16
- end
17
-
18
- def revise(content, created_at, author)
19
-
20
- if not @revisions.empty? and content == @revisions.last.content
21
- raise Instiki::ValidationError.new(
22
- "You have tried to save page '#{name}' without changing its content")
23
- end
24
-
25
- # A user may change a page, look at it and make some more changes - several times.
26
- # Not to record every such iteration as a new revision, if the previous revision was done
27
- # by the same author, not more than 30 minutes ago, then update the last revision instead of
28
- # creating a new one
29
- if !@revisions.empty? && continous_revision?(created_at, author)
30
- @revisions.last.created_at = created_at
31
- @revisions.last.content = content
32
- @revisions.last.clear_display_cache
33
- else
34
- @revisions << Revision.new(self, @revisions.length, content, created_at, author)
35
- end
36
-
37
- self.revisions.last.force_rendering
38
- # at this point the page may not be inserted in the web yet, and therefore
39
- # references to the page itself are rendered as "unresolved". Clearing the cache allows
40
- # the page to re-render itself once again, hopefully _after_ it is inserted in the web
41
- self.revisions.last.clear_display_cache
42
-
43
- @web.refresh_pages_with_references(@name) if @revisions.length == 1
44
- end
45
-
46
- def rollback(revision_number, created_at, author_ip = nil)
47
- roll_back_revision = @revisions[revision_number].dup
48
- revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
49
- end
50
-
51
- def revisions?
52
- revisions.length > 1
53
- end
54
-
55
- def revised_on
56
- created_on
57
- end
58
-
59
- def in_category?(cat)
60
- cat.nil? || cat.empty? || categories.include?(cat)
61
- end
62
-
63
- def categories
64
- display_content.find_chunks(Category).map { |cat| cat.list }.flatten
65
- end
66
-
67
- def authors
68
- revisions.collect { |rev| rev.author }
69
- end
70
-
71
- def references
72
- @web.select.pages_that_reference(name)
73
- end
74
-
75
- def linked_from
76
- @web.select.pages_that_link_to(name)
77
- end
78
-
79
- def included_from
80
- @web.select.pages_that_include(name)
81
- end
82
-
83
- # Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
84
- def plain_name
85
- @web.brackets_only ? name : WikiWords.separate(name)
86
- end
87
-
88
- # used to build chunk ids.
89
- def id
90
- @id ||= name.unpack('H*').first
91
- end
92
-
93
- def link(options = {})
94
- @web.make_link(name, nil, options)
95
- end
96
-
97
- def author_link(options = {})
98
- @web.make_link(author, nil, options)
99
- end
100
-
101
- private
102
-
103
- def continous_revision?(created_at, author)
104
- @revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
105
- end
106
-
107
- # Forward method calls to the current revision, so the page responds to all revision calls
108
- def method_missing(method_symbol)
109
- revisions.last.send(method_symbol)
110
- end
111
-
112
- end
1
+ require 'date'
2
+ require 'page_lock'
3
+ require 'revision'
4
+ require 'wiki_words'
5
+ require 'chunks/wiki'
6
+
7
+ class Page
8
+ include PageLock
9
+
10
+ attr_reader :name, :web
11
+ attr_accessor :revisions
12
+
13
+ def initialize(web, name, content, created_at, author)
14
+ @web, @name, @revisions = web, name, []
15
+ revise(content, created_at, author)
16
+ end
17
+
18
+ def revise(content, created_at, author)
19
+
20
+ if not @revisions.empty? and content == @revisions.last.content
21
+ raise Instiki::ValidationError.new(
22
+ "You have tried to save page '#{name}' without changing its content")
23
+ end
24
+
25
+ # A user may change a page, look at it and make some more changes - several times.
26
+ # Not to record every such iteration as a new revision, if the previous revision was done
27
+ # by the same author, not more than 30 minutes ago, then update the last revision instead of
28
+ # creating a new one
29
+ if !@revisions.empty? && continous_revision?(created_at, author)
30
+ @revisions.last.created_at = created_at
31
+ @revisions.last.content = content
32
+ @revisions.last.clear_display_cache
33
+ else
34
+ @revisions << Revision.new(self, @revisions.length, content, created_at, author)
35
+ end
36
+
37
+ self.revisions.last.force_rendering
38
+ # at this point the page may not be inserted in the web yet, and therefore
39
+ # references to the page itself are rendered as "unresolved". Clearing the cache allows
40
+ # the page to re-render itself once again, hopefully _after_ it is inserted in the web
41
+ self.revisions.last.clear_display_cache
42
+
43
+ @web.refresh_pages_with_references(@name) if @revisions.length == 1
44
+ end
45
+
46
+ def rollback(revision_number, created_at, author_ip = nil)
47
+ roll_back_revision = @revisions[revision_number].dup
48
+ revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
49
+ end
50
+
51
+ def revisions?
52
+ revisions.length > 1
53
+ end
54
+
55
+ def revised_on
56
+ created_on
57
+ end
58
+
59
+ def in_category?(cat)
60
+ cat.nil? || cat.empty? || categories.include?(cat)
61
+ end
62
+
63
+ def categories
64
+ display_content.find_chunks(Category).map { |cat| cat.list }.flatten
65
+ end
66
+
67
+ def authors
68
+ revisions.collect { |rev| rev.author }
69
+ end
70
+
71
+ def references
72
+ @web.select.pages_that_reference(name)
73
+ end
74
+
75
+ def linked_from
76
+ @web.select.pages_that_link_to(name)
77
+ end
78
+
79
+ def included_from
80
+ @web.select.pages_that_include(name)
81
+ end
82
+
83
+ # Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
84
+ def plain_name
85
+ @web.brackets_only ? name : WikiWords.separate(name)
86
+ end
87
+
88
+ # used to build chunk ids.
89
+ def id
90
+ @id ||= name.unpack('H*').first
91
+ end
92
+
93
+ def link(options = {})
94
+ @web.make_link(name, nil, options)
95
+ end
96
+
97
+ def author_link(options = {})
98
+ @web.make_link(author, nil, options)
99
+ end
100
+
101
+ private
102
+
103
+ def continous_revision?(created_at, author)
104
+ @revisions.last.author == author && @revisions.last.created_at + 30.minutes > created_at
105
+ end
106
+
107
+ # Forward method calls to the current revision, so the page responds to all revision calls
108
+ def method_missing(method_symbol)
109
+ revisions.last.send(method_symbol)
110
+ end
111
+
112
+ end