Pimki 1.7.092 → 1.8.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 (48) hide show
  1. data/README-PIMKI +182 -178
  2. data/app/controllers/wiki.rb +950 -942
  3. data/app/models/chunks/category.rb +33 -33
  4. data/app/models/chunks/category_test.rb +21 -21
  5. data/app/models/chunks/chunk.rb +20 -20
  6. data/app/models/chunks/engines.rb +48 -48
  7. data/app/models/chunks/include.rb +1 -1
  8. data/app/models/chunks/match.rb +1 -1
  9. data/app/models/chunks/nowiki.rb +1 -1
  10. data/app/models/chunks/nowiki_test.rb +5 -0
  11. data/app/models/chunks/todo.rb +1 -0
  12. data/app/models/chunks/wiki.rb +130 -130
  13. data/app/models/page.rb +124 -124
  14. data/app/models/revision.rb +92 -92
  15. data/app/models/web.rb +314 -316
  16. data/app/models/wiki_content.rb +2 -2
  17. data/app/models/wiki_service.rb +170 -166
  18. data/app/models/wiki_words.rb +28 -28
  19. data/app/views/error.rhtml +37 -37
  20. data/app/views/navigation.rhtml +1 -1
  21. data/app/views/static_style_sheet.rhtml +10 -5
  22. data/app/views/top.rhtml +1 -1
  23. data/app/views/wiki/adv_search.rhtml +61 -61
  24. data/app/views/wiki/bliki.rhtml +7 -6
  25. data/app/views/wiki/bliki_edit.rhtml +26 -37
  26. data/app/views/wiki/bliki_new.rhtml +58 -64
  27. data/app/views/wiki/bliki_revision.rhtml +5 -3
  28. data/app/views/wiki/edit.rhtml +44 -44
  29. data/app/views/wiki/edit_menu.rhtml +26 -19
  30. data/app/views/wiki/edit_web.rhtml +303 -305
  31. data/app/views/wiki/glossary.rhtml +35 -27
  32. data/app/views/wiki/list.rhtml +175 -174
  33. data/app/views/wiki/list.rhtml.bak +175 -0
  34. data/app/views/wiki/mind.rhtml +70 -70
  35. data/app/views/wiki/new.rhtml +34 -32
  36. data/app/views/wiki/published.rhtml +34 -49
  37. data/app/views/wiki/revision.rhtml +88 -87
  38. data/app/views/wiki/rollback.rhtml +36 -35
  39. data/app/views/wiki/todo.rhtml +2 -2
  40. data/app/views/wiki_words_help.rhtml +8 -8
  41. data/libraries/action_controller_servlet.rb +202 -202
  42. data/libraries/madeleine_service.rb +162 -162
  43. data/pimki.rb +181 -181
  44. metadata +11 -12
  45. data/README +0 -172
  46. data/app/models/chunks/acronym.rb +0 -19
  47. data/app/views/wiki/test.rhtml +0 -25
  48. data/libraries/secure_web_controller_server.rb +0 -106
@@ -1,178 +1,182 @@
1
- What is Pimki?
2
- ================
3
-
4
- Pimki is a Personal Information Manager based on Instiki's Wiki technology.
5
-
6
- It has some added features over Instiki, while keeping (most of) the simplicity
7
- of Instiki. Add added complication is installing GraphViz on the path. GraphViz
8
- is available from: http://www.research.att.com/sw/tools/graphviz.
9
-
10
- Instiki Features:
11
- * Regular expression search: Find deep stuff really fast
12
- * Revisions: Follow the changes on every page from birth. Rollback to an earlier rev
13
- * Export to HTML or markup in a zip: Take the entire wiki with you home or for reference
14
- * RSS feeds to track recently revised pages
15
- * Multiple webs: Create separate wikis with their own namespace
16
- * Password-protected webs: Keep it private
17
- * Authors: Each revision is associated with an author, so you can see who changed what
18
- * Reference tracker: Which other pages are pointing to the current?
19
- * Speed: Using Madelein for persistence (all pages are in memory)
20
- * Three markup choices: Textile (default / RedCloth), Markdown (BlueCloth), and RDoc
21
- * Embedded webserver: Through WEBrick
22
- * Internationalization: Wiki words in any latin, greek, cyrillian, or armenian characters
23
- * Color diffs: Track changes through revisions
24
-
25
- Pimki added features:
26
- * Mind Map: a graph of wiki connections (made with GraphViz). All graphs can be
27
- laid-out with the 4 GraphViz engines. Graph contents can be arranged as:
28
- * Page -> linked pages
29
- * Authors -> pages
30
- * Categories -> pages
31
- * Bliki: a blog integrated with the wiki. Blog entries are simply wiki-pages in
32
- a special space. Can link from an entry to wiki pages via PageName or [[page name]]
33
- and from pages to Bliki entries via [bliki[page name]].
34
- * c2 Wiki links, via [c2[PageName]]. If you run multiple webs within Pimki, you
35
- can now link between them using [web_address[PageName]].
36
- * Todo Items & List:
37
- * 'todo:' items are highlighted on each page.
38
- * Added a capacity to pull and list todo items from all pages.
39
- * On main list, items are highlighted according to date (if there is one).
40
- * Added capacity to (persistently :) delete/rename pages through the 'All Pages'
41
- list.
42
- * Lots of web customization and administration options through the edit_web template.
43
- * Added left-side menu. Content options are:
44
- * Only pages that reference other pages (default)
45
- * Only pages that belong to a specific category
46
- * All pages: by name
47
- * All pages: recently revised
48
- * All pages: recently visited
49
- * All pages: most often visited
50
- * User definable wiki-text
51
- * Added Glossary function: shows all acronyms defined in the web
52
- * Expanded Search:
53
- * Search also tries to match the page names (not just contents).
54
- * Now showing part of sentance around match in search-results page
55
- * Advanced Search
56
- * Case sensitivity
57
- * Search as regex, phrase or words
58
- * Search pages and bliki entries
59
- * Search in page names / contents
60
- * Limit search to selected categories
61
- * Limit search to selected authors
62
- * Removed unnecessary (for me) stuff from the nav-bar: RSS, authors, export.
63
- Moved to bottom of Home Page.
64
- * Minor stylesheet tweaks.
65
-
66
-
67
- Command-line options:
68
- Run "ruby pimki.rb --help"
69
-
70
- History:
71
-
72
- 1.7.092 New features
73
- - Better control on multiple webs, including web administration and links between webs via [web_address[PageName]]
74
- - Added Glossary function: shows all acronyms defined in the web
75
- - Better export in anticipation of Pimki2
76
- - Moved things back to the nav-bar
77
- - Even more and better things at the web setup (edit_web) template
78
- - Lots of bug fixes
79
-
80
- 1.6.092 Mainly bug fixes with some minor enhancements
81
- - Squashed horrible restart bug
82
- - Several bug fixes
83
- - Upgraded to RedCloth 3.0.3, which seems stable enough
84
- - RSS includes Bliki
85
- - Added capacity to stop server through the edit_web template
86
- - Added an error reporting template
87
- - Can now display just the Mind Map image for printing
88
- - Minor improvements to the publish interface, e.g. displaying menu
89
-
90
- 1.5.092 Bug fixes & new features release
91
- - Several bug fixes
92
- - RedCloth is now defaulting to 2.0.11. Controllable thru commandline.
93
- - Advanced Search
94
- - c2 wiki links via [c2[PageName]]
95
- - FavIcon (working in Firefox)
96
- - Madeleine Snapshot Controls (thru edit_web)
97
-
98
- 1.4.092 New features release
99
- - Added a major/minor edit override for the default 'continous edit' when
100
- revising a page.
101
- - MarkDown can now be through RedCloth 3.0.0 (experimental) or BlueCloth.
102
- - Integrated Instiki release 0.9.2 features and bug fixes.
103
- - Todo@Context
104
- - Edit page on double click (based on Mark Reid's patch to Instiki)
105
- - Possibility to require edit password for each edit
106
- - Persist user changes to free-wiki menu content
107
- - Numerous bug fixes.
108
-
109
- 1.3.092 New features and bug fixes release
110
- - Updated to RedCloth v3.0.0.
111
- - Fixed problem with incorrect handling of some complex link formats and
112
- <pre> tags. (Justin)
113
- - Expanded search to look also in Bliki entries.
114
- - Can now show diff between Bliki entry versions.
115
- - Editing left-side menu is now password protected.
116
- - Added capacity in Mind Map to filter displayed pages by category and/or
117
- filter leaf nodes (i.e. pages that do not have links to other pages). This
118
- cuts down on the anount of visual information to give a bit more topical
119
- view of the web.
120
- - Added custom symbols/elements: <:cbx> and <:cbxc> to display a checkbox
121
- (and a checked checkbox).
122
- - Added customization options through "Edit Web":
123
- - Added capacity to set the prefered size of the Mind Map image.
124
- - Customizable mapping of symbols/elements (in progress).
125
- - Added capacity for custom link formats (in progress).
126
-
127
- 1.2.092 Bug-fix and minor enhancements release
128
- - Fixed problem with free-content of menu. (Mark S)
129
- - Fixed problem in persisting menu-type changes.
130
- - Fixed broken images in Mind Map under Firefox 1.0
131
-
132
- 1.1.092 Bug-fix and minor enhancements release
133
- - Changed storage path to be relative to CWD to avoid storing under gem
134
- version directory. When running/installing service should normally
135
- provide the --storage command-line parameter. (Chad)
136
- - Fixed a problem with the bliki_new form that prevented saving the entry
137
- due to debug code in the JS validation code of the entry name. (PragDave)
138
- - Fixed problem with export and storage path relating to app-dir instead
139
- of storage dir in Instiki core. (Prag Dave)
140
- - Fixed problem of missing files in in tgz packaging (rake needs
141
- PackagingTask defined _before_ GemPackageTask). (Ruben)
142
- - Fixed require 'YAML' to correct 'yaml' in Instiki core. (Chad)
143
- - Added capability to link from pages to Bliki entries. To use link via:
144
- [bliki[entry name]]
145
- - ToDo list picks up items from bliki entries and links to the entry (Ruben)
146
- - Menu can be set as all pages belonging to a specific category. If you set
147
- some page to a category e.g. 'menu', only those pages will appear on the
148
- left-side menu.
149
-
150
- 1.0.092 Updated Initial Release
151
- - Finalized Numbering scheme.
152
- - Made available as a gem.
153
-
154
- 0.9.2-7 Initial Public Release
155
- I have been working on Pimki for my own use for quite some time. It is
156
- fairly mature and stable. The version number reflects the base Instiki
157
- version (0.9.2; or actually the latest CVS that's markes so) and the
158
- running number of my feature-addition releases.
159
-
160
-
161
- Download latest from:
162
- http://rubyforge.org/project/showfiles.php?group_id=447
163
-
164
- Or go the Gem way and install through:
165
- gem install pimki
166
-
167
- Visit the official Pimki home page at:
168
- http://pimki.rubyforge.org/
169
-
170
- Visit the official Instiki wiki:
171
- http://www.instiki.org/
172
-
173
- License is the same as Ruby's.
174
-
175
- --
176
- Assaph Mehr
177
- assaph@gmail.com
178
-
1
+ What is Pimki?
2
+ ================
3
+
4
+ Pimki is a Personal Information Manager based on Instiki's Wiki technology.
5
+
6
+ It has some added features over Instiki, while keeping (most of) the simplicity
7
+ of Instiki. Add added complication is installing GraphViz on the path. GraphViz
8
+ is available from: http://www.research.att.com/sw/tools/graphviz.
9
+
10
+ Instiki Features:
11
+ * Regular expression search: Find deep stuff really fast
12
+ * Revisions: Follow the changes on every page from birth. Rollback to an earlier rev
13
+ * Export to HTML or markup in a zip: Take the entire wiki with you home or for reference
14
+ * RSS feeds to track recently revised pages
15
+ * Multiple webs: Create separate wikis with their own namespace
16
+ * Password-protected webs: Keep it private
17
+ * Authors: Each revision is associated with an author, so you can see who changed what
18
+ * Reference tracker: Which other pages are pointing to the current?
19
+ * Speed: Using Madelein for persistence (all pages are in memory)
20
+ * Three markup choices: Textile (default / RedCloth), Markdown (BlueCloth), and RDoc
21
+ * Embedded webserver: Through WEBrick
22
+ * Internationalization: Wiki words in any latin, greek, cyrillian, or armenian characters
23
+ * Color diffs: Track changes through revisions
24
+
25
+ Pimki added features:
26
+ * Mind Map: a graph of wiki connections (made with GraphViz). All graphs can be
27
+ laid-out with the 4 GraphViz engines. Graph contents can be arranged as:
28
+ * Page -> linked pages
29
+ * Authors -> pages
30
+ * Categories -> pages
31
+ * Bliki: a blog integrated with the wiki. Blog entries are simply wiki-pages in
32
+ a special space. Can link from an entry to wiki pages via PageName or [[page name]]
33
+ and from pages to Bliki entries via [bliki[page name]].
34
+ * c2 Wiki links, via [c2[PageName]]. If you run multiple webs within Pimki, you
35
+ can now link between them using [web_address[PageName]].
36
+ * Todo Items & List:
37
+ * 'todo:' items are highlighted on each page.
38
+ * Added a capacity to pull and list todo items from all pages.
39
+ * On main list, items are highlighted according to date (if there is one).
40
+ * Added capacity to (persistently :) delete/rename pages through the 'All Pages'
41
+ list.
42
+ * Lots of web customization and administration options through the edit_web template.
43
+ * Added left-side menu. Content options are:
44
+ * Only pages that reference other pages (default)
45
+ * Only pages that belong to a specific category
46
+ * All pages: by name
47
+ * All pages: recently revised
48
+ * All pages: recently visited
49
+ * All pages: most often visited
50
+ * User definable wiki-text
51
+ * Added Glossary function: shows all acronyms defined in the web
52
+ * Expanded Search:
53
+ * Search also tries to match the page names (not just contents).
54
+ * Now showing part of sentance around match in search-results page
55
+ * Advanced Search
56
+ * Case sensitivity
57
+ * Search as regex, phrase or words
58
+ * Search pages and bliki entries
59
+ * Search in page names / contents
60
+ * Limit search to selected categories
61
+ * Limit search to selected authors
62
+ * Removed unnecessary (for me) stuff from the nav-bar: RSS, authors, export.
63
+ Moved to bottom of Home Page.
64
+ * Minor stylesheet tweaks.
65
+
66
+
67
+ Command-line options:
68
+ Run "ruby pimki.rb --help"
69
+
70
+ History:
71
+
72
+ 1.8.092 Bug Fixes & New features
73
+ - Many small & not-so-small bug fixes
74
+ - Still based on Instiki 0.9.2 for storage compatibility
75
+
76
+ 1.7.092 Bug Fixes & New features
77
+ - Better control on multiple webs, including web administration and links between webs via [web_address[PageName]]
78
+ - Added Glossary function: shows all acronyms defined in the web
79
+ - Better export in anticipation of Pimki2
80
+ - Moved things back to the nav-bar
81
+ - Even more and better things at the web setup (edit_web) template
82
+ - Lots of bug fixes
83
+
84
+ 1.6.092 Mainly bug fixes with some minor enhancements
85
+ - Squashed horrible restart bug
86
+ - Several bug fixes
87
+ - Upgraded to RedCloth 3.0.3, which seems stable enough
88
+ - RSS includes Bliki
89
+ - Added capacity to stop server through the edit_web template
90
+ - Added an error reporting template
91
+ - Can now display just the Mind Map image for printing
92
+ - Minor improvements to the publish interface, e.g. displaying menu
93
+
94
+ 1.5.092 Bug fixes &amp; new features release
95
+ - Several bug fixes
96
+ - RedCloth is now defaulting to 2.0.11. Controllable thru commandline.
97
+ - Advanced Search
98
+ - c2 wiki links via [c2[PageName]]
99
+ - FavIcon (working in Firefox)
100
+ - Madeleine Snapshot Controls (thru edit_web)
101
+
102
+ 1.4.092 New features release
103
+ - Added a major/minor edit override for the default 'continous edit' when
104
+ revising a page.
105
+ - MarkDown can now be through RedCloth 3.0.0 (experimental) or BlueCloth.
106
+ - Integrated Instiki release 0.9.2 features and bug fixes.
107
+ - Todo@Context
108
+ - Edit page on double click (based on Mark Reid's patch to Instiki)
109
+ - Possibility to require edit password for each edit
110
+ - Persist user changes to free-wiki menu content
111
+ - Numerous bug fixes.
112
+
113
+ 1.3.092 New features and bug fixes release
114
+ - Updated to RedCloth v3.0.0.
115
+ - Fixed problem with incorrect handling of some complex link formats and
116
+ <pre> tags. (Justin)
117
+ - Expanded search to look also in Bliki entries.
118
+ - Can now show diff between Bliki entry versions.
119
+ - Editing left-side menu is now password protected.
120
+ - Added capacity in Mind Map to filter displayed pages by category and/or
121
+ filter leaf nodes (i.e. pages that do not have links to other pages). This
122
+ cuts down on the anount of visual information to give a bit more topical
123
+ view of the web.
124
+ - Added custom symbols/elements: <:cbx> and <:cbxc> to display a checkbox
125
+ (and a checked checkbox).
126
+ - Added customization options through "Edit Web":
127
+ - Added capacity to set the prefered size of the Mind Map image.
128
+ - Customizable mapping of symbols/elements (in progress).
129
+ - Added capacity for custom link formats (in progress).
130
+
131
+ 1.2.092 Bug-fix and minor enhancements release
132
+ - Fixed problem with free-content of menu. (Mark S)
133
+ - Fixed problem in persisting menu-type changes.
134
+ - Fixed broken images in Mind Map under Firefox 1.0
135
+
136
+ 1.1.092 Bug-fix and minor enhancements release
137
+ - Changed storage path to be relative to CWD to avoid storing under gem
138
+ version directory. When running/installing service should normally
139
+ provide the --storage command-line parameter. (Chad)
140
+ - Fixed a problem with the bliki_new form that prevented saving the entry
141
+ due to debug code in the JS validation code of the entry name. (PragDave)
142
+ - Fixed problem with export and storage path relating to app-dir instead
143
+ of storage dir in Instiki core. (Prag Dave)
144
+ - Fixed problem of missing files in in tgz packaging (rake needs
145
+ PackagingTask defined _before_ GemPackageTask). (Ruben)
146
+ - Fixed require 'YAML' to correct 'yaml' in Instiki core. (Chad)
147
+ - Added capability to link from pages to Bliki entries. To use link via:
148
+ [bliki[entry name]]
149
+ - ToDo list picks up items from bliki entries and links to the entry (Ruben)
150
+ - Menu can be set as all pages belonging to a specific category. If you set
151
+ some page to a category e.g. 'menu', only those pages will appear on the
152
+ left-side menu.
153
+
154
+ 1.0.092 Updated Initial Release
155
+ - Finalized Numbering scheme.
156
+ - Made available as a gem.
157
+
158
+ 0.9.2-7 Initial Public Release
159
+ I have been working on Pimki for my own use for quite some time. It is
160
+ fairly mature and stable. The version number reflects the base Instiki
161
+ version (0.9.2; or actually the latest CVS that's markes so) and the
162
+ running number of my feature-addition releases.
163
+
164
+
165
+ Download latest from:
166
+ http://rubyforge.org/project/showfiles.php?group_id=447
167
+
168
+ Or go the Gem way and install through:
169
+ gem install pimki
170
+
171
+ Visit the official Pimki home page at:
172
+ http://pimki.rubyforge.org/
173
+
174
+ Visit the official Instiki wiki:
175
+ http://www.instiki.org/
176
+
177
+ License is the same as Ruby's.
178
+
179
+ --
180
+ Assaph Mehr
181
+ assaph@gmail.com
182
+
@@ -1,943 +1,951 @@
1
- require "cgi"
2
- require "redcloth_for_tex"
3
-
4
- RenderedTodo = Struct.new( :text, :context, :due_date )
5
-
6
- class WikiController < ActionControllerServlet
7
- EXPORT_DIRECTORY = WikiService.storage_path
8
-
9
- def index
10
- if web_address
11
- check_external_req_and_redirect web
12
- elsif !wiki.setup?
13
- redirect_path "/new_system/"
14
- elsif wiki.webs.length == 1
15
- check_external_req_and_redirect wiki.webs.values.first
16
- else
17
- wiki.default_web ?
18
- check_external_req_and_redirect(wiki.webs[wiki.default_web]) :
19
- redirect_path("/web_list/")
20
- end
21
- end
22
-
23
- def check_external_req_and_redirect web
24
- if web.default_to_published and @req.addr[2] != @req.peeraddr[2]
25
- redirect_action("published/", web.address)
26
- else
27
- redirect_show("HomePage", web.address)
28
- end
29
- end
30
-
31
- # Administrating the Instiki setup --------------------------------------------
32
-
33
- def new_system
34
- wiki.setup? ? redirect_path("/") : render
35
- end
36
-
37
- def new_web
38
- redirect_path("/") if wiki.system["password"].nil?
39
- end
40
-
41
- def create_system
42
- wiki.setup(@params["password"], @params["web_name"], @params["web_address"]) unless wiki.setup?
43
- redirect_path "/"
44
- end
45
-
46
- def create_web
47
- redirect_path("/") unless wiki.authenticate(@params["system_password"])
48
- wiki.create_web(@params["name"], @params["address"])
49
- redirect_show("HomePage", @params["address"])
50
- end
51
-
52
-
53
- # Outside a single web --------------------------------------------------------
54
-
55
- def web_list
56
- @system, @webs = wiki.system, wiki.webs.values
57
- render "wiki/web_list"
58
- end
59
-
60
- def login
61
- render "wiki/login"
62
- end
63
-
64
- def authenticate
65
- password_check(@params["password"]) ? redirect_show("HomePage") : redirect_action("login")
66
- end
67
-
68
- def static_style_sheet() render "static_style_sheet" end
69
-
70
- def stop
71
- redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
72
-
73
- begin
74
- secs = @params['seconds'].to_i
75
- raise if secs.zero?
76
- @logger.warn "Pimki server will stop in #{secs} seconds!"
77
- WikiService.request_stop
78
- render_text "Pimki server will stop in #{secs} seconds!"
79
- Thread.new { sleep secs; Kernel.exit! }
80
- rescue
81
- redirect_show("HomePage")
82
- end
83
- end
84
-
85
- # Within a single web ---------------------------------------------------------
86
-
87
- def parse_category
88
- @categories = web.categories
89
- @category = @params["category"]
90
- @pages_in_category = web.select { |page| page.in_category?(@category) }
91
- @pages_without_category = web.select { |page| page.categories.length == 0 }
92
- if @category == 'none'
93
- @pages_in_category = @pages_without_category
94
- end
95
- @set_name = ( @categories.include?(@category) ? "category '#{@category}'" : "the web" )
96
- @category_links = @categories.map do |c|
97
- (@category == c ? "<span class=\"selected\">[#{c}]</span>" : "<a href=\"?category=#{c}\">#{c}</a>")
98
- end
99
- end
100
-
101
- def search
102
- set_menu_pages
103
- @query = @params["query"]
104
- search_content = [nil, 'both', 'contents'].include? @params['fields']
105
- search_names = [nil, 'both', 'names'].include? @params['fields']
106
-
107
- case @params['expression']
108
- when 'regex', nil
109
- rex = Regexp.new @query, (Regexp::IGNORECASE unless @params['case'])
110
- @results = if [nil, 'all', 'pages', nil].include? @params['where']
111
- web.select do |page|
112
- (search_names and rex.match(page.name)) or (search_content and rex.match(page.content))
113
- end
114
- else
115
- []
116
- end
117
- @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
118
- web.bliki.values.select do |entry|
119
- (search_names and rex.match(entry.name)) or (search_content and rex.match(entry.content))
120
- end
121
- else
122
- []
123
- end
124
-
125
- when 'all'
126
- words = @query.split(/\s/).reject { |w| w.empty? }
127
- @results = if [nil, 'all', 'pages'].include? @params['where']
128
- web.select do |page|
129
- words.all? do |word|
130
- (search_names and page.name.index(word)) or (search_content and page.content.index(word))
131
- end
132
- end
133
- else
134
- []
135
- end
136
- @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
137
- web.bliki.values.select do |entry|
138
- words.all? do |word|
139
- (search_names and entry.name.index(word)) or (search_content and entry.content.index(word))
140
- end
141
- end
142
- else
143
- []
144
- end
145
-
146
- when 'exact'
147
- @results = if [nil, 'all', 'pages'].include? @params['where']
148
- web.select do |page|
149
- (search_names and page.name.index(@query)) or (search_content and page.content.index(@query))
150
- end
151
- else
152
- []
153
- end
154
- @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
155
- web.bliki.values.select do |entry|
156
- (search_names and entry.name.index(@query)) or (search_content and entry.content.index(@query))
157
- end
158
- else
159
- []
160
- end
161
- end
162
-
163
- if !@params['category'].nil? and @params['category'] != 'noselect'
164
- @selected_categories = parse_multi_select 'category'
165
- @results.reject! { |page| (page.categories & @selected_categories).empty? }
166
- @bliki_results.reject! { |page| (page.categories & @selected_categories).empty? }
167
- end
168
-
169
- if !@params['author'].nil? and @params['author'] != 'noselect'
170
- @selected_authors = parse_multi_select 'author'
171
- @results.reject! { |page| (page.authors & @selected_authors).empty? }
172
- @bliki_results.reject! { |page| (page.authors & @selected_authors).empty? }
173
- end
174
-
175
- redirect_show(@results.first.name) if @results.length == 1 && @bliki_results.length == 0
176
- redirect_path("/#{web_address}/bliki_revision/#{@bliki_results.first.name}?rev=#{@bliki_results.first.revisions.size-1}") if @results.length == 0 && @bliki_results.length == 1
177
- render_action "search"
178
- end
179
-
180
- def glossary
181
- set_menu_pages
182
-
183
- rex = %r{([A-Z\d]+)\(([\w\s]+)\)}
184
- scan_results = web.select.map { |page| [page.link, page.content.scan(rex)] }
185
- scan_results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] }
186
- results = Hash.new { Array.new }
187
- scan_results.each { |page, acronyms| acronyms.each { |ac| results[ac] += [page] } }
188
- @results = results.map{ |(ac, df), pg|[[ac,df], pg.uniq] }.sort_by{ |(ac, df), pg| ac }
189
-
190
- acronyms = @results.map { |(ac, df), pg| ac }
191
- rex = %r{(#{acronyms.join('|')})[^\(]}
192
- results = web.select.map { |page| [page.link, page.content.scan(rex)] }
193
- results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] }
194
- @undefined_on = Hash.new { Array.new }
195
- results.each { |page, acronyms| acronyms.each { |ac| @undefined_on[ac[0]] += [page] } }
196
- @undefined_on = @undefined_on.inject({}) { |hsh, (k, v)| hsh[k] = v.uniq; hsh }
197
- end
198
-
199
- def authors
200
- set_menu_pages
201
- @authors = web.select.authors
202
- end
203
-
204
- def recently_revised
205
- parse_category
206
- set_menu_pages
207
- @pages_by_revision = @pages_in_category.by_revision
208
- end
209
-
210
- def rss_with_content
211
- @pages_by_revision = @rss_bliki_only ? [] : web.select.by_revision.first(15)
212
- @bliki_entries = web.bliki_entries_by_date
213
- @uri = @req.request_uri
214
- host = @req.meta_vars["HTTP_X_FORWARDED_HOST"] || "#{@uri.host}:#{@uri.port.to_s}"
215
- @web_url = "#{@uri.scheme}://#{host}/#{@web.address}"
216
- @res["Content-Type"] = "text/xml"
217
- render "wiki/rss_feed"
218
- end
219
-
220
- def rss_with_headlines
221
- @hide_description = true
222
- rss_with_content
223
- end
224
-
225
- def rss_bliki_only
226
- @rss_bliki_only = true
227
- rss_with_content
228
- end
229
-
230
- def rss_todo_items
231
- todo
232
- @display_todo = true
233
- rss_with_content
234
- end
235
-
236
- def feeds
237
- set_menu_pages
238
- end
239
-
240
- def list
241
- parse_category
242
- set_menu_pages
243
- @pages_by_name = @pages_in_category.by_name
244
- @page_names_that_are_wanted = @pages_in_category.wanted_pages
245
- @pages_that_are_orphaned = @pages_in_category.orphaned_pages
246
-
247
- if @req.query['Action']
248
- # redirect_action 'list/' if web.check_pass_on_edit and not password_check(@params['password'])
249
-
250
- case @req.query['Action']
251
- when 'Delete' # Handle page deletion
252
- wiki.delete_page(web_address, @req.query['del_sel_page_name'])
253
- redirect_action "list/"
254
-
255
- when 'Create' # Handle page creation
256
- redirect_show @req.query['newpage']
257
-
258
- when 'Rename' # Handle page rename
259
- wiki.rename_page(web_address, @req.query['ren_sel_page_name'], @req.query['newpage'])
260
- redirect_action "list/"
261
- end
262
- end
263
-
264
- end
265
-
266
- def export
267
- set_menu_pages
268
- end
269
-
270
- def export_html
271
- file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
272
- file_path = File.join EXPORT_DIRECTORY, file_name
273
-
274
- export_pages_to_zip_file(file_path) unless FileTest.exists?(file_path)
275
- send_export(file_name, file_path)
276
- end
277
-
278
- def export_markup
279
- file_name = "#{web.address}-markup-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
280
- file_path = File.join EXPORT_DIRECTORY, file_name
281
-
282
- export_markup_to_zip_file(file_path) unless FileTest.exists?(file_path)
283
- send_export(file_name, file_path)
284
- end
285
-
286
- def export_pdf
287
- file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}"
288
- file_path = File.join EXPORT_DIRECTORY, file_name
289
-
290
- export_web_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
291
- convert_tex_to_pdf(file_path + ".tex")
292
- send_export(file_name + ".pdf", file_path + ".pdf")
293
- end
294
-
295
- def export_tex
296
- file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}.tex"
297
- file_path = File.join EXPORT_DIRECTORY, file_name
298
-
299
- export_web_to_tex(file_path) unless FileTest.exists?(file_path)
300
- send_export(file_name, file_path)
301
- end
302
-
303
- def edit_web #{{{
304
- parse_category
305
- set_mm_options
306
- @snapshot_interval = WikiService.snapshot_interval_hours
307
- end #}}}
308
-
309
- def update_web
310
- redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
311
-
312
- set_mm_options
313
-
314
- wiki.update_web(
315
- web.address, @params["address"], @params["name"],
316
- @params["markup"].intern,
317
- @params["color"], @params["additional_style"],
318
- @params["safe_mode"] ? true : false,
319
- @params["password"].empty? ? nil : @params["password"],
320
- @params["published"] ? true : false,
321
- @params['default_to_published'] ? true : false,
322
- @params["brackets_only"] ? true : false,
323
- @params["count_pages"] ? true : false,
324
- @params['mind_map_size'],
325
- @params['symbols_map'],
326
- @params['links_map'],
327
- @params['snapshots_interval'],
328
- @params['enable_dclick_edit'],
329
- @params['enable_menu'],
330
- @params['check_pass_on_edit'] == 'each_edit',
331
- @prog, @graph_type, @missing, @show_authors, @show_leaves, @selected_categories
332
- )
333
-
334
- redirect_show("HomePage", @params["address"])
335
- end
336
-
337
- def administrate
338
- @logger.info "Taking administrative action: #{@params['action']}"
339
- redirect_show 'HomePage' unless wiki.authenticate(@params['system_password'])
340
- case @params['action']
341
- when 'Delete Orphan Pages'
342
- wiki.remove_orphaned_pages(web_address)
343
-
344
- when 'Clear Render Cache'
345
- clear_render_cache true
346
-
347
- when 'Force Data Snapshot'
348
- WikiService.take_snapshot
349
-
350
- when 'Clean Storage'
351
- WikiService.clean_old_snapshots
352
-
353
- when 'Make This Web Default'
354
- wiki.default_web = web_address
355
-
356
- when 'Remove This Web'
357
- wiki.webs.delete web_address
358
- redirect_path '/'
359
-
360
- end
361
- @message = 'Operation succeeded'
362
- redirect_action 'edit_web/'
363
- end
364
-
365
- FAR_FUTURE = Date.new(4000,1,1).freeze
366
- TODO_RE = %r{<todo-tag context='(.*?)' due_date='(.*?)'><span class="todo"><strong>TODO(?:[^:]*)?:</strong> (.*?)</span></todo-tag>}
367
-
368
- def todo #{{{
369
- parse_category
370
- set_menu_pages
371
-
372
- @context = @params['context']
373
- @sort_order = @params['sort_order']
374
-
375
- # the clear_render_cache hack to make sure we don't have old-style rendered
376
- # todos is no longer needed - can go thru the edit web to explicitely clear the cache.
377
- @todo_items = analyse_rendered_todo_items @pages_in_category.by_name
378
- @bliki_todo_items = analyse_rendered_todo_items web.bliki.values
379
-
380
- @context_links = @todo_items.clone.update(@bliki_todo_items).map { |page, items|
381
- # 'items' contain the full 'todo' info
382
- items.map { |item| item.context }
383
- }.flatten.compact.uniq.reject { |c| c.nil? || c.empty? }.map { |context|
384
- @context == context ?
385
- "[<span class='selected'>#{context}</span>]" :
386
- "<a href='?context=#{context}#{"&sort_order=#{@sort_order}" if @sort_order}'>#{context}</a>"
387
- }
388
-
389
- @todo_items = sort_and_filter_todo_items @todo_items, @sort_order, @context
390
- @bliki_todo_items = sort_and_filter_todo_items @bliki_todo_items, @sort_order, @context
391
-
392
- end #}}}
393
-
394
- def analyse_rendered_todo_items pages
395
- items = Hash.new { Array.new }
396
- pages.each do |page|
397
- if page.has_todos?
398
- # Page has todo items. Get the rendered version (marked-up and with links):
399
- # I specifically don't use the todo chunkss because I want the fully marked-up
400
- # text of the item.
401
- content = page.revisions.last.display_content
402
- items[page] = content.scan(TODO_RE).map { |match|
403
- RenderedTodo.new match[2], (match[0].empty? ? [] : match[0].split(',') ), (Date.parse(match[1]) rescue FAR_FUTURE)
404
- }
405
- end
406
- end
407
- items
408
- end
409
-
410
- def sort_and_filter_todo_items items, sort_order, context
411
- case sort_order
412
- when 'due_date'
413
- result = items.map { |page, todos|
414
- # sort the to items themselves
415
- [page, todos.sort_by { |i| i.due_date } ]
416
- }.sort_by { |page, todos|
417
- # sort the pages by the one with the most urgent todo
418
- todos.map{ |i| i.due_date }.min
419
- }
420
-
421
- else # default = sort by page name
422
- result = items.sort_by { |page, items| page.name }
423
-
424
- end
425
-
426
- if context
427
- # filter the items to those that are in context
428
- result = result.map { |page, items|
429
- [ page, items.select { |item| item.context.include? context } ]
430
- }.select { |page, items| not items.empty? }
431
- end
432
- result
433
- end
434
-
435
- def get_todo_display_style due_date
436
- # default is the muted 'darkred', to prevent to many bright red
437
- # items on one page: (See also chunks/todo.rb)
438
- (due_date <=> Date.today) > -1 ? "todoFuture" : "todo"
439
- end
440
-
441
- def clear_render_cache dont_redirect=false
442
- web.refresh_revisions
443
- redirect_path "/#{web_address}/edit_web/" unless dont_redirect
444
- end
445
-
446
- def set_menu_pages published = false #{{{
447
- parse_category
448
- @all_pages = web.select { true }
449
- @menu_pages = case web.menu_type
450
- when 'all' then @all_pages.by_name
451
- when 'recent' then @all_pages.by_last_visited
452
- when 'viewed' then @all_pages.by_most_viewed
453
- when 'revised' then @all_pages.by_revision
454
- when 'category' then web.select { |page| page.in_category?(web.menu_category) }
455
- when 'user'
456
- @menu_content = if Page === web.menu_content
457
- web.menu_content.revisions.last.display_content
458
- else
459
- web.menu_content
460
- end
461
- @menu_content = @menu_content.gsub('/show/', '/published/').gsub( %r{<a href="../published/.*?">\?</a>}, '') if published
462
- nil
463
- when 'linkers'
464
- web.select { |page|
465
- page.wiki_words.size > 0
466
- }.sort_by { |page| page.name }
467
- end
468
- if web.menu_limit && @menu_pages
469
- @menu_pages = @menu_pages[0..web.menu_limit]
470
- end
471
- end #}}}
472
-
473
- def bliki #{{{
474
- set_menu_pages
475
- @entries = web.bliki_entries_by_date
476
- unless @req.query['authorname'].nil? || @req.query['authorname'] == 'noselect'
477
- @entries = @entries.select { |page|
478
- page.authors.include?(@req.query['authorname'])
479
- }
480
- end
481
- unless @req.query['regexp'].nil?
482
- @entries = @entries.select { |page|
483
- page.content =~ /#{@req.query['regexp']}/i
484
- }
485
- end
486
- @color = web.color
487
- @authors = web.authors
488
- end #}}}
489
-
490
- def mind #{{{
491
- parse_category
492
- set_menu_pages
493
- set_mm_options
494
-
495
- @pngFile = @mapFile = nil
496
- case @graph_type
497
- when 'normal'
498
- @pngFile, @mapFile = web.create_mind_map(@prog, @missing,
499
- @show_authors, @show_leaves, @selected_categories, @mm_size)
500
-
501
- when 'author'
502
- @pngFile, @mapFile = web.create_author_graph(@prog, @selected_categories, @mm_size)
503
-
504
- when 'category'
505
- @pngFile, @mapFile = web.create_category_graph(@prog,
506
- @show_authors, @selected_categories, @mm_size)
507
- end
508
- end #}}}
509
-
510
- def set_mm_options #{{{
511
- if @req.query.empty?
512
- @prog = web.mm_prog
513
- @graph_type = web.mm_graph_type
514
- @missing = @pages_in_category.wanted_pages if web.mm_show_missing
515
- @show_authors = web.mm_show_authors
516
- @show_leaves = web.mm_show_leaves
517
- @selected_categories = web.mm_selected_categories
518
- @mm_size = web.mind_map_size
519
- else
520
- @prog = @req.query['draw_type'] || 'neato'
521
- @graph_type = @req.query['graph_type'] || 'normal'
522
- @missing = @pages_in_category.wanted_pages if @req.query['missing']
523
- @show_authors = @req.query['show_authors'] == 'on'
524
- @show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on'
525
-
526
- @selected_categories = parse_multi_select 'selected_categs'
527
- @selected_categories = [] if @selected_categories.include? 'all'
528
- @mm_size = @params['mind_map_size'] || web.mind_map_size
529
- end
530
- end #}}}
531
-
532
- def parse_multi_select field #{{{
533
- if @req.body
534
- @req.body.split('&').map { |pair|
535
- pair.split('=') }.select { |k,v|
536
- k == field }.map { |k,v| v }
537
- else
538
- []
539
- end
540
- end #}}}
541
-
542
- def edit_menu #{{{
543
- @menu_type = web.menu_type
544
- @menu_content = web.menu_content
545
- @list_limit = web.menu_limit
546
- @list_limit += 1 if @list_limit >= -1
547
- @author = default_author
548
- end #}}}
549
-
550
- def save_menu #{{{
551
- redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
552
-
553
- unless @req.query['action'] == 'Cancel Update'
554
- type = @req.query['type']
555
- content = @req.query['content']
556
- category = @req.query['category']
557
- author = @req.query['author']
558
-
559
- limit = @req.query['limit'].to_i rescue nil
560
- limit = 20 unless limit
561
- limit -= 1 if limit >= 0
562
-
563
- # need to go through the WikiService to persist the command:
564
- wiki.save_menu_pref(web_address, type, limit, content, category, Author.new(author, remote_ip))
565
- end
566
-
567
- redirect_action 'edit_web/' # go back to web options page
568
- end #}}}
569
-
570
- def get_map_img
571
- file_name = "map.png"
572
- file_path = File.join WikiService.storage_path, file_name
573
- send_export(file_name, file_path, "image/png")
574
- end
575
-
576
- def adv_search
577
- parse_category
578
- set_menu_pages
579
- end
580
-
581
- # Within a single page --------------------------------------------------------
582
-
583
- def show
584
- set_menu_pages
585
- if @page = wiki.read_page(web_address, page_name)
586
- unless page_name == 'HomePage'
587
- # HomePage should not be in the menu as there's a link at the top.
588
- @page.last_visited = Time.now
589
- @page.viewed += 1
590
- end
591
- begin
592
- render_action "page"
593
- rescue => e
594
- $stderr << e.backtrace.join("\n")
595
- redirect_action "edit/#{CGI.escape(page_name)}?msg=#{CGI.escape(e.message)}"
596
- end
597
- else
598
- if page_name
599
- redirect_action "new/#{CGI.escape(page_name)}"
600
- else
601
- redirect_show "HomePage"
602
- end
603
- end
604
- end
605
-
606
- def published
607
- set_menu_pages true
608
- if web.published then @page = wiki.read_page(web_address, page_name || "HomePage") else redirect_show("HomePage") end
609
- end
610
-
611
- def print
612
- @page = wiki.read_page(web_address, page_name)
613
- end
614
-
615
- def tex
616
- @page = wiki.read_page(web_address, page_name)
617
- @tex_content = RedClothForTex.new(@page.content).to_tex
618
- end
619
-
620
- def pdf
621
- page = wiki.read_page(web_address, page_name)
622
- safe_page_name = page.name.gsub(/\W/, "")
623
- file_name = "#{safe_page_name}-#{web.address}-#{page.created_at.strftime("%Y-%m-%d-%H-%M")}"
624
- file_path = File.join EXPORT_DIRECTORY, file_name
625
-
626
- export_page_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
627
- convert_tex_to_pdf(file_path + ".tex")
628
- send_export(file_name + ".pdf", file_path + ".pdf")
629
- end
630
-
631
- def new
632
- # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password'])
633
- @page_name, @author = page_name, default_author
634
- end
635
-
636
- def edit
637
- @page = wiki.read_page(web_address, page_name)
638
-
639
- if !@page.locked?(Time.now) || @params["break_lock"]
640
- @page.lock(Time.now, default_author)
641
- @author = default_author
642
- render
643
- else
644
- render "wiki/locked"
645
- end
646
- end
647
-
648
- def cancel_edit
649
- @page = wiki.read_page(web_address, page_name)
650
- @page.unlock
651
- redirect_show
652
- end
653
-
654
- def save
655
- # if web.check_pass_on_edit and not password_check(@params['password'])
656
- # wiki.read_page(web_address, page_name).unlock if web.pages[page_name]
657
- # redirect_show("HomePage")
658
- # end
659
-
660
- if web.pages[page_name]
661
- page = wiki.revise_page(
662
- web_address, page_name, @params["content"], Time.now,
663
- Author.new(@params["author"], remote_ip), @params['edit_type']
664
- )
665
-
666
- page.unlock
667
- else
668
- page = wiki.write_page(
669
- web_address, page_name, @params["content"], Time.now,
670
- Author.new(@params["author"], remote_ip)
671
- )
672
- end
673
-
674
- write_cookie("author", @params["author"], true)
675
- redirect_show(page_name)
676
- end
677
-
678
- def revision
679
- redirect_show 'HomePage' if page_name.nil?
680
- set_menu_pages
681
- @page = wiki.read_page(web_address, page_name)
682
- @revision = @page.revisions[@params["rev"].to_i]
683
- end
684
-
685
- def rollback
686
- # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password'])
687
-
688
- @page = wiki.read_page(web_address, page_name)
689
- @revision = @page.revisions[@params["rev"].to_i]
690
- end
691
-
692
- # Bliki ----------------------------------------------------------------------
693
-
694
- def bliki_delete
695
- # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
696
-
697
- wiki.delete_bliki_entry(web_address, page_name)
698
- redirect_bliki
699
- end
700
-
701
- def bliki_edit
702
- # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
703
-
704
- @page = wiki.read_bliki_entry(web_address, page_name)
705
-
706
- if !@page.locked?(Time.now) || @params["break_lock"]
707
- @page.lock(Time.now, default_author)
708
- @author = default_author
709
- render
710
- else
711
- @bliki_entry = true
712
- render "wiki/locked"
713
- end
714
- end
715
-
716
- def cancel_bliki_edit
717
- @page = wiki.read_bliki_entry(web_address, page_name)
718
- @page.unlock if @page
719
- redirect_bliki
720
- end
721
-
722
- def bliki_new
723
- @entry_name = @params['entry_name']
724
- @author = default_author
725
- end
726
-
727
- def bliki_save
728
- # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
729
-
730
- pname = page_name || @params["pagename"]
731
- if web.bliki[pname]
732
- page = wiki.revise_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
733
- page.unlock
734
- else
735
- page = wiki.write_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
736
- end
737
-
738
- write_cookie("author", @params["author"])
739
- redirect_bliki
740
- end
741
-
742
- def bliki_revision
743
- parse_category
744
- set_menu_pages
745
- @page = wiki.read_bliki_entry(web_address, page_name || @params['pagename'])
746
- @revision = @page.revisions[@params["rev"].to_i] || @page.revisions.last
747
- end
748
-
749
- def rollback_bliki
750
- # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
751
-
752
- @page = wiki.read_bliki_entry(web_address, page_name)
753
- wiki.rollback_bliki_entry(web_address, page_name, @params["rev"].to_i, Time.now)
754
- redirect_bliki
755
- end
756
-
757
- # ----------------------------------------------------------------------------
758
-
759
- protected
760
- def before_action
761
- if in_a_web? && !authorized?(web_address) && !%w( login authenticate published ).include?(action_name)
762
- redirect_action("login")
763
- return false
764
- elsif in_a_web?
765
- @web, @page_name, @action_name = web, page_name, action_name
766
- end
767
- end
768
-
769
- def action_name
770
- if in_a_web?
771
- request_path[1]
772
- elsif action_methods.include?(request_path[0])
773
- request_path[0]
774
- else
775
- "index"
776
- end
777
- end
778
-
779
- def redirect_show(page = @page.name, web = web_address)
780
- redirect_path "/#{web}/show/#{CGI.escape(page)}"
781
- end
782
-
783
- def redirect_bliki
784
- redirect_path "/#{web_address}/bliki/"
785
- end
786
-
787
- def redirect_action(action, web = web_address)
788
- redirect_path "/#{web}/#{action}"
789
- end
790
-
791
- def link_to_bliki(entry, web = web_address) #{{{
792
- "<a class='existingWikiWord' href='/#{web}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>"
793
- end #}}}
794
-
795
-
796
- private
797
- def wiki
798
- WikiService.instance
799
- end
800
-
801
- def web
802
- wiki.webs[web_address]
803
- end
804
-
805
- def in_a_web?
806
- request_path.length > 1
807
- end
808
-
809
- def web_address
810
- request_path[0]
811
- end
812
-
813
- def page_name
814
- CGI.unescape(request_path[2]) if request_path[2]
815
- end
816
-
817
- def authorized?(web_address)
818
- web.nil? ||
819
- web.password.nil? ||
820
- (read_cookie(web_address) && read_cookie(web_address) == web.password) ||
821
- password_check(@params["password"])
822
- end
823
-
824
- def default_author
825
- read_cookie("author") || "AnonymousCoward"
826
- end
827
-
828
- def password_check(password)
829
- web && @params["password"] == web.password && write_cookie(web_address, @params["password"])
830
- end
831
-
832
- def export_pages_to_zip_file(zip_file_path)
833
- Zip::ZipOutputStream.open(zip_file_path) do |zos|
834
- web.select.by_name.each do |@page|
835
- zos.put_next_entry(@page.name + ".html")
836
- zos.puts(template_engine("print").result(binding))
837
- end
838
-
839
- zos.put_next_entry "pages-metadata.txt"
840
- web.select.by_name.each do |page|
841
- zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}"
842
- end
843
-
844
- web.select_bliki.by_name.each do |@page|
845
- zos.put_next_entry("bliki/#{@page.name}.html")
846
- zos.puts(template_engine("print").result(binding))
847
- end
848
-
849
- zos.put_next_entry "bliki/bliki-metadata.txt"
850
- web.select_bliki.by_name.each do |page|
851
- zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}"
852
- end
853
-
854
- zos.put_next_entry("index.html")
855
- zos.puts('<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html>')
856
- end
857
- end
858
-
859
- def export_markup_to_zip_file(zip_file_path)
860
- Zip::ZipOutputStream.open(zip_file_path) do |zos|
861
- web.select.by_name.each do |page|
862
- zos.put_next_entry(page.name + ".#{web.markup}")
863
- zos.puts(page.content)
864
- end
865
-
866
- zos.put_next_entry "pages-metadata.txt"
867
- web.select.by_name.each do |page|
868
- zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}"
869
- end
870
-
871
- web.select_bliki.by_name.each do |page|
872
- zos.put_next_entry("bliki/#{page.name}.#{web.markup}")
873
- zos.puts(page.content)
874
- end
875
-
876
- zos.put_next_entry "bliki/bliki-metadata.txt"
877
- web.select_bliki.by_name.each do |page|
878
- zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}"
879
- end
880
- end
881
- end
882
-
883
- def export_web_to_tex(file_path)
884
- @web_name = web.name
885
- @tex_content = table_of_contents(web.pages["HomePage"].content.dup, render_tex_web)
886
- File.open(file_path, "w") { |f| f.write(template_engine("tex_web").result(binding)) }
887
- end
888
-
889
- def render_tex_web
890
- pages = web.select.by_name.inject({}) do |tex_web, page|
891
- tex_web[page.name] = RedClothForTex.new(page.content).to_tex
892
- tex_web
893
- end
894
-
895
- bliki_entries = web.select_bliki.by_name.inject({}) do |tex_web, page|
896
- tex_web["bliki/#{page.name}"] = RedClothForTex.new(page.content).to_tex
897
- tex_web
898
- end
899
-
900
- pages.update bliki_entries
901
- end
902
-
903
- def export_page_to_tex(file_path)
904
- tex
905
- File.open(file_path, "w") { |f| f.write(template_engine("tex").result(binding)) }
906
- end
907
-
908
- def convert_tex_to_pdf(tex_path)
909
- `cd #{File.dirname(tex_path)}; pdflatex --interaction=scrollmode '#{File.basename(tex_path)}'`
910
- end
911
-
912
- def truncate(text, length = 30, truncate_string = "...")
913
- if text.length > length then text[0..(length - 3)] + truncate_string else text end
914
- end
915
-
916
- def render_markup_help
917
- if web
918
- markup = web.markup
919
- markup = 'markdown' if markup.to_s =~ /markdown/
920
- sub_template("#{markup}_help")
921
- else
922
- ''
923
- end
924
- end
925
-
926
- def send_export(file_name, file_path, content_type = "application/zip")
927
- @res["Content-Type"] = content_type
928
- @res["Content-Disposition"] = "attachment; filename=#{file_name}"
929
- @res["Content-Length"] = File.size(file_path)
930
- File.open(file_path, "rb") { |f| @res.body = f.read }
931
- end
932
-
933
- def template_engine(template_name)
934
- ERB.new(IO.readlines(action_template_path(template_name)).join)
935
- end
936
-
937
- def remote_ip
938
- $stderr << "#{@req.meta_vars['HTTP_X_FORWARDED_FOR']} || #{@req.meta_vars['REMOTE_ADDR']}"
939
- @req.meta_vars["HTTP_X_FORWARDED_FOR"] || @req.meta_vars["REMOTE_ADDR"]
940
- end
941
- end
942
-
1
+ require "cgi"
2
+ require "redcloth_for_tex"
3
+
4
+ RenderedTodo = Struct.new( :text, :context, :due_date )
5
+
6
+ class WikiController < ActionControllerServlet
7
+ EXPORT_DIRECTORY = WikiService.storage_path
8
+
9
+ def index
10
+ if web_address
11
+ check_external_req_and_redirect web
12
+ elsif !wiki.setup?
13
+ redirect_path "/new_system/"
14
+ elsif wiki.webs.length == 1
15
+ check_external_req_and_redirect wiki.webs.values.first
16
+ else
17
+ wiki.default_web ?
18
+ check_external_req_and_redirect(wiki.webs[wiki.default_web]) :
19
+ redirect_path("/web_list/")
20
+ end
21
+ end
22
+
23
+ def check_external_req_and_redirect web
24
+ if web.default_to_published and @req.addr[2] != @req.peeraddr[2]
25
+ redirect_action("published/", web.address)
26
+ else
27
+ redirect_show("HomePage", web.address)
28
+ end
29
+ end
30
+
31
+ # Administrating the Instiki setup --------------------------------------------
32
+
33
+ def new_system
34
+ wiki.setup? ? redirect_path("/") : render
35
+ end
36
+
37
+ def new_web
38
+ redirect_path("/") if wiki.system["password"].nil?
39
+ end
40
+
41
+ def create_system
42
+ wiki.setup(@params["password"], @params["web_name"], @params["web_address"]) unless wiki.setup?
43
+ redirect_path "/"
44
+ end
45
+
46
+ def create_web
47
+ redirect_path("/") unless wiki.authenticate(@params["system_password"])
48
+ wiki.create_web(@params["name"], @params["address"])
49
+ redirect_show("HomePage", @params["address"])
50
+ end
51
+
52
+
53
+ # Outside a single web --------------------------------------------------------
54
+
55
+ def web_list
56
+ @system, @webs = wiki.system, wiki.webs.values
57
+ render "wiki/web_list"
58
+ end
59
+
60
+ def login
61
+ render "wiki/login"
62
+ end
63
+
64
+ def authenticate
65
+ password_check(@params["password"]) ? redirect_show("HomePage") : redirect_action("login")
66
+ end
67
+
68
+ def static_style_sheet() render "static_style_sheet" end
69
+
70
+ def stop
71
+ redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
72
+
73
+ begin
74
+ secs = @params['seconds'].to_i
75
+ raise if secs.zero?
76
+ @logger.warn "Pimki server will stop in #{secs} seconds!"
77
+ WikiService.request_stop
78
+ render_text "Pimki server will stop in #{secs} seconds!"
79
+ Thread.new { sleep secs; Kernel.exit! }
80
+ rescue
81
+ redirect_show("HomePage")
82
+ end
83
+ end
84
+
85
+ # Within a single web ---------------------------------------------------------
86
+
87
+ def parse_category
88
+ @categories = web.categories
89
+ @category = @params["category"]
90
+ @pages_in_category = web.select { |page| page.in_category?(@category) }
91
+ @pages_without_category = web.select { |page| page.categories.length == 0 }
92
+ if @category == 'none'
93
+ @pages_in_category = @pages_without_category
94
+ end
95
+ @set_name = ( @categories.include?(@category) ? "category '#{@category}'" : "the web" )
96
+ @category_links = @categories.map do |c|
97
+ (@category == c ? "<span class=\"selected\">[#{c}]</span>" : "<a href=\"?category=#{c}\">#{c}</a>")
98
+ end
99
+ end
100
+
101
+ def search
102
+ set_menu_pages
103
+ @query = @params["query"]
104
+ search_content = [nil, 'both', 'contents'].include? @params['fields']
105
+ search_names = [nil, 'both', 'names'].include? @params['fields']
106
+
107
+ case @params['expression']
108
+ when 'regex', nil
109
+ rex = Regexp.new @query, (Regexp::IGNORECASE unless @params['case'])
110
+ @results = if [nil, 'all', 'pages', nil].include? @params['where']
111
+ web.select do |page|
112
+ (search_names and rex.match(page.name)) or (search_content and rex.match(page.content))
113
+ end
114
+ else
115
+ []
116
+ end
117
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
118
+ web.bliki.values.select do |entry|
119
+ (search_names and rex.match(entry.name)) or (search_content and rex.match(entry.content))
120
+ end
121
+ else
122
+ []
123
+ end
124
+
125
+ when 'all'
126
+ words = @query.split(/\s/).reject { |w| w.empty? }
127
+ @results = if [nil, 'all', 'pages'].include? @params['where']
128
+ web.select do |page|
129
+ words.all? do |word|
130
+ (search_names and page.name.index(word)) or (search_content and page.content.index(word))
131
+ end
132
+ end
133
+ else
134
+ []
135
+ end
136
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
137
+ web.bliki.values.select do |entry|
138
+ words.all? do |word|
139
+ (search_names and entry.name.index(word)) or (search_content and entry.content.index(word))
140
+ end
141
+ end
142
+ else
143
+ []
144
+ end
145
+
146
+ when 'exact'
147
+ @results = if [nil, 'all', 'pages'].include? @params['where']
148
+ web.select do |page|
149
+ (search_names and page.name.index(@query)) or (search_content and page.content.index(@query))
150
+ end
151
+ else
152
+ []
153
+ end
154
+ @bliki_results = if [nil, 'all', 'bliki'].include? @params['where']
155
+ web.bliki.values.select do |entry|
156
+ (search_names and entry.name.index(@query)) or (search_content and entry.content.index(@query))
157
+ end
158
+ else
159
+ []
160
+ end
161
+ end
162
+
163
+ if !@params['category'].nil? and @params['category'] != 'noselect'
164
+ @selected_categories = parse_multi_select 'category'
165
+ @results.reject! { |page| (page.categories & @selected_categories).empty? }
166
+ @bliki_results.reject! { |page| (page.categories & @selected_categories).empty? }
167
+ end
168
+
169
+ if !@params['author'].nil? and @params['author'] != 'noselect'
170
+ @selected_authors = parse_multi_select 'author'
171
+ @results.reject! { |page| (page.authors & @selected_authors).empty? }
172
+ @bliki_results.reject! { |page| (page.authors & @selected_authors).empty? }
173
+ end
174
+
175
+ redirect_show(@results.first.name) if @results.length == 1 && @bliki_results.length == 0
176
+ redirect_path("/#{web_address}/bliki_revision/#{@bliki_results.first.name}?rev=#{@bliki_results.first.revisions.size-1}") if @results.length == 0 && @bliki_results.length == 1
177
+ render_action "search"
178
+ end
179
+
180
+ def glossary
181
+ set_menu_pages
182
+
183
+ rex = %r{([A-Z\d]+)\(([\w\s]+)\)}
184
+ scan_results = web.select.map { |page| [page.link, page.content.scan(rex)] }
185
+ scan_results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] }
186
+ results = Hash.new { Array.new }
187
+ scan_results.each { |page, acronyms| acronyms.each { |ac| results[ac] += [page] } }
188
+ @results = results.map{ |(ac, df), pg|[[ac,df], pg.uniq] }.sort_by{ |(ac, df), pg| ac }
189
+
190
+ acronyms = @results.map { |(ac, df), pg| ac }
191
+ rex = %r{(#{acronyms.join('|')})[^\(]}
192
+ results = web.select.map { |page| [page.link, page.content.scan(rex)] }
193
+ results += web.bliki.values.map { |entry| [link_to_bliki(entry), entry.content.scan(rex)] }
194
+ @undefined_on = Hash.new { Array.new }
195
+ results.each { |page, acronyms| acronyms.each { |ac| @undefined_on[ac[0]] += [page] } }
196
+ @undefined_on = @undefined_on.inject({}) { |hsh, (k, v)| hsh[k] = v.uniq; hsh }
197
+ end
198
+
199
+ def authors
200
+ set_menu_pages
201
+ @authors = web.select.authors
202
+ end
203
+
204
+ def recently_revised
205
+ parse_category
206
+ set_menu_pages
207
+ @pages_by_revision = @pages_in_category.by_revision
208
+ end
209
+
210
+ def rss_with_content
211
+ @pages_by_revision = @rss_bliki_only ? [] : web.select.by_revision.first(15)
212
+ @bliki_entries = web.bliki_entries_by_date
213
+ @uri = @req.request_uri
214
+ host = @req.meta_vars["HTTP_X_FORWARDED_HOST"] || "#{@uri.host}:#{@uri.port.to_s}"
215
+ @web_url = "#{@uri.scheme}://#{host}/#{@web.address}"
216
+ @res["Content-Type"] = "text/xml"
217
+ render "wiki/rss_feed"
218
+ end
219
+
220
+ def rss_with_headlines
221
+ @hide_description = true
222
+ rss_with_content
223
+ end
224
+
225
+ def rss_bliki_only
226
+ @rss_bliki_only = true
227
+ rss_with_content
228
+ end
229
+
230
+ def rss_todo_items
231
+ todo
232
+ @display_todo = true
233
+ rss_with_content
234
+ end
235
+
236
+ def feeds
237
+ set_menu_pages
238
+ end
239
+
240
+ def list
241
+ parse_category
242
+ set_menu_pages
243
+ @pages_by_name = @pages_in_category.by_name
244
+ @page_names_that_are_wanted = @pages_in_category.wanted_pages
245
+ @pages_that_are_orphaned = @pages_in_category.orphaned_pages
246
+
247
+ if @req.query['Action']
248
+ # redirect_action 'list/' if web.check_pass_on_edit and not password_check(@params['password'])
249
+
250
+ case @req.query['Action']
251
+ when 'Delete' # Handle page deletion
252
+ wiki.delete_page(web_address, @req.query['del_sel_page_name'])
253
+ clear_render_cache true
254
+ redirect_action "list/"
255
+
256
+ when 'Create' # Handle page creation
257
+ redirect_show @req.query['newpage']
258
+
259
+ when 'Rename' # Handle page rename
260
+ wiki.rename_page(web_address, @req.query['ren_sel_page_name'], @req.query['ren_newpage'])
261
+ clear_render_cache true
262
+ redirect_action "list/"
263
+ end
264
+ end
265
+
266
+ end
267
+
268
+ def export
269
+ set_menu_pages
270
+ end
271
+
272
+ def export_html
273
+ file_name = "#{web.address}-html-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
274
+ file_path = File.join EXPORT_DIRECTORY, file_name
275
+
276
+ export_pages_to_zip_file(file_path) unless FileTest.exists?(file_path)
277
+ send_export(file_name, file_path)
278
+ end
279
+
280
+ def export_markup
281
+ file_name = "#{web.address}-markup-#{web.revised_on.strftime("%Y-%m-%d-%H-%M")}.zip"
282
+ file_path = File.join EXPORT_DIRECTORY, file_name
283
+
284
+ export_markup_to_zip_file(file_path) unless FileTest.exists?(file_path)
285
+ send_export(file_name, file_path)
286
+ end
287
+
288
+ def export_pdf
289
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}"
290
+ file_path = File.join EXPORT_DIRECTORY, file_name
291
+
292
+ export_web_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
293
+ convert_tex_to_pdf(file_path + ".tex")
294
+ send_export(file_name + ".pdf", file_path + ".pdf")
295
+ end
296
+
297
+ def export_tex
298
+ file_name = "#{web.address}-tex-#{web.revised_on.strftime('%Y-%m-%d-%H-%M')}.tex"
299
+ file_path = File.join EXPORT_DIRECTORY, file_name
300
+
301
+ export_web_to_tex(file_path) unless FileTest.exists?(file_path)
302
+ send_export(file_name, file_path)
303
+ end
304
+
305
+ def edit_web #{{{
306
+ parse_category
307
+ set_mm_options
308
+ @snapshot_interval = WikiService.snapshot_interval_hours
309
+ end #}}}
310
+
311
+ def update_web
312
+ redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
313
+
314
+ parse_category
315
+ set_mm_options
316
+
317
+ wiki.update_web(
318
+ web.address, @params["address"], @params["name"],
319
+ @params["markup"].intern,
320
+ @params["color"], @params["additional_style"],
321
+ @params["safe_mode"] ? true : false,
322
+ @params["password"].empty? ? nil : @params["password"],
323
+ @params["published"] ? true : false,
324
+ @params['default_to_published'] ? true : false,
325
+ @params["brackets_only"] ? true : false,
326
+ @params["count_pages"] ? true : false,
327
+ @params['mind_map_size'],
328
+ @params['symbols_map'],
329
+ @params['links_map'],
330
+ @params['snapshots_interval'],
331
+ @params['enable_dclick_edit'] ? true : false,
332
+ @params['enable_menu'] ? true : false,
333
+ @params['check_pass_on_edit'] == 'each_edit',
334
+ @prog, @graph_type, @missing, @show_authors, @show_leaves, @selected_categories
335
+ )
336
+
337
+ redirect_show("HomePage", @params["address"])
338
+ end
339
+
340
+ def administrate
341
+ @logger.info "Taking administrative action: #{@params['action']}"
342
+ redirect_show 'HomePage' unless wiki.authenticate(@params['system_password'])
343
+ case @params['action']
344
+ when 'Delete Orphan Pages'
345
+ wiki.remove_orphaned_pages(web_address)
346
+
347
+ when 'Clear Render Cache'
348
+ clear_render_cache true
349
+
350
+ when 'Force Data Snapshot'
351
+ WikiService.take_snapshot
352
+
353
+ when 'Clean Storage'
354
+ WikiService.clean_old_snapshots
355
+
356
+ when 'Make This Web Default'
357
+ wiki.default_web = web_address
358
+
359
+ when 'Remove This Web'
360
+ wiki.webs.delete web_address
361
+ redirect_path '/'
362
+
363
+ end
364
+ @message = 'Operation succeeded'
365
+ redirect_action 'edit_web/'
366
+ end
367
+
368
+ FAR_FUTURE = Date.new(4000,1,1).freeze
369
+ TODO_RE = %r{<todo-tag context='(.*?)' due_date='(.*?)'><span class="todo"><strong>TODO(?:[^:]*)?:</strong> (.*?)</span></todo-tag>}
370
+
371
+ def todo #{{{
372
+ parse_category
373
+ set_menu_pages
374
+
375
+ @context = @params['context']
376
+ @sort_order = @params['sort_order']
377
+
378
+ # the clear_render_cache hack to make sure we don't have old-style rendered
379
+ # todos is no longer needed - can go thru the edit web to explicitely clear the cache.
380
+ @todo_items = analyse_rendered_todo_items @pages_in_category.by_name
381
+ @bliki_todo_items = analyse_rendered_todo_items web.bliki.values
382
+
383
+ @context_links = @todo_items.clone.update(@bliki_todo_items).map { |page, items|
384
+ # 'items' contain the full 'todo' info
385
+ items.map { |item| item.context }
386
+ }.flatten.compact.uniq.reject { |c| c.nil? || c.empty? }.map { |context|
387
+ @context == context ?
388
+ "[<span class='selected'>#{context}</span>]" :
389
+ "<a href='?context=#{context}#{"&sort_order=#{@sort_order}" if @sort_order}'>#{context}</a>"
390
+ }
391
+
392
+ @todo_items = sort_and_filter_todo_items @todo_items, @sort_order, @context
393
+ @bliki_todo_items = sort_and_filter_todo_items @bliki_todo_items, @sort_order, @context
394
+
395
+ end #}}}
396
+
397
+ def analyse_rendered_todo_items pages
398
+ items = Hash.new { Array.new }
399
+ pages.each do |page|
400
+ if page.has_todos?
401
+ # Page has todo items. Get the rendered version (marked-up and with links):
402
+ # I specifically don't use the todo chunkss because I want the fully marked-up
403
+ # text of the item.
404
+ content = page.revisions.last.display_content
405
+ items[page] = content.scan(TODO_RE).map { |match|
406
+ RenderedTodo.new match[2], (match[0].empty? ? [] : match[0].split(',') ), (Date.parse(match[1]) rescue FAR_FUTURE)
407
+ }
408
+ end
409
+ end
410
+ items
411
+ end
412
+
413
+ def sort_and_filter_todo_items items, sort_order, context
414
+ case sort_order
415
+ when 'due_date'
416
+ result = items.map { |page, todos|
417
+ # sort the to items themselves
418
+ [page, todos.sort_by { |i| i.due_date } ]
419
+ }.sort_by { |page, todos|
420
+ # sort the pages by the one with the most urgent todo
421
+ todos.map{ |i| i.due_date }.min
422
+ }
423
+
424
+ else # default = sort by page name
425
+ result = items.sort_by { |page, items| page.name }
426
+
427
+ end
428
+
429
+ if context
430
+ # filter the items to those that are in context
431
+ result = result.map { |page, items|
432
+ [ page, items.select { |item| item.context.include? context } ]
433
+ }.select { |page, items| not items.empty? }
434
+ end
435
+ result
436
+ end
437
+
438
+ def get_todo_display_style due_date
439
+ # default is the muted 'darkred', to prevent to many bright red
440
+ # items on one page: (See also chunks/todo.rb)
441
+ (due_date <=> Date.today) > -1 ? "todoFuture" : "todo"
442
+ end
443
+
444
+ def clear_render_cache dont_redirect=false
445
+ web.refresh_revisions
446
+ redirect_path "/#{web_address}/edit_web/" unless dont_redirect
447
+ end
448
+
449
+ def set_menu_pages published = false #{{{
450
+ parse_category
451
+ @all_pages = web.select { true }
452
+ @menu_pages = case web.menu_type
453
+ when 'all' then @all_pages.by_name
454
+ when 'recent' then @all_pages.by_last_visited
455
+ when 'viewed' then @all_pages.by_most_viewed
456
+ when 'revised' then @all_pages.by_revision
457
+ when 'category' then web.select { |page| page.in_category?(web.menu_category) }
458
+ when 'user'
459
+ @menu_content = if Page === web.menu_content
460
+ web.menu_content.revisions.last.display_content
461
+ else
462
+ web.menu_content
463
+ end
464
+ @menu_content = @menu_content.gsub('/show/', '/published/').gsub( %r{<a href="../published/.*?">\?</a>}, '') if published
465
+ nil
466
+ when 'linkers'
467
+ web.select { |page|
468
+ page.wiki_words.size > 0
469
+ }.sort_by { |page| page.name }
470
+ end
471
+ if web.menu_limit && @menu_pages
472
+ @menu_pages = @menu_pages[0..web.menu_limit]
473
+ end
474
+ end #}}}
475
+
476
+ def bliki #{{{
477
+ set_menu_pages
478
+ @entries = web.bliki_entries_by_date
479
+ unless @req.query['authorname'].nil? || @req.query['authorname'] == 'noselect'
480
+ @entries = @entries.select { |page|
481
+ page.authors.include?(@req.query['authorname'])
482
+ }
483
+ end
484
+ unless @req.query['regexp'].nil?
485
+ @entries = @entries.select { |page|
486
+ page.content =~ /#{@req.query['regexp']}/i
487
+ }
488
+ end
489
+ @color = web.color
490
+ @authors = web.authors
491
+ end #}}}
492
+
493
+ def mind #{{{
494
+ parse_category
495
+ set_menu_pages
496
+ set_mm_options
497
+
498
+ @pngFile = @mapFile = nil
499
+ case @graph_type
500
+ when 'normal'
501
+ @pngFile, @mapFile = web.create_mind_map(@prog, @missing,
502
+ @show_authors, @show_leaves, @selected_categories, @mm_size)
503
+
504
+ when 'author'
505
+ @pngFile, @mapFile = web.create_author_graph(@prog, @selected_categories, @mm_size)
506
+
507
+ when 'category'
508
+ @pngFile, @mapFile = web.create_category_graph(@prog,
509
+ @show_authors, @selected_categories, @mm_size)
510
+ end
511
+ end #}}}
512
+
513
+ def set_mm_options #{{{
514
+ if @req.query.empty?
515
+ @prog = web.mm_prog
516
+ @graph_type = web.mm_graph_type
517
+ @missing = @pages_in_category.wanted_pages if web.mm_show_missing
518
+ @show_authors = web.mm_show_authors
519
+ @show_leaves = web.mm_show_leaves
520
+ @selected_categories = web.mm_selected_categories
521
+ @mm_size = web.mind_map_size
522
+ else
523
+ @prog = @req.query['draw_type'] || 'neato'
524
+ @graph_type = @req.query['graph_type'] || 'normal'
525
+ @missing = @pages_in_category.wanted_pages if @req.query['missing']
526
+ @show_authors = @req.query['show_authors'] == 'on'
527
+ @show_leaves = @req.query.empty? || @req.query['show_leaves'] == 'on'
528
+
529
+ @selected_categories = parse_multi_select 'selected_categs'
530
+ @selected_categories = [] if @selected_categories.include? 'all'
531
+ @mm_size = @params['mind_map_size'] || web.mind_map_size
532
+ end
533
+ end #}}}
534
+
535
+ def parse_multi_select field #{{{
536
+ if @req.body
537
+ @req.body.split('&').map { |pair|
538
+ pair.split('=') }.select { |k,v|
539
+ k == field }.map { |k,v| v }
540
+ else
541
+ []
542
+ end
543
+ end #}}}
544
+
545
+ def edit_menu #{{{
546
+ @menu_type = web.menu_type
547
+ @menu_content = web.menu_content
548
+ @list_limit = web.menu_limit
549
+ @list_limit += 1 if @list_limit >= -1
550
+ @author = default_author
551
+ end #}}}
552
+
553
+ def save_menu #{{{
554
+ redirect_show("HomePage") unless wiki.authenticate(@params["system_password"])
555
+
556
+ unless @req.query['action'] == 'Cancel Update'
557
+ type = @req.query['type']
558
+ content = @req.query['content']
559
+ sel_category = @req.query['sel_category']
560
+ author = @req.query['author']
561
+
562
+ limit = @req.query['limit'].to_i rescue nil
563
+ limit = 20 unless limit
564
+ limit -= 1 if limit >= 0
565
+
566
+ # need to go through the WikiService to persist the command:
567
+ wiki.save_menu_pref(web_address, type, limit, content, sel_category, Author.new(author, remote_ip))
568
+ end
569
+
570
+ redirect_action 'edit_web/' # go back to web options page
571
+ end #}}}
572
+
573
+ def get_map_img
574
+ file_name = "map.png"
575
+ file_path = File.join WikiService.storage_path, file_name
576
+ send_export(file_name, file_path, "image/png")
577
+ end
578
+
579
+ def adv_search
580
+ parse_category
581
+ set_menu_pages
582
+ end
583
+
584
+ # Within a single page --------------------------------------------------------
585
+
586
+ def show
587
+ set_menu_pages
588
+ if @page = wiki.read_page(web_address, page_name)
589
+ unless page_name == 'HomePage'
590
+ # HomePage should not be in the menu as there's a link at the top.
591
+ @page.last_visited = Time.now
592
+ @page.viewed += 1
593
+ end
594
+ begin
595
+ render_action "page"
596
+ rescue => e
597
+ $stderr << e.backtrace.join("\n")
598
+ redirect_action "edit/#{CGI.escape(page_name)}?msg=#{CGI.escape(e.message)}"
599
+ end
600
+ else
601
+ if page_name
602
+ redirect_action "new/#{CGI.escape(page_name)}"
603
+ else
604
+ redirect_show "HomePage"
605
+ end
606
+ end
607
+ end
608
+
609
+ def published
610
+ set_menu_pages true
611
+ if web.published then @page = wiki.read_page(web_address, page_name || "HomePage") else redirect_show("HomePage") end
612
+ end
613
+
614
+ def print
615
+ @page = wiki.read_page(web_address, page_name)
616
+ end
617
+
618
+ def tex
619
+ @page = wiki.read_page(web_address, page_name)
620
+ @tex_content = RedClothForTex.new(@page.content).to_tex
621
+ end
622
+
623
+ def pdf
624
+ page = wiki.read_page(web_address, page_name)
625
+ safe_page_name = page.name.gsub(/\W/, "")
626
+ file_name = "#{safe_page_name}-#{web.address}-#{page.created_at.strftime("%Y-%m-%d-%H-%M")}"
627
+ file_path = File.join EXPORT_DIRECTORY, file_name
628
+
629
+ export_page_to_tex(file_path + ".tex") unless FileTest.exists?(file_path + ".tex")
630
+ convert_tex_to_pdf(file_path + ".tex")
631
+ send_export(file_name + ".pdf", file_path + ".pdf")
632
+ end
633
+
634
+ def new
635
+ # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password'])
636
+ @page_name, @author = page_name, default_author
637
+ end
638
+
639
+ def edit
640
+ @page = wiki.read_page(web_address, page_name)
641
+
642
+ if !@page.locked?(Time.now) || @params["break_lock"]
643
+ @page.lock(Time.now, default_author)
644
+ @author = default_author
645
+ render
646
+ else
647
+ render "wiki/locked"
648
+ end
649
+ end
650
+
651
+ def cancel_edit
652
+ @page = wiki.read_page(web_address, page_name)
653
+ @page.unlock
654
+ redirect_show
655
+ end
656
+
657
+ def save
658
+ # if web.check_pass_on_edit and not password_check(@params['password'])
659
+ # wiki.read_page(web_address, page_name).unlock if web.pages[page_name]
660
+ # redirect_show("HomePage")
661
+ # end
662
+
663
+ if web.pages[page_name]
664
+ page = wiki.revise_page(
665
+ web_address, page_name, @params["content"], Time.now,
666
+ Author.new(@params["author"], remote_ip), @params['edit_type']
667
+ )
668
+
669
+ page.unlock
670
+ else
671
+ page = wiki.write_page(
672
+ web_address, page_name, @params["content"], Time.now,
673
+ Author.new(@params["author"], remote_ip)
674
+ )
675
+ end
676
+
677
+ write_cookie("author", @params["author"], true)
678
+ redirect_show(page_name)
679
+ end
680
+
681
+ def revision
682
+ redirect_show 'HomePage' if page_name.nil?
683
+ set_menu_pages
684
+ @page = wiki.read_page(web_address, page_name)
685
+ @revision = @page.revisions[@params["rev"].to_i]
686
+ end
687
+
688
+ def rollback
689
+ # redirect_show("HomePage") if web.check_pass_on_edit and not password_check(@params['password'])
690
+
691
+ @page = wiki.read_page(web_address, page_name)
692
+ @revision = @page.revisions[@params["rev"].to_i]
693
+ end
694
+
695
+ # Bliki ----------------------------------------------------------------------
696
+
697
+ def bliki_delete
698
+ # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
699
+
700
+ wiki.delete_bliki_entry(web_address, page_name)
701
+ clear_render_cache true
702
+ redirect_bliki
703
+ end
704
+
705
+ def bliki_edit
706
+ # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
707
+
708
+ @page = wiki.read_bliki_entry(web_address, page_name)
709
+
710
+ if !@page.locked?(Time.now) || @params["break_lock"]
711
+ @page.lock(Time.now, default_author)
712
+ @author = default_author
713
+ render
714
+ else
715
+ @bliki_entry = true
716
+ render "wiki/locked"
717
+ end
718
+ end
719
+
720
+ def cancel_bliki_edit
721
+ @page = wiki.read_bliki_entry(web_address, page_name)
722
+ @page.unlock if @page
723
+ redirect_bliki
724
+ end
725
+
726
+ def bliki_new
727
+ @entry_name = @params['entry_name']
728
+ @author = default_author
729
+ end
730
+
731
+ def bliki_save
732
+ # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
733
+
734
+ pname = page_name || @params["pagename"]
735
+ if web.bliki[pname]
736
+ page = wiki.revise_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
737
+ page.unlock
738
+ else
739
+ page = wiki.write_bliki_entry(web_address, pname, @params["content"], Time.now, @params["author"])
740
+ end
741
+
742
+ write_cookie("author", @params["author"])
743
+ redirect_bliki
744
+ end
745
+
746
+ def bliki_revision
747
+ parse_category
748
+ set_menu_pages
749
+ @page = wiki.read_bliki_entry(web_address, page_name || @params['pagename'])
750
+ if @page.nil?
751
+ render_text "Unable to find bliki entry #{page_name || @params['pagename']}"
752
+ return
753
+ end
754
+ @revision = @page.revisions[@params["rev"].to_i] || @page.revisions.last
755
+ end
756
+
757
+ def rollback_bliki
758
+ # redirect_bliki if web.check_pass_on_edit and not password_check(@params['password'])
759
+
760
+ @page = wiki.read_bliki_entry(web_address, page_name)
761
+ wiki.rollback_bliki_entry(web_address, page_name, @params["rev"].to_i, Time.now)
762
+ redirect_bliki
763
+ end
764
+
765
+ # ----------------------------------------------------------------------------
766
+
767
+ protected
768
+ def before_action
769
+ if in_a_web? && !authorized?(web_address) && !%w( login authenticate published ).include?(action_name)
770
+ redirect_action("login")
771
+ return false
772
+ elsif in_a_web?
773
+ @web, @page_name, @action_name = web, page_name, action_name
774
+ end
775
+ end
776
+
777
+ def action_name
778
+ if in_a_web?
779
+ request_path[1]
780
+ elsif action_methods.include?(request_path[0])
781
+ request_path[0]
782
+ else
783
+ "index"
784
+ end
785
+ end
786
+
787
+ def redirect_show(page = @page.name, web = web_address)
788
+ redirect_path "/#{web}/show/#{CGI.escape(page)}"
789
+ end
790
+
791
+ def redirect_bliki
792
+ redirect_path "/#{web_address}/bliki/"
793
+ end
794
+
795
+ def redirect_action(action, web = web_address)
796
+ redirect_path "/#{web}/#{action}"
797
+ end
798
+
799
+ def link_to_bliki(entry, web = web_address) #{{{
800
+ "<a class='existingWikiWord' href='/#{web}/bliki_revision/#{entry.name}?rev=#{entry.revisions.size-1}'>#{entry.name}</a>"
801
+ end #}}}
802
+
803
+
804
+ private
805
+ def wiki
806
+ WikiService.instance
807
+ end
808
+
809
+ def web
810
+ wiki.webs[web_address]
811
+ end
812
+
813
+ def in_a_web?
814
+ request_path.length > 1
815
+ end
816
+
817
+ def web_address
818
+ request_path[0]
819
+ end
820
+
821
+ def page_name
822
+ CGI.unescape(request_path[2]) if request_path[2]
823
+ end
824
+
825
+ def authorized?(web_address)
826
+ web.nil? ||
827
+ web.password.nil? ||
828
+ (read_cookie(web_address) && read_cookie(web_address) == web.password) ||
829
+ password_check(@params["password"])
830
+ end
831
+
832
+ def default_author
833
+ read_cookie("author") || "AnonymousCoward"
834
+ end
835
+
836
+ def password_check(password)
837
+ web && @params["password"] == web.password && write_cookie(web_address, @params["password"])
838
+ end
839
+
840
+ def export_pages_to_zip_file(zip_file_path)
841
+ Zip::ZipOutputStream.open(zip_file_path) do |zos|
842
+ web.select.by_name.each do |@page|
843
+ zos.put_next_entry(@page.name + ".html")
844
+ zos.puts(template_engine("print").result(binding))
845
+ end
846
+
847
+ zos.put_next_entry "pages-metadata.txt"
848
+ web.select.by_name.each do |page|
849
+ zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}"
850
+ end
851
+
852
+ web.select_bliki.by_name.each do |@page|
853
+ zos.put_next_entry("bliki/#{@page.name}.html")
854
+ zos.puts(template_engine("print").result(binding))
855
+ end
856
+
857
+ zos.put_next_entry "bliki/bliki-metadata.txt"
858
+ web.select_bliki.by_name.each do |page|
859
+ zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}"
860
+ end
861
+
862
+ zos.put_next_entry("index.html")
863
+ zos.puts('<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html>')
864
+ end
865
+ end
866
+
867
+ def export_markup_to_zip_file(zip_file_path)
868
+ Zip::ZipOutputStream.open(zip_file_path) do |zos|
869
+ web.select.by_name.each do |page|
870
+ zos.put_next_entry(page.name + ".#{web.markup}")
871
+ zos.puts(page.content)
872
+ end
873
+
874
+ zos.put_next_entry "pages-metadata.txt"
875
+ web.select.by_name.each do |page|
876
+ zos.puts "#{page.name}: by #{page.author}, created on #{page.pretty_created_at}"
877
+ end
878
+
879
+ web.select_bliki.by_name.each do |page|
880
+ zos.put_next_entry("bliki/#{page.name}.#{web.markup}")
881
+ zos.puts(page.content)
882
+ end
883
+
884
+ zos.put_next_entry "bliki/bliki-metadata.txt"
885
+ web.select_bliki.by_name.each do |page|
886
+ zos.puts "#{page.name}: by #{page.author}, created on #{page.revisions.first.pretty_created_at}"
887
+ end
888
+ end
889
+ end
890
+
891
+ def export_web_to_tex(file_path)
892
+ @web_name = web.name
893
+ @tex_content = table_of_contents(web.pages["HomePage"].content.dup, render_tex_web)
894
+ File.open(file_path, "w") { |f| f.write(template_engine("tex_web").result(binding)) }
895
+ end
896
+
897
+ def render_tex_web
898
+ pages = web.select.by_name.inject({}) do |tex_web, page|
899
+ tex_web[page.name] = RedClothForTex.new(page.content).to_tex
900
+ tex_web
901
+ end
902
+
903
+ bliki_entries = web.select_bliki.by_name.inject({}) do |tex_web, page|
904
+ tex_web["bliki/#{page.name}"] = RedClothForTex.new(page.content).to_tex
905
+ tex_web
906
+ end
907
+
908
+ pages.update bliki_entries
909
+ end
910
+
911
+ def export_page_to_tex(file_path)
912
+ tex
913
+ File.open(file_path, "w") { |f| f.write(template_engine("tex").result(binding)) }
914
+ end
915
+
916
+ def convert_tex_to_pdf(tex_path)
917
+ `cd #{File.dirname(tex_path)}; pdflatex --interaction=scrollmode '#{File.basename(tex_path)}'`
918
+ end
919
+
920
+ def truncate(text, length = 30, truncate_string = "...")
921
+ if text.length > length then text[0..(length - 3)] + truncate_string else text end
922
+ end
923
+
924
+ def render_markup_help
925
+ if web
926
+ markup = web.markup
927
+ markup = 'markdown' if markup.to_s =~ /markdown/
928
+ sub_template("#{markup}_help")
929
+ else
930
+ ''
931
+ end
932
+ end
933
+
934
+ def send_export(file_name, file_path, content_type = "application/zip")
935
+ @res["Content-Type"] = content_type
936
+ @res["Content-Disposition"] = "attachment; filename=#{file_name}"
937
+ @res["Content-Length"] = File.size(file_path)
938
+ File.open(file_path, "rb") { |f| @res.body = f.read }
939
+ end
940
+
941
+ def template_engine(template_name)
942
+ ERB.new(IO.readlines(action_template_path(template_name)).join)
943
+ end
944
+
945
+ def remote_ip
946
+ $stderr << "#{@req.meta_vars['HTTP_X_FORWARDED_FOR']} || #{@req.meta_vars['REMOTE_ADDR']}"
947
+ @req.meta_vars["HTTP_X_FORWARDED_FOR"] || @req.meta_vars["REMOTE_ADDR"]
948
+ end
949
+ end
950
+
943
951
  # jEdit :folding=indent:collapseFolds=2: