Pimki 1.7.092 → 1.8.092

Sign up to get free protection for your applications and to get access to all the features.
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: