instiki 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/CHANGELOG +165 -0
  2. data/README +68 -172
  3. data/app/controllers/admin_controller.rb +94 -0
  4. data/app/controllers/application.rb +131 -0
  5. data/app/controllers/file_controller.rb +129 -0
  6. data/app/controllers/wiki_controller.rb +354 -0
  7. data/{libraries/view_helper.rb → app/helpers/application_helper.rb} +68 -33
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -31
  10. data/app/models/chunks/chunk.rb +86 -20
  11. data/app/models/chunks/engines.rb +54 -38
  12. data/app/models/chunks/include.rb +41 -29
  13. data/app/models/chunks/literal.rb +31 -19
  14. data/app/models/chunks/nowiki.rb +28 -31
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -97
  17. data/app/models/chunks/wiki.rb +141 -82
  18. data/app/models/file_yard.rb +58 -0
  19. data/app/models/page.rb +112 -86
  20. data/app/models/page_lock.rb +22 -23
  21. data/app/models/page_set.rb +89 -64
  22. data/app/models/revision.rb +123 -90
  23. data/app/models/web.rb +176 -89
  24. data/app/models/wiki_content.rb +207 -105
  25. data/app/models/wiki_service.rb +233 -83
  26. data/app/models/wiki_words.rb +23 -25
  27. data/app/views/{wiki/new_system.rhtml → admin/create_system.rhtml} +83 -78
  28. data/app/views/{wiki/new_web.rhtml → admin/create_web.rhtml} +69 -64
  29. data/app/views/admin/edit_web.rhtml +136 -0
  30. data/app/views/file/file.rhtml +19 -0
  31. data/app/views/file/import.rhtml +23 -0
  32. data/app/views/layouts/default.rhtml +85 -0
  33. data/app/views/markdown_help.rhtml +12 -16
  34. data/app/views/mixed_help.rhtml +7 -0
  35. data/app/views/navigation.rhtml +30 -19
  36. data/app/views/rdoc_help.rhtml +12 -16
  37. data/app/views/textile_help.rhtml +24 -28
  38. data/app/views/wiki/authors.rhtml +11 -13
  39. data/app/views/wiki/edit.rhtml +39 -31
  40. data/app/views/wiki/export.rhtml +12 -14
  41. data/app/views/wiki/feeds.rhtml +14 -10
  42. data/app/views/wiki/list.rhtml +64 -57
  43. data/app/views/wiki/locked.rhtml +23 -14
  44. data/app/views/wiki/login.rhtml +14 -11
  45. data/app/views/wiki/new.rhtml +31 -27
  46. data/app/views/wiki/page.rhtml +115 -81
  47. data/app/views/wiki/print.rhtml +14 -16
  48. data/app/views/wiki/published.rhtml +9 -10
  49. data/app/views/wiki/recently_revised.rhtml +27 -30
  50. data/app/views/wiki/revision.rhtml +103 -81
  51. data/app/views/wiki/rollback.rhtml +14 -9
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -15
  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 -13
  57. data/app/views/wiki_words_help.rhtml +9 -8
  58. data/config/environment.rb +82 -0
  59. data/config/environments/development.rb +5 -0
  60. data/config/environments/production.rb +4 -0
  61. data/config/environments/test.rb +17 -0
  62. data/config/routes.rb +18 -0
  63. data/instiki +6 -67
  64. data/instiki.rb +3 -0
  65. data/lib/active_record_stub.rb +31 -0
  66. data/{libraries/diff → lib}/diff.rb +444 -475
  67. data/lib/instiki_errors.rb +15 -0
  68. data/{libraries → lib}/rdocsupport.rb +151 -155
  69. data/lib/redcloth_for_tex.rb +736 -0
  70. data/natives/osx/desktop_launcher/AppDelegate.h +18 -0
  71. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -0
  72. data/natives/osx/desktop_launcher/Credits.html +16 -0
  73. data/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings +0 -0
  74. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +13 -0
  75. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -0
  76. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib +0 -0
  77. data/natives/osx/desktop_launcher/Info.plist +13 -0
  78. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -0
  79. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -0
  80. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -0
  81. data/natives/osx/desktop_launcher/main.mm +14 -0
  82. data/natives/osx/desktop_launcher/version.plist +16 -0
  83. data/public/404.html +6 -0
  84. data/public/500.html +6 -0
  85. data/public/dispatch.rb +10 -0
  86. data/public/favicon.ico +0 -0
  87. data/public/javascripts/edit_web.js +52 -0
  88. data/public/javascripts/prototype.js +336 -0
  89. data/{app/views/static_style_sheet.rhtml → public/stylesheets/instiki.css} +221 -198
  90. data/script/breakpointer +4 -0
  91. data/script/server +93 -0
  92. metadata +59 -32
  93. data/app/controllers/wiki.rb +0 -389
  94. data/app/models/chunks/match.rb +0 -19
  95. data/app/views/bottom.rhtml +0 -4
  96. data/app/views/top.rhtml +0 -49
  97. data/app/views/wiki/edit_web.rhtml +0 -138
  98. data/libraries/action_controller_servlet.rb +0 -177
  99. data/libraries/erb.rb +0 -490
  100. data/libraries/madeleine_service.rb +0 -68
  101. data/libraries/redcloth_for_tex.rb +0 -869
  102. data/libraries/web_controller_server.rb +0 -81
@@ -0,0 +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,86 +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
- CONTINOUS_REVISION_PERIOD = 30 * 60 # 30 minutes
11
-
12
- attr_reader :name, :revisions, :web
13
-
14
- def initialize(web, name, content, created_at, author)
15
- @web, @name, @revisions = web, name, []
16
- revise(content, created_at, author)
17
- end
18
-
19
- def revise(content, created_at, author)
20
- if !@revisions.empty? && continous_revision?(created_at, author)
21
- @revisions.last.created_at = Time.now
22
- @revisions.last.content = content
23
- @revisions.last.clear_display_cache
24
- else
25
- @revisions << Revision.new(self, @revisions.length, content, created_at, author)
26
- end
27
-
28
- web.refresh_pages_with_references(name) if @revisions.length == 1
29
- end
30
-
31
- def rollback(revision_number, created_at, author_ip = nil)
32
- roll_back_revision = @revisions[revision_number].dup
33
- revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
34
- end
35
-
36
- def revisions?
37
- revisions.length > 1
38
- end
39
-
40
- def revised_on
41
- created_on
42
- end
43
-
44
- def pretty_revised_on
45
- DateTime.new(revised_on.year, revised_on.mon, revised_on.day).strftime "%B %e, %Y"
46
- end
47
-
48
- def in_category?(cat)
49
- cat.nil? || cat.empty? || categories.include?(cat)
50
- end
51
-
52
- def categories
53
- display_content.find_chunks(Category).map { |cat| cat.list }.flatten
54
- end
55
-
56
- def authors
57
- revisions.collect { |rev| rev.author }
58
- end
59
-
60
- def references
61
- web.select.pages_that_reference(name)
62
- end
63
-
64
- # Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
65
- def plain_name
66
- WikiWords.separate(name, web.brackets_only)
67
- end
68
-
69
- def link(options = {})
70
- web.make_link(name, nil, options)
71
- end
72
-
73
- def author_link(options = {})
74
- web.make_link(author, nil, options)
75
- end
76
-
77
- private
78
- def continous_revision?(created_at, author)
79
- @revisions.last.author == author && @revisions.last.created_at + CONTINOUS_REVISION_PERIOD > created_at
80
- end
81
-
82
- # Forward method calls to the current revision, so the page responds to all revision calls
83
- def method_missing(method_symbol)
84
- revisions.last.send(method_symbol)
85
- end
86
- 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
@@ -1,24 +1,23 @@
1
- # Contains all the lock methods to be mixed in with the page
2
- module PageLock
3
- LOCKING_PERIOD = 30 * 60 # 30 minutes
4
-
5
- def lock(time, locked_by)
6
- @locked_at, @locked_by = time, locked_by
7
- end
8
-
9
- def lock_duration(time)
10
- ((time - @locked_at) / 60).to_i unless @locked_at.nil?
11
- end
12
-
13
- def unlock
14
- @locked_at = nil
15
- end
16
-
17
- def locked?(comparison_time)
18
- @locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
19
- end
20
-
21
- def locked_by_link
22
- web.make_link(@locked_by)
23
- end
1
+ # Contains all the lock methods to be mixed in with the page
2
+ module PageLock
3
+ LOCKING_PERIOD = 30 * 60 # 30 minutes
4
+
5
+ attr_reader :locked_by
6
+
7
+ def lock(time, locked_by)
8
+ @locked_at, @locked_by = time, locked_by
9
+ end
10
+
11
+ def lock_duration(time)
12
+ ((time - @locked_at) / 60).to_i unless @locked_at.nil?
13
+ end
14
+
15
+ def unlock
16
+ @locked_at = nil
17
+ end
18
+
19
+ def locked?(comparison_time)
20
+ @locked_at + LOCKING_PERIOD > comparison_time unless @locked_at.nil?
21
+ end
22
+
24
23
  end
@@ -1,64 +1,89 @@
1
- # Container for a set of pages with methods for manipulation.
2
- class PageSet < Array
3
- attr_reader :web
4
-
5
- def initialize(web, pages, accept_proc)
6
- @web = web
7
- if accept_proc.nil? then
8
- super(pages)
9
- else
10
- super(pages.select { |page| accept_proc[page] })
11
- end
12
- end
13
-
14
- def most_recent_revision
15
- self.sort_by { |page| [page.created_at] }.reverse.first.created_at
16
- end
17
-
18
- def by_name
19
- self.sort_by { |page| [page.name] }
20
- end
21
-
22
- def by_revision
23
- self.sort_by { |page| [page.created_at] }.reverse
24
- end
25
-
26
- def pages_that_reference(page_name)
27
- self.select { |page| page.wiki_words.include?(page_name) }
28
- end
29
-
30
- def pages_authored_by(author)
31
- self.select { |page| page.authors.include?(author) }
32
- end
33
-
34
- def characters
35
- self.inject(0) { |chars,page| chars += page.content.size }
36
- end
37
-
38
- # Returns all the orphaned pages in this page set. That is,
39
- # pages in this set for which there is no reference in the web.
40
- # The HomePage and author pages are always assumed to have
41
- # references and so cannot be orphans
42
- def orphaned_pages
43
- references = web.select.wiki_words + ["HomePage"] + web.select.authors
44
- self.reject { |page| references.include?(page.name) }
45
- end
46
-
47
- # Returns all the wiki words in this page set for which
48
- # there are no pages in this page set's web
49
- def wanted_pages
50
- wiki_words - web.select.names
51
- end
52
-
53
- def names
54
- self.map { |page| page.name }
55
- end
56
-
57
- def wiki_words
58
- self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
59
- end
60
-
61
- def authors
62
- self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
63
- end
64
- end
1
+ # Container for a set of pages with methods for manipulation.
2
+
3
+ class PageSet < Array
4
+ attr_reader :web
5
+
6
+ def initialize(web, pages = nil, condition = nil)
7
+ @web = web
8
+ # if pages is not specified, make a list of all pages in the web
9
+ if pages.nil?
10
+ super(web.pages.values)
11
+ # otherwise use specified pages and condition to produce a set of pages
12
+ elsif condition.nil?
13
+ super(pages)
14
+ else
15
+ super(pages.select { |page| condition[page] })
16
+ end
17
+ end
18
+
19
+ def most_recent_revision
20
+ self.map { |page| page.created_at }.max || Time.at(0)
21
+ end
22
+
23
+
24
+ def by_name
25
+ PageSet.new(@web, sort_by { |page| page.name })
26
+ end
27
+
28
+ alias :sort :by_name
29
+
30
+ def by_revision
31
+ PageSet.new(@web, sort_by { |page| page.created_at }).reverse
32
+ end
33
+
34
+ def pages_that_reference(page_name)
35
+ self.select { |page| page.wiki_references.include?(page_name) }
36
+ end
37
+
38
+ def pages_that_link_to(page_name)
39
+ self.select { |page| page.wiki_words.include?(page_name) }
40
+ end
41
+
42
+ def pages_that_include(page_name)
43
+ self.select { |page| page.wiki_includes.include?(page_name) }
44
+ end
45
+
46
+ def pages_authored_by(author)
47
+ self.select { |page| page.authors.include?(author) }
48
+ end
49
+
50
+ def characters
51
+ self.inject(0) { |chars,page| chars += page.content.size }
52
+ end
53
+
54
+ # Returns all the orphaned pages in this page set. That is,
55
+ # pages in this set for which there is no reference in the web.
56
+ # The HomePage and author pages are always assumed to have
57
+ # references and so cannot be orphans
58
+ # Pages that refer to themselves and have no links from outside are oprphans.
59
+ def orphaned_pages
60
+ never_orphans = web.select.authors + ['HomePage']
61
+ self.select { |page|
62
+ if never_orphans.include? page.name
63
+ false
64
+ else
65
+ references = pages_that_reference(page.name)
66
+ references.empty? or references == [page]
67
+ end
68
+ }
69
+ end
70
+
71
+ # Returns all the wiki words in this page set for which
72
+ # there are no pages in this page set's web
73
+ def wanted_pages
74
+ wiki_words - web.select.names
75
+ end
76
+
77
+ def names
78
+ self.map { |page| page.name }
79
+ end
80
+
81
+ def wiki_words
82
+ self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
83
+ end
84
+
85
+ def authors
86
+ self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
87
+ end
88
+
89
+ end