radiant-tags-extension 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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