instiki 0.9.2 → 0.10.0

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 (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