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
data/README ADDED
@@ -0,0 +1,158 @@
1
+ <pre>
2
+ What is Instiki?
3
+ ================
4
+
5
+ Admitted, it's Yet Another Wiki Clone[1], but with a strong focus
6
+ on simplicity of installation and running:
7
+
8
+ Step 1. Download
9
+ Step 2. Run "instiki.rb"
10
+ Step 3. *Chuckle*... "There's no step three!" (TM)
11
+
12
+ You're now running a perfectly suitable wiki on port 2500
13
+ that'll present you with one-step setup, followed by a textarea for the home page
14
+ on http://localhost:2500.
15
+
16
+ Instiki lowers the barriers of interest for when you might consider
17
+ using a wiki. It's so simple to get running that you'll find yourself
18
+ using it for anything -- taking notes, brainstorming, organizing a
19
+ gathering.
20
+
21
+ Features:
22
+ * Regular expression search: Find deep stuff really fast
23
+ * Revisions: Follow the changes on every page from birth. Rollback to an earlier rev
24
+ * Export to HTML or markup in a zip: Take the entire wiki with you home or for reference
25
+ * RSS feeds to track recently revised pages
26
+ * Multiple webs: Create separate wikis with their own namespace
27
+ * Password-protected webs: Keep it private
28
+ * Authors: Each revision is associated with an author, so you can see who changed what
29
+ * Reference tracker: Which other pages are pointing to the current?
30
+ * Speed: Using Madelein for persistence (all pages are in memory)
31
+ * Three markup choices: Textile (default / RedCloth), Markdown (BlueCloth), and RDoc
32
+ * Embedded webserver: Through WEBrick
33
+ * Internationalization: Wiki words in any latin, greek, cyrillian, or armenian characters
34
+ * Color diffs: Track changes through revisions
35
+
36
+ Missing:
37
+ * File attachments
38
+
39
+ Command-line options:
40
+ Run "ruby instiki.rb --help"
41
+
42
+ History:
43
+ * 0.9.2: Changed rollback link to be powered by javascript so it's not trigged by search engines
44
+ Changed to use inline style on published pages
45
+ Fixed "forward in time" on the last revision before current page
46
+ * 0.9.1: Added performance improvements for updating existing pages
47
+ Fixed IP logging and RSS feeds behind proxies [With help from Guan Yang]
48
+ Fixed default storage directory (borked running on Windows) [Spotted by Curt Hibbs]
49
+ * 0.9.0: Added aliased links such as [[HomePage|that nice home page]] [Mark Reid]
50
+ Added include other page content with [[!include TableOfContents]] [Mark Reid]
51
+ Added delete orphan pages from the Edit Web screen [by inspiration from Simon Arnaud]
52
+ Added logging of IP address for authors (who's behind the rollback wars)
53
+ Added Categories pages through backlinks (use "categories: news, instiki" on start of line) [Mark Reid]
54
+ Added option to use bracket-style wiki links only (and hence ban WikiWords)
55
+ Added command-line option to specify different storage path
56
+ Added print view without navigation
57
+ Added character and page (2275 characters including spaces) counter (important for student papers)
58
+ Off by default, activate it on the Edit Web screen
59
+ Added LaTeX/PDF integration on Textile installations with pdflatex installed on system (EXPERIMENTAL)
60
+ Use the home page as a table of contents with a unordered list to control sections
61
+ Added limit of 15 to the number of pages included in RSS feed
62
+ Moved static parts of stylesheet to separate file [Lau T�rnskov]
63
+ Fixed better semantics for revision movement [Ryan Singer]
64
+ Fixed color diffs to work much better [Xen/Mertz/Atkins]
65
+ Fixed performance problems for All Pages list [Dennis Mertz]
66
+ Fixed lots of rendering bugs [Mark Reid]
67
+ Upgraded to RedCloth 2.0.11 [integrating the fine work of Dennis Mertz]
68
+ * 0.8.9: Added color diffs to see changes between revisions [Bill Atkins]
69
+ They're aren't quite perfect yet as new paragraphs split the <ins> tags (hence 0.8.9, not 0.9.0)
70
+ Added redirect to edit if content of page generates an error
71
+ (so the page doesn't become unusable on bugs in the markup engines)
72
+ Fixed update Web with different address bug [Denis Metz]
73
+ Fixed a bunch of wiki word rendering issues by doing wiki word detection and replacment at once
74
+ Upgraded to BlueCloth 0.0.3b (should fix loads of problems on Markdown wikis)
75
+ * 0.8.5: Instiki can now serve as a CMS by running a password-protected web with a published front
76
+ Added version check at startup (Instiki needs Ruby 1.8.1)
77
+ * 0.8.1: Actually included RedCloth 2.0.7 in the release
78
+ * 0.8.0: NOTE: Single-web wikis created in versions prior to 0.8.0 have "instiki" as their system password
79
+ Accepts wiki words in bracket style. Ex: [[wiki word]], [[c]], [[We could'nt have done it!]]
80
+ Accepts camel-case wiki words in all latin, greek, cyrillian, and armenian unicode characters
81
+ Many thanks to Guan Yang for building the higher- and lower-case lookup tables
82
+ And thanks to Simon Arnaud for the initial patch that got the work started
83
+ Changed charset to UTF-8
84
+ Cut down on command-line options and replaced them with an per-web config screen
85
+ Added option to extend the stylesheet on a per-web basis to tweak the look in details
86
+ Added simple color options for variety
87
+ Added option to add/remove password protection on each web
88
+ Added the wiki name of the author locking a given page (instead of just "someone")
89
+ Removed single/multi-web distinction -- all Instikis are now multi-web
90
+ Load libraries from an unshifted load path, so that old installed libraries doesn't clash [Emiel van de Laar]
91
+ Keeps the author cookie forever, so you don't have to enter your name again and again
92
+ Fixed XHTML so it validates [Bruce D'Arcus]
93
+ Authors are no longer listed under orphan pages
94
+ Added export to markup (great for backups, potentially for switching wiki engine)
95
+ Don't link wiki words that proceeds from either /, = or ?
96
+ (http://c2.com/cgi/wiki?WikiWikiClones, /show/HomePage, cgi.pl?show=WikiWord without escaping)
97
+ Accessing an unexisting page redirects to a different url (/new/PageName)
98
+ Increased snapshot time to just once a day (cuts down on disk storage requirements)
99
+ Made RDoc support work better with 1.8.1 [Mauricio Fern�ndez]
100
+ Added convinient redirect from /wiki/ to /wiki/show/HomePage
101
+ Fixed BlueCloth bug with backticks at start of line
102
+ Updated to RedCloth 2.0.7 (and linked to the new Textile reference)
103
+ * 0.7.0: Added Markdown (BlueCloth) and RDoc [Mauricio Fern�ndez] as command-line markup choices
104
+ Added wanted and orphan page lists to All pages (only show up if there's actually orphan or wanted pages)
105
+ Added ISO-8859-1 as XML encoding in RSS feeds (makes FeedReader among others happy for special entities)
106
+ Added proper links in the RSS feed (but the body links are still relative, which NNW and others doesn't grok)
107
+ Added access keys: E => Edit, H => HomePage, A => All Pages, U => Recently Revised, X => Export
108
+ Added password-login through URL (so you can subscribe to feed on a protected web)
109
+ Added web passwords to the feed links for protected webs, so they work without manual login
110
+ Added the web name in small letters above all pages within a web
111
+ Polished authors and recently revised
112
+ Updated to RedCloth 2.0.6
113
+ Changed content type for RSS feeds to text/xml (makes Mozilla Aggreg8 happy)
114
+ Changed searching to be case insensitive
115
+ Changed HomePage to display the name of the web instead
116
+ Changed exported HTML pages to be valid XHTML (which can be preprocessed by XSLT)
117
+ Fixed broken recently revised
118
+ * 0.6.0: Fixed Windows compatibility [Florian]
119
+ Fixed bug that would prevent Madeleine from taking snapshots in Daemon mode
120
+ Added export entire web as HTML in a zip file
121
+ Added RSS feeds
122
+ Added proper getops support for the growing number of options [Florian]
123
+ Added safe mode that forbids style options in RedCloth [Florian]
124
+ Updated RedCloth to 2.0.5
125
+ * 0.5.0: NOTE: 0.5.0 is NOT compatible with databases from earlier versions
126
+ Added revisions
127
+ Added multiple webs
128
+ Added password protection for webs on multi-web setups
129
+ Added the notion of authors (that are saved in a cookie)
130
+ Added command-line option for not running as a Daemon on Unix
131
+ * 0.3.1: Added option to escape wiki words with \
132
+ * 0.3.0: Brought all files into common style (including Textile help on the edit page)
133
+ Added page locking (if someone already is editing a page there's a warning)
134
+ Added daemon abilities on Unix (keep Instiki running after you close the terminal)
135
+ Made port 2500 the default port, so Instiki can be launched by dobbelt-click
136
+ Added Textile cache to speed-up rendering of large pages
137
+ Made WikiWords look like "Wiki Words"
138
+ Updated RedCloth to 2.0.4
139
+ * 0.2.5: Upgraded to RedCloth 2.0.2 and Madeleine 0.6.1, which means the
140
+ Windows problems are gone. Also fixed a problem with wikiwords
141
+ that used part of other wikiwords.
142
+ * 0.2.0: First public release
143
+
144
+ Download latest from:
145
+ <a href="http://rubyforge.org/project/showfiles.php?group_id=186">http://rubyforge.org/project/showfiles.php?group_id=186</a>
146
+
147
+ Visit the official Instiki wiki:
148
+ http://www.instiki.org/
149
+
150
+ License is the same as Ruby's
151
+
152
+ [1] <a href="http://c2.com/cgi/wiki?WikiWikiClones">http://c2.com/cgi/wiki?WikiWikiClones</a>
153
+ [2] Textile Syntax: <a href="http://www.textism.com/tools/textile/">http://www.textism.com/tools/textile/</a>
154
+ --
155
+ David Heinemeier Hansson,
156
+ david@loudthinking.com
157
+ http://www.loudthinking.com/
158
+ </pre>
@@ -0,0 +1,87 @@
1
+ <pre>
2
+ What is Pimki?
3
+ ================
4
+
5
+ Pimki is a Personal Information Manager based on Instiki's Wiki technology.
6
+
7
+ It has some added features over Instiki, while keeping the simplicity of
8
+
9
+ Instiki Features:
10
+ * Regular expression search: Find deep stuff really fast
11
+ * Revisions: Follow the changes on every page from birth. Rollback to an earlier rev
12
+ * Export to HTML or markup in a zip: Take the entire wiki with you home or for reference
13
+ * RSS feeds to track recently revised pages
14
+ * Multiple webs: Create separate wikis with their own namespace
15
+ * Password-protected webs: Keep it private
16
+ * Authors: Each revision is associated with an author, so you can see who changed what
17
+ * Reference tracker: Which other pages are pointing to the current?
18
+ * Speed: Using Madelein for persistence (all pages are in memory)
19
+ * Three markup choices: Textile (default / RedCloth), Markdown (BlueCloth), and RDoc
20
+ * Embedded webserver: Through WEBrick
21
+ * Internationalization: Wiki words in any latin, greek, cyrillian, or armenian characters
22
+ * Color diffs: Track changes through revisions
23
+
24
+ Pimki added features:
25
+ * Mind Map: a graph of wiki connections (made with GraphViz). All graphs can be
26
+ laid-out with the 4 GraphViz engines. Graph contents can be arranged as:
27
+ * Page ? linked pages
28
+ * Authors ? pages
29
+ * Categories ? pages
30
+ * Bliki: a blog integrated with the wiki. Blog entries are simply wiki-pages in
31
+ a special space. Can link from an entry to wiki pages.
32
+ * Todo Items & List:
33
+ * �todo:� items are highlighted on each page.
34
+ * Added a capacity to pull and list todo items from all pages.
35
+ * On main list, items are highlighted according to date (if there is one).
36
+ * Added capacity to (persistently :) delete/rename pages through the 'All Pages'
37
+ list.
38
+ * Added left-side menu. Content options are:
39
+ * Only pages that reference other pages (default)
40
+ * All pages: by name
41
+ * All pages: recently revised
42
+ * All pages: recently visited
43
+ * All pages: most often visited
44
+ * User definable wiki-text
45
+ * Expanded Search:
46
+ * Search also tries to match the page names (not just contents).
47
+ * Now showing part of sentance around match in search-results page
48
+ * Removed unnecessary (for me) stuff from the nav-bar: RSS, authors, export.
49
+ Moved to bottom of Home Page.
50
+
51
+ Missing:
52
+
53
+ * Expand todo with more task types?
54
+ * Expand Search
55
+ * Highlight on individual page when clicked form search results
56
+ * Advanced Search (not just regexp, case sensitivity)?
57
+ * Editable left-side menu options: by search results? by author?
58
+ * Bliki: allow linking from wiki-pages to bliki entries.
59
+
60
+
61
+ Command-line options:
62
+ Run "ruby pimki.rb --help"
63
+
64
+ History:
65
+
66
+ 0.9.2-7 Initial Public Release
67
+ I have been working on Pimki for my own use for quite some time. It is
68
+ fairly mature and stable. The version number reflects the base Instiki
69
+ version (0.9.2; or actually the latest CVS that's markes so) and the
70
+ running number of my feature-addition releases.
71
+
72
+
73
+ Download latest from:
74
+ <a href="http://rubyforge.org/project/showfiles.php?group_id=447">http://rubyforge.org/project/showfiles.php?group_id=447</a>
75
+
76
+ Visit the official Pimki home page at:
77
+ http://pimki.rubyforge.org/
78
+
79
+ Visit the official Instiki wiki:
80
+ http://www.instiki.org/
81
+
82
+ License is the same as Ruby's.
83
+
84
+ --
85
+ Assaph Mehr
86
+ assaph@gmail.com
87
+ </pre>
@@ -0,0 +1,563 @@
1
+ require "cgi"
2
+ require "redcloth_for_tex"
3
+
4
+ class WikiController < ActionControllerServlet
5
+ EXPORT_DIRECTORY = File.dirname(__FILE__) + "/../../storage/" unless const_defined?("EXPORT_DIRECTORY")
6
+
7
+ def index
8
+ if web_address
9
+ redirect_show "HomePage"
10
+ elsif !wiki.setup?
11
+ redirect_path "/new_system/"
12
+ elsif wiki.webs.length == 1
13
+ redirect_show "HomePage", wiki.webs.values.first.address
14
+ else
15
+ redirect_path "/web_list/"
16
+ end
17
+ end
18
+
19
+ # Administrating the Instiki setup --------------------------------------------
20
+
21
+ def new_system
22
+ wiki.setup? ? redirect_path("/") : render
23
+ end
24
+
25
+ def new_web
26
+ redirect_path("/") if wiki.system["password"].nil?
27
+ end
28
+
29
+ def create_system
30
+ wiki.setup(@params["password"], @params["web_name"], @params["web_address"]) unless wiki.setup?
31
+ redirect_path "/"
32
+ end
33
+
34
+ def create_web
35
+ redirect_path("/") unless wiki.authenticate(@params["system_password"])
36
+ wiki.create_web(@params["name"], @params["address"])
37
+ redirect_show("HomePage", @params["address"])
38
+ end
39
+
40
+
41
+ # Outside a single web --------------------------------------------------------
42
+
43
+ def web_list
44
+ @system, @webs = wiki.system, wiki.webs.values
45
+ render "wiki/web_list"
46
+ end
47
+
48
+ def login
49
+ render "wiki/login"
50
+ end
51
+
52
+ def authenticate
53
+ password_check(@params["password"]) ? redirect_show("HomePage") : redirect_action("login")
54
+ end
55
+
56
+ def static_style_sheet() render "static_style_sheet" end
57
+
58
+ # Within a single web ---------------------------------------------------------
59
+
60
+ def parse_category
61
+ @categories = web.categories
62
+ @category = @params["category"]
63
+ @pages_in_category = web.select { |page| page.in_category?(@category) }
64
+ @set_name = ( @categories.include?(@category) ? "category '#{@category}'" : "the web" )
65
+ @category_links = @categories.map do |c|
66
+ (@category == c ? "<span class=\"selected\">#{c}</span>" : "<a href=\"?category=#{c}\">#{c}</a>")
67
+ end
68
+ end
69
+
70
+ def search
71
+ set_menu_pages
72
+ @query = @params["query"]
73
+ rex = /#{@query}/i
74
+ @results = web.select { |page| rex.match(page.name) or rex.match(page.content) }
75
+ @results.length == 1 ? redirect_show(@results.first.name) : render
76
+ end
77
+
78
+ def authors
79
+ @authors = web.select.authors
80
+ end
81
+
82
+ def recently_revised
83
+ parse_category
84
+ set_menu_pages
85
+ @pages_by_revision = @pages_in_category.by_revision
86
+ end
87
+
88
+ def rss_with_content
89
+ @pages_by_revision = web.select.by_revision.first(15)
90
+ @uri = @req.request_uri
91
+ host = @req.meta_vars["HTTP_X_FORWARDED_HOST"] || "#{@uri.host}:#{@uri.port.to_s}"
92
+ @web_url = "#{@uri.scheme}://#{host}/#{@web.address}"
93
+ @res["Content-Type"] = "text/xml"
94
+ render "wiki/rss_feed"
95
+ end
96
+
97
+ def rss_with_headlines
98
+ @hide_description = true
99
+ rss_with_content
100
+ end
101
+
102
+ def list
103
+ parse_category
104
+ set_menu_pages
105
+ @pages_by_name = @pages_in_category.by_name
106
+ @page_names_that_are_wanted = @pages_in_category.wanted_pages
107
+ @pages_that_are_orphaned = @pages_in_category.orphaned_pages
108
+ case @req.query['Action']
109
+ when 'Delete' # Handle page deletion
110
+ wiki.delete_page(web_address, @req.query['sel_page_name'])
111
+ redirect_action "list/"
112
+
113
+ when 'Create' # Handle page creation
114
+ redirect_show @req.query['newpage']
115
+
116
+ when 'Rename' # Handle page rename
117
+ wiki.rename_page(web_address, @req.query['sel_page_name'], @req.query['newpage'])
118
+ redirect_action "list/"
119
+ end
120
+
121
+ end
122
+
123
+ def export_html
124
+ file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
125
+ file_path = EXPORT_DIRECTORY + file_name
126
+
127
+ export_pages_to_zip_file(file_path) unless FileTest.exists?(file_path)
128
+ send_export(file_name, file_path)
129
+ end
130
+
131
+ def export_markup
132
+ file_name = "#{web.address}-markup-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
133
+ file_path = EXPORT_DIRECTORY + file_name
134
+
135
+ export_markup_to_zip_file(file_path) unless FileTest.exists?(file_path)
136
+ send_export(file_name, file_path)
137
+ end
138
+
139
+ def export_pdf
140
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}"
141
+ file_path = EXPORT_DIRECTORY + file_name
142
+
143
+ export_web_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
144
+ convert_tex_to_pdf(file_path + ".tex")
145
+ send_export(file_name + ".pdf", file_path + ".pdf")
146
+ end
147
+
148
+ def export_tex
149
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.tex"
150
+ file_path = EXPORT_DIRECTORY + file_name
151
+
152
+ export_web_to_tex(file_path) unless FileTest.exists?(file_path)
153
+ send_export(file_name, file_path)
154
+ end
155
+
156
+ def update_web
157
+ redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
158
+
159
+ wiki.update_web(
160
+ web.address, @params["address"], @params["name"],
161
+ @params["markup"].intern,
162
+ @params["color"], @params["additional_style"],
163
+ @params["safe_mode"] ? true : false,
164
+ @params["password"].empty? ? nil : @params["password"],
165
+ @params["published"] ? true : false,
166
+ @params["brackets_only"] ? true : false,
167
+ @params["count_pages"] ? true : false
168
+ )
169
+
170
+ redirect_show("HomePage", @params["address"])
171
+ end
172
+
173
+ def remove_orphaned_pages
174
+ if wiki.authenticate(@params["system_password"])
175
+ wiki.remove_orphaned_pages(web_address)
176
+ redirect_action "list/"
177
+ else
178
+ redirect_show "HomePage"
179
+ end
180
+ end
181
+
182
+ def todo #{{{
183
+ parse_category
184
+ set_menu_pages
185
+ @pages_by_name = @pages_in_category.by_name
186
+ @todo_items = Hash.new { Array.new }
187
+ @pages_by_name.each do |page|
188
+ if page.content =~ Todo.pattern
189
+ # Page has todo items. Get the rendered version (marked-up and with links):
190
+ content = page.revisions.last.display_content
191
+ @todo_items[page] = content.scan /<span class="todo"><strong>TODO:<\/strong> (.*?)<\/span>/
192
+ end
193
+ end
194
+ @todo_items = @todo_items.sort_by { |page, items| page.name }
195
+ end #}}}
196
+
197
+ def set_menu_pages #{{{
198
+ parse_category
199
+ @menu_pages = case web.menu_type
200
+ when 'all' then @pages_in_category.by_name
201
+ when 'recent' then @pages_in_category.by_last_visited
202
+ when 'viewed' then @pages_in_category.by_most_viewed
203
+ when 'revised' then @pages_in_category.by_revision
204
+ when 'user' then @menu_content = web.rendered_menu; nil
205
+ when 'linkers'
206
+ web.select { |page|
207
+ page.wiki_words.size > 0
208
+ }.sort_by { |page| page.name }
209
+ end
210
+ if web.menu_limit
211
+ @menu_pages = @menu_pages[0..web.menu_limit]
212
+ end
213
+ end #}}}
214
+
215
+ def bliki #{{{
216
+ set_menu_pages
217
+ @entries = web.bliki_entries_by_date
218
+ unless @req.query['authorname'].nil? || @req.query['authorname'] == 'noselect'
219
+ @entries = @entries.select { |page|
220
+ page.authors.include?(@req.query['authorname'])
221
+ }
222
+ end
223
+ unless @req.query['regexp'].nil?
224
+ @entries = @entries.select { |page|
225
+ page.content =~ /#{@req.query['regexp']}/i
226
+ }
227
+ end
228
+ @color = web.color
229
+ @authors = web.authors
230
+ end #}}}
231
+
232
+ def mind #{{{
233
+ parse_category
234
+ set_menu_pages
235
+
236
+ @prog = @req.query['draw_type'] || 'neato'
237
+ @graph_type = @req.query['graph_type'] || 'normal'
238
+ missing = @pages_in_category.wanted_pages if @req.query['missing']
239
+ show_authors = @req.query['show_authors'] == 'on'
240
+ @pngFile = @mapFile = nil
241
+ case @graph_type
242
+ when 'normal' then @pngFile, @mapFile = web.create_mind_map(@prog, missing, show_authors)
243
+ when 'author' then @pngFile, @mapFile = web.create_author_graph(@prog)
244
+ when 'category' then @pngFile, @mapFile = web.create_category_graph(@prog, show_authors)
245
+ end
246
+ end #}}}
247
+
248
+ def edit_menu #{{{
249
+ @menu_type = web.menu_type
250
+ @menu_content = web.menu_content
251
+ @list_limit = web.menu_limit
252
+ @list_limit += 1 if @list_limit >= -1
253
+ end #}}}
254
+
255
+ def save_menu #{{{
256
+ unless @req.query['action'] == 'Cancel Update'
257
+ type = @req.query['type']
258
+ content = @req.query['content']
259
+
260
+ limit = @req.query['limit'].to_i rescue nil
261
+ limit = 20 unless limit
262
+ limit -= 1 if limit >= 0
263
+
264
+ # need to go through the WikiService to persist the command:
265
+ wiki.save_menu_pref web, type, limit, content
266
+ end
267
+
268
+ if web_address
269
+ redirect_show "HomePage"
270
+ elsif wiki.webs.length == 1
271
+ redirect_show "HomePage", wiki.webs.values.first.address
272
+ else
273
+ redirect_path "/web_list/"
274
+ end
275
+ end #}}}
276
+
277
+
278
+ # Within a single page --------------------------------------------------------
279
+
280
+ def show
281
+ set_menu_pages
282
+ if @page = wiki.read_page(web_address, page_name)
283
+ unless page_name == 'HomePage'
284
+ # HomePage should not be in the menu as there's a link at the top.
285
+ @page.last_visited = Time.now
286
+ @page.viewed += 1
287
+ end
288
+ begin
289
+ render_action "page"
290
+ rescue => e
291
+ $stderr << e.backtrace.join("\n")
292
+ redirect_action "edit/#{CGI.escape(page_name)}?msg=#{CGI.escape(e.message)}"
293
+ end
294
+ else
295
+ redirect_action "new/#{CGI.escape(page_name)}"
296
+ end
297
+ end
298
+
299
+ def published
300
+ if web.published then @page = wiki.read_page(web_address, page_name || "HomePage") else redirect_show("HomePage") end
301
+ end
302
+
303
+ def print
304
+ @page = wiki.read_page(web_address, page_name)
305
+ end
306
+
307
+ def tex
308
+ @page = wiki.read_page(web_address, page_name)
309
+ @tex_content = RedClothForTex.new(@page.content).to_tex
310
+ end
311
+
312
+ def pdf
313
+ page = wiki.read_page(web_address, page_name)
314
+ safe_page_name = page.name.gsub(/\W/, "")
315
+ file_name = "#{safe_page_name}-#{web.address}-#{page.created_at.strftime("%Y-%m-%d-%H-%M")}"
316
+ file_path = EXPORT_DIRECTORY + file_name
317
+
318
+ export_page_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
319
+ convert_tex_to_pdf(file_path + ".tex")
320
+ send_export(file_name + ".pdf", file_path + ".pdf")
321
+ end
322
+
323
+ def new
324
+ @page_name, @author = page_name, default_author
325
+ end
326
+
327
+ def edit
328
+ @page = wiki.read_page(web_address, page_name)
329
+
330
+ if !@page.locked?(Time.now) || @params["break_lock"]
331
+ @page.lock(Time.now, default_author)
332
+ @author = default_author
333
+ render
334
+ else
335
+ render "wiki/locked"
336
+ end
337
+ end
338
+
339
+ def cancel_edit
340
+ @page = wiki.read_page(web_address, page_name)
341
+ @page.unlock
342
+ redirect_show
343
+ end
344
+
345
+ def save
346
+ if web.pages[page_name]
347
+ page = wiki.revise_page(
348
+ web_address, page_name, @params["content"], Time.now,
349
+ Author.new(@params["author"], remote_ip)
350
+ )
351
+
352
+ page.unlock
353
+ else
354
+ page = wiki.write_page(
355
+ web_address, page_name, @params["content"], Time.now,
356
+ Author.new(@params["author"], remote_ip)
357
+ )
358
+ end
359
+
360
+ write_cookie("author", @params["author"], true)
361
+ redirect_show(page_name)
362
+ end
363
+
364
+ def revision
365
+ @page = wiki.read_page(web_address, page_name)
366
+ @revision = @page.revisions[@params["rev"].to_i]
367
+ end
368
+
369
+ def rollback
370
+ @page = wiki.read_page(web_address, page_name)
371
+ wiki.rollback_page(web_address, page_name, @params["rev"].to_i, Time.now, remote_ip)
372
+ redirect_show
373
+ end
374
+
375
+ # Bliki ----------------------------------------------------------------------
376
+
377
+ def bliki_delete
378
+ wiki.delete_bliki_entry(web_address, page_name)
379
+ redirect_bliki("")
380
+ end
381
+
382
+ def bliki_edit
383
+ @page = wiki.read_bliki_entry(web_address, page_name)
384
+
385
+ if !@page.locked?(Time.now) || @params["break_lock"]
386
+ @page.lock(Time.now, default_author)
387
+ @author = default_author
388
+ render
389
+ else
390
+ redirect_path "#{web_address}/locked"
391
+ end
392
+ end
393
+
394
+ def cancel_bliki_edit
395
+ @page = wiki.read_bliki_entry(web_address, page_name)
396
+ @page.unlock if @page
397
+ redirect_bliki(@page? @page.name : "")
398
+ end
399
+
400
+ def bliki_save
401
+ pname = page_name || @params["pagename"]
402
+ if web.bliki[pname]
403
+ page = wiki.revise_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
404
+ page.unlock
405
+ else
406
+ page = wiki.write_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
407
+ end
408
+
409
+ write_cookie("author", @params["author"])
410
+ redirect_bliki('')
411
+ end
412
+
413
+ def bliki_revision
414
+ @page = wiki.read_bliki_entry(web_address, page_name || @params['pagename'])
415
+ @revision = @page.revisions[@params["rev"].to_i] || @page.revisions.last
416
+ end
417
+
418
+ def rollback_bliki
419
+ @page = wiki.read_bliki_entry(web_address, page_name)
420
+ wiki.rollback_bliki_entry(web_address, page_name, @params["rev"].to_i-1, Time.now)
421
+ redirect_bliki
422
+ end
423
+
424
+ # ----------------------------------------------------------------------------
425
+
426
+ protected
427
+ def before_action
428
+ if in_a_web? && !authorized?(web_address) && !%w( login authenticate published ).include?(action_name)
429
+ redirect_action("login")
430
+ return false
431
+ elsif in_a_web?
432
+ @web, @page_name, @action_name = web, page_name, action_name
433
+ end
434
+ end
435
+
436
+ def action_name
437
+ if in_a_web?
438
+ request_path[1]
439
+ elsif action_methods.include?(request_path[0])
440
+ request_path[0]
441
+ else
442
+ "index"
443
+ end
444
+ end
445
+
446
+ def redirect_show(page = @page.name, web = web_address)
447
+ redirect_path "/#{web}/show/#{CGI.escape(page)}"
448
+ end
449
+
450
+ def redirect_bliki(page = @page.name, web = web_address)
451
+ redirect_path "/#{web}/bliki/#{page}"
452
+ end
453
+
454
+ def redirect_action(action, web = web_address)
455
+ redirect_path "/#{web}/#{action}"
456
+ end
457
+
458
+
459
+
460
+ private
461
+ def wiki
462
+ WikiService.instance
463
+ end
464
+
465
+ def web
466
+ wiki.webs[web_address]
467
+ end
468
+
469
+ def in_a_web?
470
+ request_path.length > 1
471
+ end
472
+
473
+ def web_address
474
+ request_path[0]
475
+ end
476
+
477
+ def page_name
478
+ CGI.unescape(request_path[2]) if request_path[2]
479
+ end
480
+
481
+ def authorized?(web_address)
482
+ (web && web.password.nil?) ||
483
+ (read_cookie(web_address) && read_cookie(web_address) == web.password) ||
484
+ password_check(@params["password"])
485
+ end
486
+
487
+ def default_author
488
+ read_cookie("author") || "AnonymousCoward"
489
+ end
490
+
491
+ def password_check(password)
492
+ web && @params["password"] == web.password && write_cookie(web_address, @params["password"])
493
+ end
494
+
495
+ def export_pages_to_zip_file(zip_file_path)
496
+ Zip::ZipOutputStream.open(zip_file_path) do |zos|
497
+ web.select.by_name.each do |@page|
498
+ zos.put_next_entry(@page.name + ".html")
499
+ zos.puts(template_engine("print").result(binding))
500
+ end
501
+
502
+ zos.put_next_entry("index.html")
503
+ zos.puts('<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html>')
504
+ end
505
+ end
506
+
507
+ def export_markup_to_zip_file(zip_file_path)
508
+ Zip::ZipOutputStream.open(zip_file_path) do |zos|
509
+ web.select.by_name.each do |page|
510
+ zos.put_next_entry(page.name + ".#{web.markup}")
511
+ zos.puts(page.content)
512
+ end
513
+ end
514
+ end
515
+
516
+ def export_web_to_tex(file_path)
517
+ @web_name = web.name
518
+ @tex_content = table_of_contents(web.pages["HomePage"].content.dup, render_tex_web)
519
+ File.open(file_path, "w") { |f| f.write(template_engine("tex_web").result(binding)) }
520
+ end
521
+
522
+ def render_tex_web
523
+ web.select.by_name.inject({}) do |tex_web, page|
524
+ tex_web[page.name] = RedClothForTex.new(page.content).to_tex
525
+ tex_web
526
+ end
527
+ end
528
+
529
+ def export_page_to_tex(file_path)
530
+ tex
531
+ File.open(file_path, "w") { |f| f.write(template_engine("tex").result(binding)) }
532
+ end
533
+
534
+ def convert_tex_to_pdf(tex_path)
535
+ `cd #{File.dirname(tex_path)}; pdflatex --interaction=scrollmode '#{File.basename(tex_path)}'`
536
+ end
537
+
538
+ def truncate(text, length = 30, truncate_string = "...")
539
+ if text.length > length then text[0..(length - 3)] + truncate_string else text end
540
+ end
541
+
542
+ def render_markup_help
543
+ sub_template "#{web.markup}_help"
544
+ end
545
+
546
+ def send_export(file_name, file_path, content_type = "application/zip")
547
+ @res["Content-Type"] = content_type
548
+ @res["Content-Disposition"] = "attachment; filename=#{file_name}"
549
+ @res["Content-Length"] = File.size(file_path)
550
+ File.open(file_path, "rb") { |f| @res.body = f.read }
551
+ end
552
+
553
+ def template_engine(template_name)
554
+ ERB.new(IO.readlines(action_template_path(template_name)).join)
555
+ end
556
+
557
+ def remote_ip
558
+ $stderr << "#{@req.meta_vars['HTTP_X_FORWARDED_FOR']} || #{@req.meta_vars['REMOTE_ADDR']}"
559
+ @req.meta_vars["HTTP_X_FORWARDED_FOR"] || @req.meta_vars["REMOTE_ADDR"]
560
+ end
561
+ end
562
+
563
+ # jEdit :folding=indent:collapseFolds=2: