instiki 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/CHANGELOG +165 -0
  2. data/README +68 -172
  3. data/app/controllers/admin_controller.rb +94 -0
  4. data/app/controllers/application.rb +131 -0
  5. data/app/controllers/file_controller.rb +129 -0
  6. data/app/controllers/wiki_controller.rb +354 -0
  7. data/{libraries/view_helper.rb → app/helpers/application_helper.rb} +68 -33
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -31
  10. data/app/models/chunks/chunk.rb +86 -20
  11. data/app/models/chunks/engines.rb +54 -38
  12. data/app/models/chunks/include.rb +41 -29
  13. data/app/models/chunks/literal.rb +31 -19
  14. data/app/models/chunks/nowiki.rb +28 -31
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -97
  17. data/app/models/chunks/wiki.rb +141 -82
  18. data/app/models/file_yard.rb +58 -0
  19. data/app/models/page.rb +112 -86
  20. data/app/models/page_lock.rb +22 -23
  21. data/app/models/page_set.rb +89 -64
  22. data/app/models/revision.rb +123 -90
  23. data/app/models/web.rb +176 -89
  24. data/app/models/wiki_content.rb +207 -105
  25. data/app/models/wiki_service.rb +233 -83
  26. data/app/models/wiki_words.rb +23 -25
  27. data/app/views/{wiki/new_system.rhtml → admin/create_system.rhtml} +83 -78
  28. data/app/views/{wiki/new_web.rhtml → admin/create_web.rhtml} +69 -64
  29. data/app/views/admin/edit_web.rhtml +136 -0
  30. data/app/views/file/file.rhtml +19 -0
  31. data/app/views/file/import.rhtml +23 -0
  32. data/app/views/layouts/default.rhtml +85 -0
  33. data/app/views/markdown_help.rhtml +12 -16
  34. data/app/views/mixed_help.rhtml +7 -0
  35. data/app/views/navigation.rhtml +30 -19
  36. data/app/views/rdoc_help.rhtml +12 -16
  37. data/app/views/textile_help.rhtml +24 -28
  38. data/app/views/wiki/authors.rhtml +11 -13
  39. data/app/views/wiki/edit.rhtml +39 -31
  40. data/app/views/wiki/export.rhtml +12 -14
  41. data/app/views/wiki/feeds.rhtml +14 -10
  42. data/app/views/wiki/list.rhtml +64 -57
  43. data/app/views/wiki/locked.rhtml +23 -14
  44. data/app/views/wiki/login.rhtml +14 -11
  45. data/app/views/wiki/new.rhtml +31 -27
  46. data/app/views/wiki/page.rhtml +115 -81
  47. data/app/views/wiki/print.rhtml +14 -16
  48. data/app/views/wiki/published.rhtml +9 -10
  49. data/app/views/wiki/recently_revised.rhtml +27 -30
  50. data/app/views/wiki/revision.rhtml +103 -81
  51. data/app/views/wiki/rollback.rhtml +14 -9
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -15
  54. data/app/views/wiki/tex.rhtml +22 -22
  55. data/app/views/wiki/tex_web.rhtml +34 -34
  56. data/app/views/wiki/web_list.rhtml +18 -13
  57. data/app/views/wiki_words_help.rhtml +9 -8
  58. data/config/environment.rb +82 -0
  59. data/config/environments/development.rb +5 -0
  60. data/config/environments/production.rb +4 -0
  61. data/config/environments/test.rb +17 -0
  62. data/config/routes.rb +18 -0
  63. data/instiki +6 -67
  64. data/instiki.rb +3 -0
  65. data/lib/active_record_stub.rb +31 -0
  66. data/{libraries/diff → lib}/diff.rb +444 -475
  67. data/lib/instiki_errors.rb +15 -0
  68. data/{libraries → lib}/rdocsupport.rb +151 -155
  69. data/lib/redcloth_for_tex.rb +736 -0
  70. data/natives/osx/desktop_launcher/AppDelegate.h +18 -0
  71. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -0
  72. data/natives/osx/desktop_launcher/Credits.html +16 -0
  73. data/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings +0 -0
  74. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +13 -0
  75. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -0
  76. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib +0 -0
  77. data/natives/osx/desktop_launcher/Info.plist +13 -0
  78. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -0
  79. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -0
  80. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -0
  81. data/natives/osx/desktop_launcher/main.mm +14 -0
  82. data/natives/osx/desktop_launcher/version.plist +16 -0
  83. data/public/404.html +6 -0
  84. data/public/500.html +6 -0
  85. data/public/dispatch.rb +10 -0
  86. data/public/favicon.ico +0 -0
  87. data/public/javascripts/edit_web.js +52 -0
  88. data/public/javascripts/prototype.js +336 -0
  89. data/{app/views/static_style_sheet.rhtml → public/stylesheets/instiki.css} +221 -198
  90. data/script/breakpointer +4 -0
  91. data/script/server +93 -0
  92. metadata +59 -32
  93. data/app/controllers/wiki.rb +0 -389
  94. data/app/models/chunks/match.rb +0 -19
  95. data/app/views/bottom.rhtml +0 -4
  96. data/app/views/top.rhtml +0 -49
  97. data/app/views/wiki/edit_web.rhtml +0 -138
  98. data/libraries/action_controller_servlet.rb +0 -177
  99. data/libraries/erb.rb +0 -490
  100. data/libraries/madeleine_service.rb +0 -68
  101. data/libraries/redcloth_for_tex.rb +0 -869
  102. data/libraries/web_controller_server.rb +0 -81
@@ -1,10 +1,9 @@
1
- <%
2
- @title = @page.plain_name
3
- @hide_navigation = true
4
- @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
5
- @inline_style = true
6
- %><%= sub_template "top" %>
7
-
8
- <%= @page.display_published %>
9
-
10
- <%= sub_template "bottom" %>
1
+ <%
2
+ @title = @page.plain_name
3
+ @hide_navigation = false
4
+ @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
5
+ @inline_style = true
6
+ @show_footer = true
7
+ %>
8
+
9
+ <%= @page.display_published %>
@@ -1,30 +1,27 @@
1
- <% @title = "Recently Revised" %><%= sub_template "top" %>
2
-
3
- <% unless @categories.empty? %>
4
- <div id="categories">
5
- <strong>Categories</strong>:
6
- [<a href=".">Any</a>]
7
- <%= @category_links.join(', ') %>
8
- </div>
9
- <% end %>
10
-
11
- <% revision_date = Date.new(2100) %>
12
- <ul>
13
- <% for page in @pages_by_revision %>
14
- <% if page.revised_on < revision_date %>
15
- </ul><b><%= page.pretty_revised_on %></b><ul>
16
- <% end %>
17
-
18
- <li>
19
- <a href="../show/<%= page.name %>"><%= page.plain_name %></a>
20
- <div class="byline" style="margin-bottom: 0px">
21
- by <%= page.author_link %>
22
- at <%= page.created_at.strftime "%H:%M" %>
23
- <%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
24
- </div>
25
- </li>
26
-
27
- <% revision_date = page.revised_on %>
28
- <% end %>
29
-
30
- <%= sub_template "bottom" %>
1
+ <% @title = "Recently Revised" %>
2
+
3
+ <%= categories_menu %>
4
+
5
+ <% unless @pages_by_revision.empty? %>
6
+ <% revision_date = @pages_by_revision.first.revised_on %>
7
+ <h3><%= revision_date.strftime('%B %e, %Y') %></h3>
8
+ <ul>
9
+ <% for page in @pages_by_revision %>
10
+ <% if page.revised_on < revision_date %>
11
+ <% revision_date = page.revised_on %>
12
+ </ul>
13
+ <h3><%= revision_date.strftime('%B %e, %Y') %></h3>
14
+ <ul>
15
+ <% end %>
16
+ <li>
17
+ <%= link_to_existing_page page %>
18
+ <div class="byline" style="margin-bottom: 0px">
19
+ by <%= link_to_page(page.author) %>
20
+ at <%= page.created_at.strftime "%H:%M" %>
21
+ <%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
22
+ </div>
23
+ </li>
24
+ <% end %>
25
+ </ul>
26
+ <% end %>
27
+
@@ -1,81 +1,103 @@
1
- <% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %><%= sub_template "top" %>
2
-
3
- <div id="revision">
4
- <%= @revision.display_content %>
5
- </div>
6
-
7
- <div id="changes" style="display: none">
8
- <p style="background: #eee; padding: 3px; border: 1px solid silver">
9
- <small>
10
- Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
11
- <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
12
- </small>
13
- </p>
14
-
15
- <%= @revision.display_diff %>
16
- </div>
17
-
18
-
19
- <div class="byline">
20
- <%= "Revision from #{@revision.pretty_created_at} by" %>
21
- <%= @page.web.make_link(@revision.author) %>
22
- </div>
23
-
24
- <div class="navigation">
25
-
26
- <% if @revision.next_revision %>
27
- <% if @revision.next_revision.number < (@page.revisions.length - 1) %>
28
- <a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
29
- <% else %>
30
- <a href="../show/<%= @page.name %>" class="navlink">
31
- <% end %>
32
- Forward in time</a>
33
- (<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
34
- <% end %>
35
-
36
- <% if @revision.next_revision && @revision.previous_revision %>
37
- |
38
- <% end %>
39
-
40
- <% if @revision.previous_revision %>
41
- <a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
42
- (<%= @revision.previous_revision.number + 1 %> more)
43
- <% end %>
44
-
45
- | <a href="../show/<%= @page.name %>" class="navlink">See current</a>
46
-
47
- <% if @revision.previous_revision %>
48
- <span id="show_changes">
49
- | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
50
- </span>
51
- <span id="hide_changes" style="display: none">
52
- | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
53
- </span>
54
- <% end %>
55
-
56
- | <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
57
-
58
- <% if @page.references.length > 0 %>
59
- <small>
60
- | Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
61
- </small>
62
- <% end %>
63
- </div>
64
-
65
- <script language="Javascript">
66
- function toggleChanges() {
67
- if (document.getElementById("changes").style.display == "none") {
68
- document.getElementById("changes").style.display = "block";
69
- document.getElementById("revision").style.display = "none";
70
- document.getElementById("show_changes").style.display = "none";
71
- document.getElementById("hide_changes").style.display = "inline";
72
- } else {
73
- document.getElementById("changes").style.display = "none";
74
- document.getElementById("revision").style.display = "block";
75
- document.getElementById("show_changes").style.display = "inline";
76
- document.getElementById("hide_changes").style.display = "none";
77
- }
78
- }
79
- </script>
80
-
81
- <%= sub_template "bottom" %>
1
+ <% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %>
2
+
3
+ <div id="revision">
4
+ <%= @revision.display_content %>
5
+ </div>
6
+
7
+ <div id="changes" style="display: none">
8
+ <p style="background: #eee; padding: 3px; border: 1px solid silver">
9
+ <small>
10
+ Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
11
+ <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
12
+ </small>
13
+ </p>
14
+
15
+ <%= @revision.display_diff %>
16
+ </div>
17
+
18
+
19
+ <div class="byline">
20
+ <%= "Revision from #{@revision.pretty_created_at} by" %>
21
+ <%= link_to_page @revision.author %>
22
+ </div>
23
+
24
+ <div class="navigation">
25
+
26
+ <% if @revision.next_revision %>
27
+ <% if @revision.next_revision.number < (@page.revisions.length - 1) %>
28
+ <%= link_to('Forward in time',
29
+ {:web => @web.address, :action => 'revision', :id => @page.name,
30
+ :rev => @revision.next_revision.number},
31
+ {:class => 'navlink'})
32
+ %>
33
+ <% else %>
34
+ <%= link_to('Forward in time',
35
+ {:web => @web.address, :action => 'show', :id => @page.name},
36
+ {:class => 'navlink'})
37
+ %>
38
+ <% end %>
39
+ (<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
40
+ <% end %>
41
+
42
+ <% if @revision.next_revision && @revision.previous_revision %>
43
+ |
44
+ <% end %>
45
+
46
+ <% if @revision.previous_revision %>
47
+ <%= link_to('Back in time',
48
+ {:web => @web.address, :action => 'revision', :id => @page.name,
49
+ :rev => @revision.previous_revision.number},
50
+ {:class => 'navlink'})
51
+ %>
52
+ (<%= @revision.previous_revision.number + 1 %> more)
53
+ <% end %>
54
+
55
+ |
56
+ <%= link_to('See current', {:web => @web.address, :action => 'show', :id => @page.name},
57
+ {:class => 'navlink'})
58
+ %>
59
+
60
+ <% if @revision.previous_revision %>
61
+ <span id="show_changes">
62
+ | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
63
+ </span>
64
+ <span id="hide_changes" style="display: none">
65
+ | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
66
+ </span>
67
+ <% end %>
68
+
69
+ |
70
+
71
+ <%= link_to('Rollback',
72
+ {:web => @web.address, :action => 'rollback', :id => @page.name, :rev => @revision.number},
73
+ {:class => 'navlink'})
74
+ %>
75
+
76
+ <% if @page.references.length > 0 %>
77
+ <small>
78
+ | Linked from:
79
+ <%= @page.references.collect { |ref|
80
+ link_to ref.name, :web => @web.address, :action => 'show', :id => ref.name
81
+ }.join(", ")
82
+ %>
83
+ </small>
84
+ <% else %>
85
+ Orphan page
86
+ <% end %>
87
+ </div>
88
+
89
+ <script language="Javascript">
90
+ function toggleChanges() {
91
+ if (document.getElementById("changes").style.display == "none") {
92
+ document.getElementById("changes").style.display = "block";
93
+ document.getElementById("revision").style.display = "none";
94
+ document.getElementById("show_changes").style.display = "none";
95
+ document.getElementById("hide_changes").style.display = "inline";
96
+ } else {
97
+ document.getElementById("changes").style.display = "none";
98
+ document.getElementById("revision").style.display = "block";
99
+ document.getElementById("show_changes").style.display = "inline";
100
+ document.getElementById("hide_changes").style.display = "none";
101
+ }
102
+ }
103
+ </script>
@@ -2,23 +2,30 @@
2
2
  @title = "Rollback to #{@page.plain_name} Rev ##{@revision.number}"
3
3
  @content_width = 720
4
4
  @hide_navigation = true
5
- %><%= sub_template "top" %>
5
+ %>
6
6
 
7
7
  <%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{@params["msg"]}</small></p>" if @params["msg"] %>
8
8
 
9
- <form id="editForm" action="../save/<%= @page.name %>" method="post" onSubmit="cleanAuthorName();">
9
+ <%= form_tag({:web => @web.address, :action => 'save', :id => @page.name},
10
+ {:id => 'editForm', :method => 'post', :onSubmit => 'cleanAuthorName();'})
11
+ %>
10
12
  <p>
11
13
  <textarea name="content" style="font-size: 12px; width: 450px; height: 500px"><%= @revision.content %></textarea>
12
14
  </p>
13
15
  <p>
14
- <input type="submit" value="Update"> as
16
+ <input type="submit" value="Update" accesskey="u" /> as
15
17
  <input type="text" name="author" id="authorName" value="<%= @author %>"
16
- onClick="this.value == 'AnonymousCoward' ? this.value = '' : true">
17
- | <a href="../cancel_edit/<%= @page.name %>">Cancel</a> <small>(unlocks page)</small>
18
+ onClick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
19
+ |
20
+ <% link_to('Cancel',
21
+ {:web => @web.address, :action => 'cancel_edit', :id => @page.name},
22
+ {:accesskey => 'c'})
23
+ %>
24
+ <small>(unlocks page)</small>
18
25
  </p>
19
- </form>
26
+ <%= end_form_tag %>
20
27
 
21
- <%= render_markup_help %>
28
+ <%= render("#{@web.markup}_help") if @web %>
22
29
 
23
30
  <script language="JavaScript1.2">
24
31
  function cleanAuthorName() {
@@ -27,5 +34,3 @@ function cleanAuthorName() {
27
34
  }
28
35
  }
29
36
  </script>
30
-
31
- <%= sub_template "bottom" %>
@@ -1,22 +1,22 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
3
- <channel>
4
- <title><%= @web.name %></title>
5
- <link><%= "#{@web_url}/show/HomePage" %></link>
6
- <description>An Instiki wiki</description>
7
- <language>en-us</language>
8
- <ttl>40</ttl>
9
- <% for page in @pages_by_revision %>
10
- <item>
11
- <title><%= page.plain_name %></title>
12
- <% unless @hide_description %>
13
- <description><%= CGI.escapeHTML(page.display_content) %></description>
14
- <% end %>
15
- <pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
16
- <guid><%= "#{@web_url}/show/#{page.name}" %></guid>
17
- <link><%= "#{@web_url}/show/#{page.name}" %></link>
18
- <dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
19
- </item>
20
- <% end %>
21
- </channel>
22
- </rss>
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
3
+ <channel>
4
+ <title><%= @web.name %></title>
5
+ <link><%= url_for :only_path => false, :web => @web_name, :action => @link_action, :id => 'HomePage' %></link>
6
+ <description>An Instiki wiki</description>
7
+ <language>en-us</language>
8
+ <ttl>40</ttl>
9
+ <% for page in @pages_by_revision %>
10
+ <item>
11
+ <title><%= page.plain_name %></title>
12
+ <% unless @hide_description %>
13
+ <description><%= h page.display_content %></description>
14
+ <% end %>
15
+ <pubDate><%= page.created_at.strftime "%a, %e %b %Y %H:%M:%S %Z" %></pubDate>
16
+ <guid><%= url_for :only_path => false, :web => @web_name, :action => @link_action, :id => page.name %></guid>
17
+ <link><%= url_for :only_path => false, :web => @web_name, :action => @link_action, :id => page.name %></link>
18
+ <dc:creator><%= WikiWords.separate(page.author) %></dc:creator>
19
+ </item>
20
+ <% end %>
21
+ </channel>
22
+ </rss>
@@ -1,15 +1,38 @@
1
- <% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %><%= sub_template "top" %>
2
-
3
- <% if @results.length > 0 %>
4
- <ul>
5
- <% for page in @results %>
6
- <li><a href="../show/<%= page.name %>"><%= page.plain_name %></a></li>
7
- <% end %>
8
- </ul>
9
- <% else %>
10
- <p>Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation&mdash;only as a sentence fragment.</p>
11
-
12
- <p>If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"</p>
13
- <% end %>
14
-
15
- <%= sub_template "bottom" %>
1
+ <% @title = "Search results for \"#{@params["query"]}\"" %>
2
+
3
+ <% unless @title_results.empty? %>
4
+ <h2><%= @title_results.length %> page(s) containing search string in the page name:</h2>
5
+ <ul>
6
+ <% for page in @title_results %>
7
+ <li>
8
+ <%= link_to page.plain_name, :web => @web.address, :action => 'show', :id => page.name %>
9
+ </li>
10
+ <% end %>
11
+ </ul>
12
+ <% end %>
13
+
14
+
15
+ <% unless @results.empty? %>
16
+ <h2> <%= @results.length %> page(s) containing search string in the page text:</h2>
17
+ <ul>
18
+ <% for page in @results %>
19
+ <li>
20
+ <%= link_to page.plain_name, :web => @web.address, :action => 'show', :id => page.name %>
21
+ </li>
22
+ <% end %>
23
+ </ul>
24
+ <% end %>
25
+
26
+ <% if (@results + @title_results).empty? %>
27
+ <h2>No pages contains "<%= @params["query"] %>" </h2>
28
+ <p>
29
+ Perhaps you should try expanding your query. Remember that Instiki searches for entire
30
+ phrases, so if you search for "all that jazz" it will not match pages that contain these
31
+ words in separation&mdash;only as a sentence fragment.
32
+ </p>
33
+ <p>
34
+ If you're a high-tech computer wizard, you might even want try constructing a Ruby regular
35
+ expression. That's actually what Instiki uses, so go right ahead and flex your
36
+ "[a-z]*Leet?RegExpSkill(s|z)"
37
+ </p>
38
+ <% end %>
@@ -1,23 +1,23 @@
1
- \documentclass[12pt,titlepage]{article}
2
-
3
- \usepackage[danish]{babel} %danske tekster
4
- \usepackage[OT1]{fontenc} %rigtige danske bogstaver...
5
- \usepackage{a4}
6
- \usepackage{graphicx}
7
- \usepackage{ucs}
8
- \usepackage[utf8]{inputenc}
9
- \input epsf
10
-
11
- %-------------------------------------------------------------------
12
-
13
- \begin{document}
14
-
15
- \sloppy
16
-
17
- %-------------------------------------------------------------------
18
-
19
- \section*{<%= @page.name %>}
20
-
21
- <%= @tex_content %>
22
-
1
+ \documentclass[12pt,titlepage]{article}
2
+
3
+ \usepackage[danish]{babel} %danske tekster
4
+ \usepackage[OT1]{fontenc} %rigtige danske bogstaver...
5
+ \usepackage{a4}
6
+ \usepackage{graphicx}
7
+ \usepackage{ucs}
8
+ \usepackage[utf8x]{inputenc}
9
+ \input epsf
10
+
11
+ %-------------------------------------------------------------------
12
+
13
+ \begin{document}
14
+
15
+ \sloppy
16
+
17
+ %-------------------------------------------------------------------
18
+
19
+ \section*{<%= @page.name %>}
20
+
21
+ <%= @tex_content %>
22
+
23
23
  \end{document}