Pimki 1.0.092

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 (85) hide show
  1. data/README +158 -0
  2. data/README-PIMKI +87 -0
  3. data/app/controllers/wiki.rb +563 -0
  4. data/app/models/author.rb +4 -0
  5. data/app/models/chunks/category.rb +31 -0
  6. data/app/models/chunks/category_test.rb +21 -0
  7. data/app/models/chunks/chunk.rb +20 -0
  8. data/app/models/chunks/engines.rb +34 -0
  9. data/app/models/chunks/include.rb +29 -0
  10. data/app/models/chunks/literal.rb +19 -0
  11. data/app/models/chunks/match.rb +19 -0
  12. data/app/models/chunks/nowiki.rb +31 -0
  13. data/app/models/chunks/nowiki_test.rb +14 -0
  14. data/app/models/chunks/test.rb +18 -0
  15. data/app/models/chunks/todo.rb +22 -0
  16. data/app/models/chunks/uri.rb +97 -0
  17. data/app/models/chunks/uri_test.rb +92 -0
  18. data/app/models/chunks/wiki.rb +82 -0
  19. data/app/models/chunks/wiki_test.rb +36 -0
  20. data/app/models/page.rb +91 -0
  21. data/app/models/page_lock.rb +24 -0
  22. data/app/models/page_set.rb +73 -0
  23. data/app/models/page_test.rb +76 -0
  24. data/app/models/revision.rb +91 -0
  25. data/app/models/revision_test.rb +252 -0
  26. data/app/models/web.rb +277 -0
  27. data/app/models/web_test.rb +53 -0
  28. data/app/models/wiki_content.rb +113 -0
  29. data/app/models/wiki_service.rb +137 -0
  30. data/app/models/wiki_service_test.rb +15 -0
  31. data/app/models/wiki_words.rb +26 -0
  32. data/app/models/wiki_words_test.rb +12 -0
  33. data/app/views/bottom.rhtml +4 -0
  34. data/app/views/markdown_help.rhtml +16 -0
  35. data/app/views/menu.rhtml +20 -0
  36. data/app/views/navigation.rhtml +26 -0
  37. data/app/views/rdoc_help.rhtml +16 -0
  38. data/app/views/static_style_sheet.rhtml +231 -0
  39. data/app/views/style.rhtml +179 -0
  40. data/app/views/textile_help.rhtml +28 -0
  41. data/app/views/top.rhtml +52 -0
  42. data/app/views/wiki/authors.rhtml +15 -0
  43. data/app/views/wiki/bliki.rhtml +101 -0
  44. data/app/views/wiki/bliki_edit.rhtml +33 -0
  45. data/app/views/wiki/bliki_new.rhtml +61 -0
  46. data/app/views/wiki/bliki_revision.rhtml +51 -0
  47. data/app/views/wiki/edit.rhtml +34 -0
  48. data/app/views/wiki/edit_menu.rhtml +27 -0
  49. data/app/views/wiki/edit_web.rhtml +139 -0
  50. data/app/views/wiki/export.rhtml +14 -0
  51. data/app/views/wiki/feeds.rhtml +10 -0
  52. data/app/views/wiki/list.rhtml +164 -0
  53. data/app/views/wiki/locked.rhtml +14 -0
  54. data/app/views/wiki/login.rhtml +11 -0
  55. data/app/views/wiki/mind.rhtml +39 -0
  56. data/app/views/wiki/new.rhtml +27 -0
  57. data/app/views/wiki/new_system.rhtml +78 -0
  58. data/app/views/wiki/new_web.rhtml +64 -0
  59. data/app/views/wiki/page.rhtml +84 -0
  60. data/app/views/wiki/print.rhtml +16 -0
  61. data/app/views/wiki/published.rhtml +10 -0
  62. data/app/views/wiki/recently_revised.rhtml +31 -0
  63. data/app/views/wiki/revision.rhtml +87 -0
  64. data/app/views/wiki/rss_feed.rhtml +22 -0
  65. data/app/views/wiki/search.rhtml +26 -0
  66. data/app/views/wiki/tex.rhtml +23 -0
  67. data/app/views/wiki/tex_web.rhtml +35 -0
  68. data/app/views/wiki/todo.rhtml +39 -0
  69. data/app/views/wiki/web_list.rhtml +13 -0
  70. data/app/views/wiki_words_help.rhtml +8 -0
  71. data/libraries/action_controller_servlet.rb +177 -0
  72. data/libraries/bluecloth.rb +1127 -0
  73. data/libraries/diff/diff.rb +475 -0
  74. data/libraries/diff/diff_test.rb +80 -0
  75. data/libraries/erb.rb +490 -0
  76. data/libraries/madeleine/automatic.rb +357 -0
  77. data/libraries/madeleine/clock.rb +94 -0
  78. data/libraries/madeleine_service.rb +69 -0
  79. data/libraries/rdocsupport.rb +156 -0
  80. data/libraries/redcloth_for_tex.rb +869 -0
  81. data/libraries/redcloth_for_tex_test.rb +41 -0
  82. data/libraries/view_helper.rb +33 -0
  83. data/libraries/web_controller_server.rb +95 -0
  84. data/pimki.rb +97 -0
  85. metadata +169 -0
@@ -0,0 +1,82 @@
1
+ require 'wiki_words'
2
+ require 'chunks/chunk'
3
+ require 'cgi'
4
+
5
+ # Contains all the methods for finding and replacing wiki related
6
+ # links.
7
+ module WikiChunk
8
+ include Chunk
9
+
10
+ # A wiki link is the top-level class for anything that refers to
11
+ # another wiki page.
12
+ class WikiLink < Chunk::Abstract
13
+ # By default, no escaped text
14
+ def escaped_text() nil end
15
+
16
+ # Delimit the link text with markers to replace later unless
17
+ # the word is escaped. In that case, just return the link text
18
+ def mask(content) escaped_text || pre_mask + link_text + post_mask end
19
+
20
+ def regexp() Regexp.new(pre_mask + '(.*)?' + post_mask) end
21
+
22
+ def revert(content) content.sub!(regexp, text) end
23
+
24
+ # Do not keep this chunk if it is escaped.
25
+ # Otherwise, pass the link procedure a page_name and link_text and
26
+ # get back a string of HTML to replace the mask with.
27
+ def unmask(content)
28
+ return nil if escaped_text
29
+ return self if content.sub!(regexp) { |match| content.page_link(page_name, $1) }
30
+ end
31
+ end
32
+
33
+ # This chunk matches a WikiWord. WikiWords can be escaped
34
+ # by prepending a '\'. When this is the case, the +escaped_text+
35
+ # method will return the WikiWord instead of the usual +nil+.
36
+ # The +page_name+ method returns the matched WikiWord.
37
+ class Word < WikiLink
38
+ def self.pattern
39
+ Regexp.new('(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
40
+ end
41
+
42
+ attr_reader :page_name
43
+
44
+ def initialize(match_data)
45
+ super(match_data)
46
+ @escape = match_data[1]
47
+ @page_name = match_data[2]
48
+ end
49
+
50
+ def escaped_text() (@escape.nil? ? nil : page_name) end
51
+ def link_text() WikiWords.separate(page_name) end
52
+ end
53
+
54
+ # This chunk handles [[bracketted wiki words]] and
55
+ # [[AliasedWords|aliased wiki words]]. The first part of an
56
+ # aliased wiki word must be a WikiWord. If the WikiWord
57
+ # is aliased, the +link_text+ field will contain the
58
+ # alias, otherwise +link_text+ will contain the entire
59
+ # contents within the double brackets.
60
+ #
61
+ # NOTE: This chunk must be tested before WikiWord since
62
+ # a WikiWords can be a substring of a WikiLink.
63
+ class Link < WikiLink
64
+ def self.pattern() /\[\[([^\]]+)\]\]/ end
65
+ ALIASED_LINK_PATTERN= Regexp.new('^(.*)?\|(.*)$', 0, "utf-8")
66
+
67
+ attr_reader :page_name, :link_text
68
+
69
+ def initialize(match_data)
70
+ super(match_data)
71
+
72
+ # If the like is aliased, set the page name to the first bit
73
+ # and the link text to the second, otherwise set both to the
74
+ # contents of the double brackets.
75
+ if match_data[1] =~ ALIASED_LINK_PATTERN
76
+ @page_name, @link_text = $1, $2
77
+ else
78
+ @page_name, @link_text = match_data[1], match_data[1]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ require 'chunks/wiki'
2
+ require 'chunks/match'
3
+ require 'test/unit'
4
+
5
+ class WikiTest < Test::Unit::TestCase
6
+ include ChunkMatch
7
+
8
+ def test_simple
9
+ match(WikiChunk::Word, 'This is a WikiWord okay?', :page_name => 'WikiWord')
10
+ end
11
+
12
+ def test_escaped
13
+ match(WikiChunk::Word, 'Do not link to an \EscapedWord',
14
+ :page_name => 'EscapedWord', :escaped_text => 'EscapedWord'
15
+ )
16
+ end
17
+
18
+ def test_simple_brackets
19
+ match(WikiChunk::Link, 'This is a [[bracketted link]]',
20
+ :page_name => 'bracketted link', :escaped_text => nil
21
+ )
22
+ end
23
+
24
+ def test_complex_brackets
25
+ match(WikiChunk::Link, 'This is a tricky link [[Sperberg-McQueen]]',
26
+ :page_name => 'Sperberg-McQueen', :escaped_text => nil
27
+ )
28
+ end
29
+
30
+ # MDR: I'm not sure how to deal with this case just yet...
31
+ #
32
+ # def test_textile_link
33
+ # assert_no_match(WikiChunk::Word.pattern, '"Here is a special link":SpecialLink')
34
+ # end
35
+
36
+ end
@@ -0,0 +1,91 @@
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_accessor :name, :last_visited, :viewed
13
+ attr_reader :revisions, :web
14
+
15
+ def initialize(web, name, content, created_at, author)
16
+ @web, @name, @revisions = web, name, []
17
+ revise(content, created_at, author)
18
+ end
19
+
20
+ def revise(content, created_at, author)
21
+ if !@revisions.empty? && continous_revision?(created_at, author)
22
+ @revisions.last.created_at = Time.now
23
+ @revisions.last.content = content
24
+ @revisions.last.clear_display_cache
25
+ else
26
+ @revisions << Revision.new(self, @revisions.length, content, created_at, author)
27
+ end
28
+
29
+ web.refresh_pages_with_references(name) if @revisions.length == 1
30
+ end
31
+
32
+ def rollback(revision_number, created_at, author_ip = nil)
33
+ roll_back_revision = @revisions[revision_number].dup
34
+ revise(roll_back_revision.content, created_at, Author.new(roll_back_revision.author, author_ip))
35
+ end
36
+
37
+ def revisions?
38
+ revisions.length > 1
39
+ end
40
+
41
+ def revised_on
42
+ created_on
43
+ end
44
+
45
+ def pretty_revised_on
46
+ DateTime.new(revised_on.year, revised_on.mon, revised_on.day).strftime "%B %e, %Y"
47
+ end
48
+
49
+ def in_category?(cat)
50
+ cat.nil? || cat.empty? || categories.include?(cat)
51
+ end
52
+
53
+ def categories
54
+ display_content.find_chunks(Category).map { |cat| cat.list }.flatten
55
+ end
56
+
57
+ def authors
58
+ revisions.collect { |rev| rev.author }
59
+ end
60
+
61
+ def references
62
+ web.select.pages_that_reference(name)
63
+ end
64
+
65
+ # Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
66
+ def plain_name
67
+ WikiWords.separate(name, web.brackets_only)
68
+ end
69
+
70
+ def link(options = {})
71
+ web.make_link(name, nil, options)
72
+ end
73
+
74
+ def author_link(options = {})
75
+ web.make_link(author, nil, options)
76
+ end
77
+
78
+ def last_visited() @last_visited || revisions.last.created_at end
79
+ def viewed() @viewed ||= 0 end
80
+
81
+
82
+ private
83
+ def continous_revision?(created_at, author)
84
+ @revisions.last.author == author && @revisions.last.created_at + CONTINOUS_REVISION_PERIOD > created_at
85
+ end
86
+
87
+ # Forward method calls to the current revision, so the page responds to all revision calls
88
+ def method_missing(method_symbol)
89
+ revisions.last.send(method_symbol)
90
+ end
91
+ end
@@ -0,0 +1,24 @@
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
24
+ end
@@ -0,0 +1,73 @@
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 by_last_visited #{{{
27
+ self.sort_by { |page| [page.last_visited] }.reverse
28
+ end #}}}
29
+
30
+ def by_most_viewed #{{{
31
+ self.sort_by { |page| [page.viewed] }.reverse
32
+ end #}}}
33
+
34
+
35
+ def pages_that_reference(page_name)
36
+ self.select { |page| page.wiki_words.include?(page_name) }
37
+ end
38
+
39
+ def pages_authored_by(author)
40
+ self.select { |page| page.authors.include?(author) }
41
+ end
42
+
43
+ def characters
44
+ self.inject(0) { |chars,page| chars += page.content.size }
45
+ end
46
+
47
+ # Returns all the orphaned pages in this page set. That is,
48
+ # pages in this set for which there is no reference in the web.
49
+ # The HomePage and author pages are always assumed to have
50
+ # references and so cannot be orphans
51
+ def orphaned_pages
52
+ references = web.select.wiki_words + ["HomePage"] + web.select.authors
53
+ self.reject { |page| references.include?(page.name) }
54
+ end
55
+
56
+ # Returns all the wiki words in this page set for which
57
+ # there are no pages in this page set's web
58
+ def wanted_pages
59
+ wiki_words - web.select.names
60
+ end
61
+
62
+ def names
63
+ self.map { |page| page.name }
64
+ end
65
+
66
+ def wiki_words
67
+ self.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
68
+ end
69
+
70
+ def authors
71
+ self.inject([]) { |authors, page| authors << page.authors }.flatten.uniq.sort
72
+ end
73
+ end
@@ -0,0 +1,76 @@
1
+ require "test/unit"
2
+ require "web"
3
+ require "page"
4
+
5
+ class MockWeb < Web
6
+ def initialize() super('test','test') end
7
+ def [](wiki_word) %w( MyWay ThatWay SmartEngine ).include?(wiki_word) end
8
+ def refresh_pages_with_references(name) end
9
+ end
10
+
11
+ class PageTest < Test::Unit::TestCase
12
+ def setup
13
+ @page = Page.new(
14
+ MockWeb.new,
15
+ "FirstPage",
16
+ "HisWay would be MyWay in kinda ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in that SmartEngineGUI",
17
+ Time.local(2004, 4, 4, 16, 50),
18
+ "DavidHeinemeierHansson"
19
+ )
20
+ end
21
+
22
+ def test_basics
23
+ assert_equal "First Page", @page.plain_name
24
+ assert_equal "April 4, 2004", @page.pretty_revised_on
25
+ end
26
+
27
+ def test_locking
28
+ assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
29
+
30
+ @page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
31
+
32
+ assert @page.locked?(Time.local(2004, 4, 4, 16, 50))
33
+ assert !@page.locked?(Time.local(2004, 4, 4, 17, 1))
34
+
35
+ @page.unlock
36
+
37
+ assert !@page.locked?(Time.local(2004, 4, 4, 16, 50))
38
+ end
39
+
40
+ def test_locking_duration
41
+ @page.lock(Time.local(2004, 4, 4, 16, 30), "DavidHeinemeierHansson")
42
+
43
+ assert_equal 15, @page.lock_duration(Time.local(2004, 4, 4, 16, 45))
44
+ end
45
+
46
+ def test_revision
47
+ @page.revise("HisWay would be MyWay in kinda lame", Time.local(2004, 4, 4, 16, 55), "MarianneSyhler")
48
+ assert_equal 2, @page.revisions.length, "Should have two revisions"
49
+ assert_equal "MarianneSyhler", @page.author, "Mary should be the author now"
50
+ assert_equal "DavidHeinemeierHansson", @page.revisions.first.author, "David was the first author"
51
+ end
52
+
53
+ def test_rollback
54
+ @page.revise("spot two", Time.now, "David")
55
+ @page.revise("spot three", Time.now + 2000, "David")
56
+ assert_equal 3, @page.revisions.length, "Should have three revisions"
57
+ @page.rollback(1, Time.now)
58
+ assert_equal "spot two", @page.content
59
+ end
60
+
61
+ def test_continous_revision
62
+ @page.revise("HisWay would be MyWay in kinda lame", Time.local(2004, 4, 4, 16, 55), "MarianneSyhler")
63
+ assert_equal 2, @page.revisions.length
64
+
65
+ @page.revise("HisWay would be MyWay in kinda update", Time.local(2004, 4, 4, 16, 57), "MarianneSyhler")
66
+ assert_equal 2, @page.revisions.length
67
+ assert_equal "HisWay would be MyWay in kinda update", @page.revisions.last.content
68
+
69
+ @page.revise("HisWay would be MyWay in the house", Time.local(2004, 4, 4, 16, 58), "DavidHeinemeierHansson")
70
+ assert_equal 3, @page.revisions.length
71
+ assert_equal "HisWay would be MyWay in the house", @page.revisions.last.content
72
+
73
+ @page.revise("HisWay would be MyWay in my way", Time.local(2004, 4, 4, 17, 30), "DavidHeinemeierHansson")
74
+ assert_equal 4, @page.revisions.length
75
+ end
76
+ end
@@ -0,0 +1,91 @@
1
+
2
+ require "diff/diff"
3
+
4
+ require "wiki_content"
5
+ require "chunks/wiki"
6
+
7
+ require "date"
8
+ require "author"
9
+ require "page"
10
+
11
+ class Revision
12
+ attr_accessor :page, :number, :content, :created_at, :author
13
+
14
+ def initialize(page, number, content, created_at, author)
15
+ @page, @number, @created_at, @author = page, number, created_at, author
16
+ self.content = content
17
+ end
18
+
19
+ # Ensure that the wiki content is parsed when ever it is updated.
20
+ def content=(content)
21
+ @content = content
22
+ end
23
+
24
+ def created_on
25
+ Date.new(@created_at.year, @created_at.mon, @created_at.day)
26
+ end
27
+
28
+ def pretty_created_at
29
+ # Must use DateTime because Time doesn't support %e on at least some platforms
30
+ DateTime.new(
31
+ @created_at.year, @created_at.mon, @created_at.day, @created_at.hour, @created_at.min
32
+ ).strftime "%B %e, %Y %H:%M"
33
+ end
34
+
35
+ def next_revision
36
+ page.revisions[number + 1]
37
+ end
38
+
39
+ def previous_revision
40
+ number - 1 >= 0 && page.revisions[number - 1]
41
+ end
42
+
43
+ # Returns an array of all the WikiWords present in the content of this revision.
44
+ def wiki_words
45
+ unless @wiki_words_cache
46
+ wiki_chunks = display_content.find_chunks(WikiChunk::WikiLink)
47
+ @wiki_words_cache = wiki_chunks.map { |c| ( c.escaped_text ? nil : c.page_name ) }.compact.uniq
48
+ end
49
+ @wiki_words_cache
50
+ end
51
+
52
+ # Returns an array of all the WikiWords present in the content of this revision.
53
+ # that already exists as a page in the web.
54
+ def existing_pages
55
+ wiki_words.select { |wiki_word| page.web.pages[wiki_word] }
56
+ end
57
+
58
+ # Returns an array of all the WikiWords present in the content of this revision
59
+ # that *doesn't* already exists as a page in the web.
60
+ def unexisting_pages
61
+ wiki_words - existing_pages
62
+ end
63
+
64
+ # Explicit check for new type of display cache with find_chunks method.
65
+ # Ensures new version works with older snapshots.
66
+ def display_content
67
+ unless @display_cache && @display_cache.respond_to?(:find_chunks)
68
+ #$stderr << "Rendering: #{page.name}\n"
69
+ @display_cache = WikiContent.new(self)
70
+ end
71
+ @display_cache
72
+ end
73
+
74
+ def display_diff
75
+ previous_revision ? HTMLDiff.diff(previous_revision.display_content, display_content) : display_content
76
+ end
77
+
78
+ def clear_display_cache
79
+ @display_cache = @published_cache = @wiki_words_cache = nil
80
+ end
81
+
82
+ def display_published
83
+ @published_cache = WikiContent.new(self, {:mode => :publish}) if @published_cache.nil?
84
+ @published_cache
85
+ end
86
+
87
+ def display_content_for_export
88
+ WikiContent.new(self, {:mode => :export} )
89
+ end
90
+
91
+ end