radiant-tags-extension 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. data/HELP.md +50 -0
  2. data/HELP_admin.md +30 -0
  3. data/README +38 -0
  4. data/Rakefile +25 -0
  5. data/app/models/meta_tag.rb +53 -0
  6. data/app/models/radius_tags.rb +328 -0
  7. data/app/models/tag_search_page.rb +87 -0
  8. data/app/models/tagging.rb +10 -0
  9. data/app/views/admin/help/_using_tags.html.haml +4 -0
  10. data/app/views/admin/pages/_tag_field.html.haml +8 -0
  11. data/db/migrate/001_add_tag_support.rb +20 -0
  12. data/lib/tagging_methods.rb +128 -0
  13. data/lib/tasks/tags_extension_tasks.rake +32 -0
  14. data/public/stylesheets/tags.css +11 -0
  15. data/tags_extension.rb +57 -0
  16. data/test/fixtures/meta_tags.yml +12 -0
  17. data/test/fixtures/page_parts.yml +17 -0
  18. data/test/fixtures/pages.yml +43 -0
  19. data/test/fixtures/taggings.yml +20 -0
  20. data/test/functional/tag_search_page_test.rb +44 -0
  21. data/test/functional/tags_extension_test.rb +19 -0
  22. data/test/helpers/render_test_helper.rb +60 -0
  23. data/test/test_helper.rb +20 -0
  24. data/test/unit/page_taggability_test.rb +20 -0
  25. data/vendor/plugins/has_many_polymorphs/.gitignore +1 -0
  26. data/vendor/plugins/has_many_polymorphs/CHANGELOG +86 -0
  27. data/vendor/plugins/has_many_polymorphs/LICENSE +184 -0
  28. data/vendor/plugins/has_many_polymorphs/Manifest +173 -0
  29. data/vendor/plugins/has_many_polymorphs/README +205 -0
  30. data/vendor/plugins/has_many_polymorphs/Rakefile +28 -0
  31. data/vendor/plugins/has_many_polymorphs/TODO +2 -0
  32. data/vendor/plugins/has_many_polymorphs/examples/hmph.rb +69 -0
  33. data/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb +97 -0
  34. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb +28 -0
  35. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb +39 -0
  36. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb +15 -0
  37. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb +16 -0
  38. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb +203 -0
  39. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb +85 -0
  40. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml +23 -0
  41. data/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml +7 -0
  42. data/vendor/plugins/has_many_polymorphs/init.rb +2 -0
  43. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb +160 -0
  44. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb +69 -0
  45. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb +60 -0
  46. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb +600 -0
  47. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb +19 -0
  48. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb +103 -0
  49. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb +35 -0
  50. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb +58 -0
  51. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb +88 -0
  52. data/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs.rb +27 -0
  53. data/vendor/plugins/has_many_polymorphs/test/fixtures/bow_wows.yml +10 -0
  54. data/vendor/plugins/has_many_polymorphs/test/fixtures/cats.yml +18 -0
  55. data/vendor/plugins/has_many_polymorphs/test/fixtures/eaters_foodstuffs.yml +0 -0
  56. data/vendor/plugins/has_many_polymorphs/test/fixtures/fish.yml +12 -0
  57. data/vendor/plugins/has_many_polymorphs/test/fixtures/frogs.yml +5 -0
  58. data/vendor/plugins/has_many_polymorphs/test/fixtures/keep_your_enemies_close.yml +0 -0
  59. data/vendor/plugins/has_many_polymorphs/test/fixtures/little_whale_pupils.yml +0 -0
  60. data/vendor/plugins/has_many_polymorphs/test/fixtures/people.yml +7 -0
  61. data/vendor/plugins/has_many_polymorphs/test/fixtures/petfoods.yml +11 -0
  62. data/vendor/plugins/has_many_polymorphs/test/fixtures/whales.yml +5 -0
  63. data/vendor/plugins/has_many_polymorphs/test/fixtures/wild_boars.yml +10 -0
  64. data/vendor/plugins/has_many_polymorphs/test/generator/tagging_generator_test.rb +42 -0
  65. data/vendor/plugins/has_many_polymorphs/test/integration/app/README +182 -0
  66. data/vendor/plugins/has_many_polymorphs/test/integration/app/Rakefile +19 -0
  67. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application.rb +7 -0
  68. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/bones_controller.rb +5 -0
  69. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/addresses_helper.rb +2 -0
  70. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/application_helper.rb +3 -0
  71. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/bones_helper.rb +2 -0
  72. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/sellers_helper.rb +28 -0
  73. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/states_helper.rb +2 -0
  74. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/users_helper.rb +2 -0
  75. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/bone.rb +2 -0
  76. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent.rb +2 -0
  77. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent_relationship.rb +2 -0
  78. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/organic_substance.rb +2 -0
  79. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent.rb +4 -0
  80. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent_relationship.rb +4 -0
  81. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stick.rb +2 -0
  82. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stone.rb +2 -0
  83. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/edit.html.erb +12 -0
  84. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/index.html.erb +18 -0
  85. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/new.html.erb +11 -0
  86. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/show.html.erb +3 -0
  87. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/bones/index.rhtml +5 -0
  88. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/addresses.html.erb +17 -0
  89. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/sellers.html.erb +17 -0
  90. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/states.html.erb +17 -0
  91. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/users.html.erb +17 -0
  92. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/edit.html.erb +12 -0
  93. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/index.html.erb +20 -0
  94. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/new.html.erb +11 -0
  95. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/show.html.erb +3 -0
  96. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/edit.html.erb +12 -0
  97. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/index.html.erb +19 -0
  98. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/new.html.erb +11 -0
  99. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/show.html.erb +3 -0
  100. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/edit.html.erb +12 -0
  101. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/index.html.erb +22 -0
  102. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/new.html.erb +11 -0
  103. data/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/show.html.erb +3 -0
  104. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/boot.rb +110 -0
  105. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/database.yml +17 -0
  106. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb +19 -0
  107. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb.canonical +19 -0
  108. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/development.rb +9 -0
  109. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/production.rb +18 -0
  110. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/test.rb +19 -0
  111. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/locomotive.yml +6 -0
  112. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/routes.rb +33 -0
  113. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/default.base +56 -0
  114. data/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/development.conf.canonical +155 -0
  115. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/001_create_sticks.rb +11 -0
  116. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/002_create_stones.rb +11 -0
  117. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/003_create_organic_substances.rb +11 -0
  118. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/004_create_bones.rb +8 -0
  119. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/005_create_single_sti_parents.rb +11 -0
  120. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/006_create_double_sti_parents.rb +11 -0
  121. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb +13 -0
  122. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb +14 -0
  123. data/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/009_create_library_model.rb +11 -0
  124. data/vendor/plugins/has_many_polymorphs/test/integration/app/doc/README_FOR_APP +2 -0
  125. data/vendor/plugins/has_many_polymorphs/test/integration/app/generators/commenting_generator_test.rb +83 -0
  126. data/vendor/plugins/has_many_polymorphs/test/integration/app/lib/library_model.rb +2 -0
  127. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/.htaccess +40 -0
  128. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/404.html +30 -0
  129. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/500.html +30 -0
  130. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.cgi +10 -0
  131. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.fcgi +24 -0
  132. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.rb +10 -0
  133. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/favicon.ico +0 -0
  134. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/images/rails.png +0 -0
  135. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/index.html +277 -0
  136. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/application.js +2 -0
  137. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/controls.js +833 -0
  138. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/dragdrop.js +942 -0
  139. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/effects.js +1088 -0
  140. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/prototype.js +2515 -0
  141. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/robots.txt +1 -0
  142. data/vendor/plugins/has_many_polymorphs/test/integration/app/public/stylesheets/scaffold.css +74 -0
  143. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/about +3 -0
  144. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/breakpointer +3 -0
  145. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/console +3 -0
  146. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/destroy +3 -0
  147. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/generate +3 -0
  148. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/benchmarker +3 -0
  149. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/profiler +3 -0
  150. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/plugin +3 -0
  151. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/inspector +3 -0
  152. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/reaper +3 -0
  153. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/spawner +3 -0
  154. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/runner +3 -0
  155. data/vendor/plugins/has_many_polymorphs/test/integration/app/script/server +3 -0
  156. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parent_relationships.yml +7 -0
  157. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parents.yml +7 -0
  158. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/organic_substances.yml +5 -0
  159. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parent_relationships.yml +7 -0
  160. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parents.yml +7 -0
  161. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/sticks.yml +7 -0
  162. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/stones.yml +7 -0
  163. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/addresses_controller_test.rb +57 -0
  164. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/bones_controller_test.rb +8 -0
  165. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/sellers_controller_test.rb +57 -0
  166. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/states_controller_test.rb +57 -0
  167. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/users_controller_test.rb +57 -0
  168. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/test_helper.rb +8 -0
  169. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/bone_test.rb +8 -0
  170. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_relationship_test.rb +8 -0
  171. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_test.rb +8 -0
  172. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/organic_substance_test.rb +8 -0
  173. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_relationship_test.rb +8 -0
  174. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_test.rb +8 -0
  175. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stick_test.rb +8 -0
  176. data/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stone_test.rb +8 -0
  177. data/vendor/plugins/has_many_polymorphs/test/integration/server_test.rb +43 -0
  178. data/vendor/plugins/has_many_polymorphs/test/models/aquatic/fish.rb +5 -0
  179. data/vendor/plugins/has_many_polymorphs/test/models/aquatic/pupils_whale.rb +7 -0
  180. data/vendor/plugins/has_many_polymorphs/test/models/aquatic/whale.rb +15 -0
  181. data/vendor/plugins/has_many_polymorphs/test/models/beautiful_fight_relationship.rb +26 -0
  182. data/vendor/plugins/has_many_polymorphs/test/models/canine.rb +9 -0
  183. data/vendor/plugins/has_many_polymorphs/test/models/cat.rb +5 -0
  184. data/vendor/plugins/has_many_polymorphs/test/models/dog.rb +18 -0
  185. data/vendor/plugins/has_many_polymorphs/test/models/eaters_foodstuff.rb +8 -0
  186. data/vendor/plugins/has_many_polymorphs/test/models/frog.rb +4 -0
  187. data/vendor/plugins/has_many_polymorphs/test/models/kitten.rb +3 -0
  188. data/vendor/plugins/has_many_polymorphs/test/models/parentship.rb +4 -0
  189. data/vendor/plugins/has_many_polymorphs/test/models/person.rb +9 -0
  190. data/vendor/plugins/has_many_polymorphs/test/models/petfood.rb +39 -0
  191. data/vendor/plugins/has_many_polymorphs/test/models/tabby.rb +2 -0
  192. data/vendor/plugins/has_many_polymorphs/test/models/wild_boar.rb +3 -0
  193. data/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb +9 -0
  194. data/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb +9 -0
  195. data/vendor/plugins/has_many_polymorphs/test/patches/symlinked_plugins_1.2.6.diff +46 -0
  196. data/vendor/plugins/has_many_polymorphs/test/schema.rb +87 -0
  197. data/vendor/plugins/has_many_polymorphs/test/setup.rb +14 -0
  198. data/vendor/plugins/has_many_polymorphs/test/test_helper.rb +52 -0
  199. data/vendor/plugins/has_many_polymorphs/test/unit/has_many_polymorphs_test.rb +713 -0
  200. metadata +421 -0
data/HELP.md ADDED
@@ -0,0 +1,50 @@
1
+ The Tags extension provides a way for you to easily categorize your pages.
2
+
3
+ == Results page
4
+
5
+ <r:search:empty>
6
+ <h2>I couldn't find anything tagged with "<r:search:query/>".</h2>
7
+ </r:search:empty>
8
+
9
+ <r:search:results>
10
+ <h2>Found the following pages that are tagged with "<em><r:search:query/></em>".</h2>
11
+
12
+ <ul>
13
+ <r:search:results:each>
14
+ <li><r:link/> - <r:author/> - <r:date/></li>
15
+ </r:search:results:each>
16
+ </ul>
17
+ </r:search:results>
18
+
19
+
20
+ == Tag cloud
21
+
22
+ Use `<r:tag_cloud />` anywhere you like.
23
+ I made a stab at building the 'perfect' tag cloud markup, as inspired by a post on 24ways.org; http://24ways.org/2006/marking-up-a-tag-cloud
24
+
25
+ == Tag list
26
+
27
+ Use `<r:tag_list />` to get a list of tags for the current page.
28
+ Also works through children:each.
29
+
30
+ == All tags
31
+
32
+ Use `<r:all_tags />` to get a list of all tags. You may iterate through them with
33
+ `<r:all_tags:each>` and access their associated pages with `<r:all_tags:each:pages:each>`
34
+
35
+ == Collections
36
+
37
+ You can grab a collection of pages with a certain tag like so;
38
+
39
+ <r:tagged with="sometag" [scope="/some/page"] [with_any="true"]>
40
+ <r:link />
41
+ </r:tagged>
42
+
43
+ Which would iterate over all the resulting pages, like you do with children:each.
44
+ When you define scope, only this page and any of it's (grand)children will be used.
45
+ Using scope="current_page" will use the page that is currently being rendered as scope.
46
+ You can also set limit, offset etc like with children:each.
47
+
48
+ Using r:tagged in it's default setting searches for pages that have all of the given tags.
49
+ Using r:tagged with the attribute 'with_any' set to 'true' will find pages that have any of
50
+ the given tags.
data/HELP_admin.md ADDED
@@ -0,0 +1,30 @@
1
+ The Tags extension allows you to configure the style of tagging between 2 options:
2
+ simple or complex strings.
3
+
4
+ == Simple Tagging
5
+
6
+ By default you may add tags to a page in a string such as "this that other". That string
7
+ will be parsed and turned into 3 separate tags.
8
+
9
+ == Complex Tagging
10
+
11
+ You may set `Radiant::Config['tags.complex_strings'] = true` to allow you to enter more
12
+ complex tags for your pages.
13
+
14
+ When this setting is `true` the tags are delimited by a semi-colon, allowing you to enter
15
+ a string of tags such as "My Summer Vacation (2008); Entertainment/Nonsense; Miscellaneous 2.0".
16
+ The result of that string will return 3 tags: `My Summer Vacation (2008)`,
17
+ `Entertainment/Nonsense` and 'Miscellaneous 2.0'
18
+
19
+ == Making the choice
20
+
21
+ You'll need to restart the application server after changing this setting. Please keep in
22
+ mind that any changes to this setting may affect any tags you currently have in the database.
23
+ It is recommended that you choose either Simple or Complex, but that you do not switch
24
+ after creating your tags.
25
+
26
+ == Tag Clouds
27
+
28
+ Some styles are provided in tags.css for the <r:tag_cloud>. To use it, add this to your layout:
29
+
30
+ <link rel="stylesheet" type="text/css" href="/stylesheets/tags.css" />
data/README ADDED
@@ -0,0 +1,38 @@
1
+ = Tags extension
2
+
3
+ Created by: Keith Bingman - keithbingman.com
4
+ Revived by: Benny Degezelle - gorilla-webdesign.be
5
+ New features by: Jim Gay - saturnflyer.com
6
+ Version: 1.5
7
+
8
+ This extension enhances the page model with tagging capabilities, tagging as in "2.0" and tagclouds.
9
+
10
+ == Requirements
11
+
12
+ This extension depends on the has_many_polymorphs plugin (included in vendor/plugins)
13
+ If you're still on Radiant pre-0.6.7, then the shards_extension should be installed and loaded first.
14
+ You can change the load order of extensions in config/environment.rb (see http://wiki.radiantcms.org/Using_the_Shards_Extension).
15
+
16
+ == Installation
17
+
18
+ 1. Copy the extension to your vendor/extensions directory as you would any other extension.
19
+ 2. Run 'rake radiant:extensions:tags:install'
20
+ 3. Make a page to sit in /search/by-tag, and give it the "Tag Search" pagetype.
21
+ If you want to change this location, it's in Radiant::Config['tags.results_page_url'].
22
+
23
+ Done!
24
+ Here's a sample results page to get you started;
25
+
26
+ <r:search:empty>
27
+ <h2>I couldn't find anything tagged with "<r:search:query/>".</h2>
28
+ </r:search:empty>
29
+
30
+ <r:search:results>
31
+ <h2>Found the following pages that are tagged with "<em><r:search:query/></em>".</h2>
32
+
33
+ <ul>
34
+ <r:search:results:each>
35
+ <li><r:link/> - <r:author/> - <r:date/></li>
36
+ </r:search:results:each>
37
+ </ul>
38
+ </r:search:results>
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the tags extension.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the tags extension.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'TagsExtension'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ # Load any custom rakefiles for extension
25
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
@@ -0,0 +1,53 @@
1
+ class MetaTag < ActiveRecord::Base
2
+
3
+ if Radiant::Config['tags.complex_strings'] == 'true'
4
+ delim = ";"
5
+ re_format = /^[a-zA-Z0-9,\_\-\s\/()'.&]+$/
6
+ else
7
+ delim = " "
8
+ re_format = /^[a-zA-Z0-9\_\-]+$/
9
+ end
10
+ DELIMITER = delim
11
+ # how to separate tags in strings (you may
12
+ # also need to change the validates_format_of parameters
13
+ # if you update this)
14
+
15
+ # if speed becomes an issue, you could remove these validations
16
+ # and rescue the AR index errors instead
17
+ validates_presence_of :name
18
+ validates_uniqueness_of :name, :case_sensitive => false
19
+ validates_format_of :name, :with => re_format,
20
+ :message => "can not contain special characters"
21
+
22
+ has_many_polymorphs :taggables,
23
+ :from => [:pages],
24
+ :through => :taggings,
25
+ :dependent => :destroy,
26
+ :skip_duplicates => false
27
+
28
+ def after_save
29
+ # if you allow editable tag names, you might want before_save instead
30
+ self.name = name.downcase.strip.squeeze(" ")
31
+ end
32
+
33
+ class << self
34
+ def find_or_create_by_name!(name)
35
+ find_by_name(name) || create!(:name => name)
36
+ end
37
+
38
+ def cloud(args = {})
39
+ find(:all, :select => 'meta_tags.*, count(*) as popularity',
40
+ :limit => args[:limit] || 5,
41
+ :joins => "JOIN taggings ON taggings.meta_tag_id = meta_tags.id",
42
+ :conditions => args[:conditions],
43
+ :group => "meta_tags.id, meta_tags.name",
44
+ :order => "popularity DESC" )
45
+ end
46
+ end
47
+
48
+ def <=>(other)
49
+ # To be able to sort an array of tags
50
+ name <=> other.name
51
+ end
52
+
53
+ end
@@ -0,0 +1,328 @@
1
+ module RadiusTags
2
+ include Radiant::Taggable
3
+ include ActionView::Helpers::TextHelper
4
+
5
+ class TagError < StandardError; end
6
+
7
+ desc %{
8
+ Expands if a <pre><r:tagged with="" /></pre> call would return items. Takes the same options as the 'tagged' tag.
9
+ The <pre><r:unless_tagged with="" /></pre> is also available.
10
+ }
11
+ tag "if_tagged" do |tag|
12
+ if tag.attr["with"]
13
+ tag.locals.tagged_results = find_with_tag_options(tag)
14
+ tag.expand unless tag.locals.tagged_results.empty?
15
+ else
16
+ tag.expand unless tag.locals.page.tag_list.empty?
17
+ end
18
+ end
19
+ tag "unless_tagged" do |tag|
20
+ if tag.attr["with"]
21
+ tag.expand if find_with_tag_options(tag).empty?
22
+ else
23
+ tag.expand if tag.locals.page.tag_list.empty?
24
+ end
25
+ end
26
+
27
+ desc %{
28
+ Find all pages with certain tags, within in an optional scope. Additionally, you may set with_any to true to select pages that have any of the listed tags (opposed to all listed tags which is the provided default).
29
+
30
+ *Usage:*
31
+ <pre><code><r:tagged with="shoes diesel" [scope="/fashion/cult-update"] [with_any="true"] [offset="number"] [limit="number"] [by="attribute"] [order="asc|desc"]>...</r:tagged></code></pre>
32
+ }
33
+ tag "tagged" do |tag|
34
+ unless tag.locals.tagged_results.nil? # We're inside an r:if_tagged, so results are already available;
35
+ results = tag.locals.tagged_results
36
+ else
37
+ results = find_with_tag_options(tag)
38
+ end
39
+ output = []
40
+ results.each do |page|
41
+ tag.locals.page = page
42
+ output << tag.expand
43
+ end
44
+ output
45
+ end
46
+
47
+ desc %{
48
+ Find all pages related to the current page, based on all or any of the current page's tags. A scope attribute may be given to limit results to a certain site area.
49
+
50
+ *Usage:*
51
+ <pre><code><r:related_by_tags [scope="/fashion/cult-update"] [offset="number"] [limit="number"] [by="attribute"] [order="asc|desc"]>...</r:related_by_tags></code></pre>
52
+ }
53
+ tag "related_by_tags" do |tag|
54
+ tag.attr["with"] = tag.locals.page.tag_list.split(MetaTag::DELIMITER)
55
+ tag.attr["with_any"] = true
56
+ tag.attr["exclude_id"] = tag.locals.page.id
57
+ results = find_with_tag_options(tag)
58
+ return false if results.size < 1
59
+ output = []
60
+ first = true
61
+ results.each do |page|
62
+ tag.locals.page = page
63
+ tag.locals.first = first
64
+ output << tag.expand
65
+ first = false
66
+ end
67
+ output
68
+ end
69
+
70
+ tag "if_has_related_by_tags" do |tag|
71
+ tag.attr["with"] = tag.locals.page.tag_list.split(MetaTag::DELIMITER)
72
+ tag.attr["with_any"] = true
73
+ tag.attr["exclude_id"] = tag.locals.page.id
74
+ results = find_with_tag_options(tag)
75
+ results -= [tag.locals.page]
76
+ tag.expand if results.size > 0
77
+ end
78
+
79
+ tag "related_by_tags:if_first" do |tag|
80
+ tag.expand if tag.locals.first
81
+ end
82
+
83
+ desc %{
84
+ Render a Tag cloud
85
+ The results_page attribute will default to #{Radiant::Config['tags.results_page_url']}
86
+
87
+ *Usage:*
88
+ <pre><code><r:tag_cloud_list [limit="number"] [results_page="/some/url"] [scope="/some/url"]/></code></pre>
89
+ }
90
+ tag "tag_cloud" do |tag|
91
+ tag_cloud = MetaTag.cloud(:limit => tag.attr['limit'] || 5).sort
92
+ tag_cloud = filter_tags_to_url_scope(tag_cloud, tag.attr['scope']) unless tag.attr['scope'].nil?
93
+
94
+ results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
95
+ output = "<ol class=\"tag_cloud\">"
96
+ if tag_cloud.length > 0
97
+ build_tag_cloud(tag_cloud, %w(size1 size2 size3 size4 size5 size6 size7 size8 size9)) do |tag, cloud_class, amount|
98
+ output += "<li class=\"#{cloud_class}\"><span>#{pluralize(amount, 'page is', 'pages are')} tagged with </span><a href=\"#{results_page}/#{tag}\" class=\"tag\">#{tag}</a></li>"
99
+ end
100
+ else
101
+ return "<p>No tags found.</p>"
102
+ end
103
+ output += "</ol>"
104
+ end
105
+
106
+ desc %{
107
+ Render a Tag list, more for 'categories'-ish usage, i.e.: Cats (2) Logs (1) ...
108
+ The results_page attribute will default to #{Radiant::Config['tags.results_page_url']}
109
+
110
+ *Usage:*
111
+ <pre><code><r:tag_cloud_list [results_page="/some/url"] [scope="/some/url"]/></code></pre>
112
+ }
113
+ tag "tag_cloud_list" do |tag|
114
+ tag_cloud = MetaTag.cloud({:limit => 100}).sort
115
+ tag_cloud = filter_tags_to_url_scope(tag_cloud, tag.attr['scope']) unless tag.attr['scope'].nil?
116
+
117
+ results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
118
+ output = "<ul class=\"tag_list\">"
119
+ if tag_cloud.length > 0
120
+ build_tag_cloud(tag_cloud, %w(size1 size2 size3 size4 size5 size6 size7 size8 size9)) do |tag, cloud_class, amount|
121
+ output += "<li class=\"#{cloud_class}\"><a href=\"#{results_page}/#{tag}\" class=\"tag\">#{tag} (#{amount})</a></li>"
122
+ end
123
+ else
124
+ return "<p>No tags found.</p>"
125
+ end
126
+ output += "</ul>"
127
+ end
128
+
129
+ desc "List the current page's tags"
130
+ tag "tag_list" do |tag|
131
+ results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
132
+ output = []
133
+ tag.locals.page.tag_list.split(MetaTag::DELIMITER).each {|t| output << "<a href=\"#{results_page}/#{t}\" class=\"tag\">#{t}</a>"}
134
+ output.join ", "
135
+ end
136
+
137
+ desc "List the current page's tagsi as technorati tags. this should be included in the body of a post or in your rss feed"
138
+ tag "tag_list_technorati" do |tag|
139
+ output = []
140
+ tag.locals.page.tag_list.split(MetaTag::DELIMITER).each {|t| output << "<a href=\"http://technorati.com/tag/#{ t.split(" ").join("+")}\" rel=\"tag\">#{t}</a>"}
141
+ output.join ", "
142
+ end
143
+
144
+ tag "tags" do |tag|
145
+ tag.expand
146
+ end
147
+
148
+ desc "Iterates over the tags of the current page"
149
+ tag "tags:each" do |tag|
150
+ result = []
151
+ tag.locals.page.meta_tags.each do |meta_tag|
152
+ tag.locals.meta_tag = meta_tag
153
+ result << tag.expand
154
+ end
155
+ result
156
+ end
157
+
158
+ tag "tags:each:name" do |tag|
159
+ tag.locals.meta_tag.name
160
+ end
161
+
162
+ tag "tags:each:link" do |tag|
163
+ results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
164
+ name = tag.locals.meta_tag.name
165
+ return "<a href=\"#{results_page}/#{name}\" class=\"tag\">#{name}</a>"
166
+ end
167
+
168
+ desc "Set the scope for all tags in the database"
169
+ tag "all_tags" do |tag|
170
+ tag.expand
171
+ end
172
+
173
+ desc %{
174
+ Iterates through each tag and allows you to specify the order: by popularity or by name.
175
+ The default is by name. You may also limit the search; the default is 5 results.
176
+
177
+ Usage: <pre><code><r:all_tags:each order="popularity" limit="5">...</r:all_tags:each></code></pre>
178
+ }
179
+ tag "all_tags:each" do |tag|
180
+ order = tag.attr['order'] || 'name'
181
+ limit = tag.attr['limit'] || '5'
182
+ result = []
183
+ case order
184
+ when 'name'
185
+ all_tags = MetaTag.find(:all, :limit => limit)
186
+ else
187
+ all_tags = MetaTag.cloud(:limit => limit)
188
+ end
189
+ all_tags.each do |t|
190
+ next if t.pages.empty? # skip unused tags
191
+ tag.locals.meta_tag = t
192
+ result << tag.expand
193
+ end
194
+ result
195
+ end
196
+
197
+ desc "Renders the tag's name"
198
+ tag "all_tags:each:name" do |tag|
199
+ tag.locals.meta_tag.name
200
+ end
201
+
202
+ tag "all_tags:each:link" do |tag|
203
+ results_page = tag.attr['results_page'] || Radiant::Config['tags.results_page_url']
204
+ name = tag.locals.meta_tag.name
205
+ return "<a href=\"#{results_page}/#{name}\" class=\"tag\">#{name}</a>"
206
+ end
207
+
208
+
209
+ desc "Set the scope for the tag's pages"
210
+ tag "all_tags:each:pages" do |tag|
211
+ tag.expand
212
+ end
213
+
214
+ desc "Iterates through each page"
215
+ tag "all_tags:each:pages:each" do |tag|
216
+ result = []
217
+ tag.locals.meta_tag.taggables.each do |taggable|
218
+ if taggable.is_a?(Page)
219
+ tag.locals.page = taggable
220
+ result << tag.expand
221
+ end
222
+ end
223
+ result
224
+ end
225
+
226
+ private
227
+
228
+ def build_tag_cloud(tag_cloud, style_list)
229
+ max, min = 0, 0
230
+ tag_cloud.each do |tag|
231
+ max = tag.popularity.to_i if tag.popularity.to_i > max
232
+ min = tag.popularity.to_i if tag.popularity.to_i < min
233
+ end
234
+
235
+ divisor = ((max - min) / style_list.size) + 1
236
+
237
+ tag_cloud.each do |tag|
238
+ yield tag.name, style_list[(tag.popularity.to_i - min) / divisor], tag.popularity.to_i
239
+ end
240
+ end
241
+
242
+ def tag_item_url(name)
243
+ "#{Radiant::Config['tags.results_page_url']}/#{name}"
244
+ end
245
+
246
+ def find_with_tag_options(tag)
247
+ options = tagged_with_options(tag)
248
+ with_any = tag.attr['with_any'] || false
249
+ scope_attr = tag.attr['scope'] || '/'
250
+ results = []
251
+ raise TagError, "`tagged' tag must contain a `with' attribute." unless (tag.attr['with'] || tag.locals.page.class_name = TagSearchPage)
252
+ ttag = tag.attr['with'] || @request.parameters[:tag]
253
+
254
+ scope = scope_attr == 'current_page' ? Page.find_by_url(@request.request_uri) : Page.find_by_url(scope_attr)
255
+ return "The scope attribute must be a valid url to an existing page." if scope.nil? || scope.class_name.eql?('FileNotFoundPage')
256
+
257
+ if with_any
258
+ Page.tagged_with_any(ttag, options).each do |page|
259
+ next unless (page.ancestors.include?(scope) or page == scope)
260
+ results << page
261
+ end
262
+ else
263
+ Page.tagged_with(ttag, options).each do |page|
264
+ next unless (page.ancestors.include?(scope) or page == scope)
265
+ results << page
266
+ end
267
+ end
268
+ results
269
+ end
270
+
271
+ def tagged_with_options(tag)
272
+ attr = tag.attr.symbolize_keys
273
+
274
+ options = {}
275
+
276
+ [:limit, :offset].each do |symbol|
277
+ if number = attr[symbol]
278
+ if number =~ /^\d{1,4}$/
279
+ options[symbol] = number.to_i
280
+ else
281
+ raise TagError.new("`#{symbol}' attribute of `each' tag must be a positive number between 1 and 4 digits")
282
+ end
283
+ end
284
+ end
285
+
286
+ by = (attr[:by] || 'published_at').strip
287
+ order = (attr[:order] || 'asc').strip
288
+ order_string = ''
289
+ if self.attributes.keys.include?(by)
290
+ order_string << by
291
+ else
292
+ raise TagError.new("`by' attribute of `each' tag must be set to a valid field name")
293
+ end
294
+ if order =~ /^(asc|desc)$/i
295
+ order_string << " #{$1.upcase}"
296
+ else
297
+ raise TagError.new(%{`order' attribute of `each' tag must be set to either "asc" or "desc"})
298
+ end
299
+ options[:order] = order_string
300
+
301
+ status = (attr[:status] || 'published').downcase
302
+ exclude = attr[:exclude_id] ? "AND pages.id != #{attr[:exclude_id]}" : ""
303
+
304
+ unless status == 'all'
305
+ stat = Status[status]
306
+ unless stat.nil?
307
+ options[:conditions] = ["(virtual = ?) and (status_id = ?) #{exclude} and (published_at <= ?)", false, stat.id, Time.current]
308
+ else
309
+ raise TagError.new(%{`status' attribute of `each' tag must be set to a valid status})
310
+ end
311
+ else
312
+ options[:conditions] = ["virtual = ? #{exclude}", false]
313
+ end
314
+ options
315
+ end
316
+
317
+ def filter_tags_to_url_scope(tags, scope)
318
+ new_tags = []
319
+ tags.each do |t|
320
+ catch :record_found do # using fancy ballsports stuff to avoid unnecessary db calls (by calling each page, ànd by calling page.url)
321
+ t.pages.each do |p|
322
+ (new_tags << t; throw :record_found) if p.url.include?(scope)
323
+ end
324
+ end
325
+ end
326
+ new_tags
327
+ end
328
+ end
@@ -0,0 +1,87 @@
1
+ class TagSearchPage < Page
2
+
3
+ attr_accessor :requested_tag
4
+ #### Tags ####
5
+ desc %{ The namespace for all search tags.}
6
+ tag 'search' do |tag|
7
+ tag.expand
8
+ end
9
+
10
+ desc %{ Renders the passed query.}
11
+ tag 'search:query' do |tag|
12
+ CGI.escapeHTML(requested_tag)
13
+ end
14
+
15
+ desc %{ Renders the contained block if no results were returned.}
16
+ tag 'search:empty' do |tag|
17
+ if found_tags.blank?
18
+ tag.expand
19
+ end
20
+ end
21
+
22
+ desc %{ Renders the contained block if results were returned.}
23
+ tag 'search:results' do |tag|
24
+ unless found_tags.blank?
25
+ tag.expand
26
+ end
27
+ end
28
+
29
+ desc %{ <r:search:results:each [sort_by="id"] [order="asc"]/>
30
+ Renders the contained block for each result page. The context
31
+ inside the tag refers to the found page. The optional sort_by and order attributes
32
+ specify how the results are sorted}
33
+ tag 'search:results:each' do |tag|
34
+ # Ordering in Ruby because we already fetched our resultset before
35
+ tags = found_tags
36
+ tags = tags.sort_by(&tag.attr['sort_by'].to_sym) if tag.attr['sort_by']
37
+ tags = tags.reverse if tag.attr['order'].to_s =~ /desc/i
38
+
39
+ returning String.new do |content|
40
+ tags.each do |page|
41
+ tag.locals.page = page
42
+ content << tag.expand
43
+ end
44
+ end
45
+ end
46
+
47
+ desc %{ <r:truncate_and_strip [length="100"] />
48
+ Truncates and strips all HTML tags from the content of the contained block.
49
+ Useful for displaying a snippet of a found page. The optional `length' attribute
50
+ specifies how many characters to truncate to.}
51
+ tag 'truncate_and_strip' do |tag|
52
+ tag.attr['length'] ||= 100
53
+ length = tag.attr['length'].to_i
54
+ helper = ActionView::Base.new
55
+ helper.truncate(helper.strip_tags(tag.expand).gsub(/\s+/," "), length)
56
+ end
57
+
58
+ #### "Behavior" methods ####
59
+ def cache?
60
+ true
61
+ end
62
+
63
+ def found_tags
64
+ return @found_tags if @found_tags
65
+ return [] if requested_tag.blank?
66
+
67
+ @found_tags = Page.tagged_with(requested_tag).delete_if { |p| !p.published? }
68
+ end
69
+
70
+ def render
71
+ self.requested_tag = @request.parameters[:tag] unless requested_tag
72
+ self.title = "Tagged with #{requested_tag}" if requested_tag
73
+
74
+ super
75
+ end
76
+
77
+ def find_by_url(url, live = true, clean = false)
78
+ url = clean_url(url).chop # chop off trailing slash added by clean_url
79
+ if url =~ /^#{self.url}([a-zA-Z0-9,\_\-\s\/()'.&]*)\/?$/
80
+ self.requested_tag = $1
81
+ self
82
+ else
83
+ super
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,10 @@
1
+ class Tagging < ActiveRecord::Base
2
+ belongs_to :meta_tag
3
+ belongs_to :taggable, :polymorphic => true
4
+
5
+ def before_destroy
6
+ # if all the taggings for a particular <%= parent_association_name -%> are deleted, we want to
7
+ # delete the <%= parent_association_name -%> too
8
+ meta_tag.destroy_without_callbacks if meta_tag.taggings.count < 2
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ %h4 Tags
2
+ %p Tags that you list on your pages are ad-hoc categories and tags that you apply to your pages are as flexible as you want them to be. Rather than selecting a category for your page from a pre-defined list of categories, you can create whatever you need.
3
+ %p Tags are delimited (separated) by a space, so if you want to tag a page with Totally Awesome as one tag, be sure to put it in quotes such as 'Totally Awesome', otherwise you'll create two tags: one called Totally and one called Awesome.
4
+ %p These tags may be used by the site developers to provide a list of links (for example) to a group of pages with the same tag.
@@ -0,0 +1,8 @@
1
+ %tr
2
+ %th.label
3
+ %label{:for => "page_meta_tags"}
4
+ Tags
5
+ %td.field
6
+ = f.text_field :meta_tags, :value => @page.tag_list, :class => 'textbox'
7
+ - unless model.errors.on_base.nil?
8
+ %span.error= model.errors.on_base
@@ -0,0 +1,20 @@
1
+ class AddTagSupport < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :meta_tags do |t|
4
+ t.column :name, :string, :null => false
5
+ end
6
+ add_index :meta_tags, :name, :unique => true
7
+
8
+ create_table :taggings do |t|
9
+ t.column :meta_tag_id, :integer, :null => false
10
+ t.column :taggable_id, :integer, :null => false
11
+ t.column :taggable_type, :string, :null => false
12
+ end
13
+ add_index :taggings, [:meta_tag_id, :taggable_id, :taggable_type], :unique => true
14
+ end
15
+
16
+ def self.down
17
+ drop_table :meta_tags
18
+ drop_table :taggings
19
+ end
20
+ end