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.
- data/CHANGELOG +174 -165
- data/README +68 -68
- data/app/controllers/admin_controller.rb +94 -94
- data/app/controllers/application.rb +135 -131
- data/app/controllers/file_controller.rb +129 -129
- data/app/controllers/wiki_controller.rb +354 -354
- data/app/helpers/application_helper.rb +68 -68
- data/app/models/author.rb +3 -3
- data/app/models/chunks/category.rb +33 -33
- data/app/models/chunks/chunk.rb +86 -86
- data/app/models/chunks/engines.rb +61 -54
- data/app/models/chunks/include.rb +41 -41
- data/app/models/chunks/literal.rb +31 -31
- data/app/models/chunks/nowiki.rb +28 -28
- data/app/models/chunks/test.rb +18 -18
- data/app/models/chunks/uri.rb +182 -182
- data/app/models/chunks/wiki.rb +141 -141
- data/app/models/file_yard.rb +58 -58
- data/app/models/page.rb +112 -112
- data/app/models/page_lock.rb +22 -22
- data/app/models/page_set.rb +89 -89
- data/app/models/revision.rb +123 -123
- data/app/models/web.rb +182 -176
- data/app/models/wiki_content.rb +207 -207
- data/app/models/wiki_service.rb +233 -233
- data/app/models/wiki_words.rb +23 -23
- data/app/views/admin/create_system.rhtml +83 -83
- data/app/views/admin/create_web.rhtml +69 -69
- data/app/views/admin/edit_web.rhtml +137 -136
- data/app/views/file/file.rhtml +18 -18
- data/app/views/file/import.rhtml +22 -22
- data/app/views/layouts/default.rhtml +86 -85
- data/app/views/markdown_help.rhtml +12 -12
- data/app/views/mixed_help.rhtml +6 -6
- data/app/views/navigation.rhtml +30 -30
- data/app/views/rdoc_help.rhtml +12 -12
- data/app/views/textile_help.rhtml +24 -24
- data/app/views/wiki/authors.rhtml +11 -11
- data/app/views/wiki/edit.rhtml +39 -39
- data/app/views/wiki/export.rhtml +12 -12
- data/app/views/wiki/feeds.rhtml +14 -14
- data/app/views/wiki/list.rhtml +64 -64
- data/app/views/wiki/locked.rhtml +23 -23
- data/app/views/wiki/login.rhtml +14 -14
- data/app/views/wiki/new.rhtml +31 -31
- data/app/views/wiki/page.rhtml +115 -115
- data/app/views/wiki/print.rhtml +14 -14
- data/app/views/wiki/published.rhtml +9 -9
- data/app/views/wiki/recently_revised.rhtml +26 -26
- data/app/views/wiki/revision.rhtml +103 -103
- data/app/views/wiki/rollback.rhtml +36 -36
- data/app/views/wiki/rss_feed.rhtml +22 -22
- data/app/views/wiki/search.rhtml +38 -38
- data/app/views/wiki/tex.rhtml +22 -22
- data/app/views/wiki/tex_web.rhtml +34 -34
- data/app/views/wiki/web_list.rhtml +18 -18
- data/app/views/wiki_words_help.rhtml +9 -9
- data/config/environment.rb +82 -82
- data/config/environments/development.rb +5 -5
- data/config/environments/production.rb +4 -4
- data/config/environments/test.rb +17 -17
- data/config/routes.rb +18 -18
- data/lib/active_record_stub.rb +31 -31
- data/lib/bluecloth_tweaked.rb +1127 -0
- data/lib/diff.rb +444 -444
- data/lib/instiki_errors.rb +14 -14
- data/lib/rdocsupport.rb +151 -151
- data/lib/redcloth_for_tex.rb +736 -736
- data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
- data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
- data/natives/osx/desktop_launcher/Credits.html +15 -15
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
- data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
- data/natives/osx/desktop_launcher/Info.plist +12 -12
- data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
- data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
- data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
- data/natives/osx/desktop_launcher/main.mm +14 -14
- data/natives/osx/desktop_launcher/version.plist +16 -16
- data/public/404.html +5 -5
- data/public/500.html +5 -5
- data/public/dispatch.rb +9 -9
- data/public/javascripts/edit_web.js +52 -52
- data/public/javascripts/prototype.js +336 -336
- data/public/stylesheets/instiki.css +222 -222
- data/script/breakpointer +4 -4
- data/script/server +93 -93
- metadata +4 -3
data/app/models/chunks/wiki.rb
CHANGED
@@ -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
|
data/app/models/file_yard.rb
CHANGED
@@ -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
|