instiki 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
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