formol 0.0.4

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 (226) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +147 -0
  3. data/Rakefile +29 -0
  4. data/app/assets/images/formol/read.png +0 -0
  5. data/app/assets/images/formol/unread.png +0 -0
  6. data/app/assets/javascripts/formol/admin/categories.js.coffee +55 -0
  7. data/app/assets/javascripts/formol/admin/forums.js +2 -0
  8. data/app/assets/javascripts/formol/application.js +9 -0
  9. data/app/assets/javascripts/formol/categories.js +2 -0
  10. data/app/assets/javascripts/formol/forums.js +2 -0
  11. data/app/assets/javascripts/formol/poll_votes.js +2 -0
  12. data/app/assets/javascripts/formol/posts.js.coffee +9 -0
  13. data/app/assets/javascripts/formol/preferences.js +2 -0
  14. data/app/assets/javascripts/formol/subscriptions.js.coffee +13 -0
  15. data/app/assets/javascripts/formol/topics.js.coffee +54 -0
  16. data/app/assets/javascripts/lib/jquery.prevent_double_submit.js.coffee +11 -0
  17. data/app/assets/javascripts/markitup/jquery.markitup.js +593 -0
  18. data/app/assets/javascripts/markitup/sets/markdown/set.js +53 -0
  19. data/app/assets/stylesheets/form_struct.css.scss +39 -0
  20. data/app/assets/stylesheets/formol/admin/categories.css.scss +30 -0
  21. data/app/assets/stylesheets/formol/admin/forums.css +4 -0
  22. data/app/assets/stylesheets/formol/categories.css.scss +3 -0
  23. data/app/assets/stylesheets/formol/development.css +4 -0
  24. data/app/assets/stylesheets/formol/forums.css.scss +3 -0
  25. data/app/assets/stylesheets/formol/mixins/after_clear.css.scss +7 -0
  26. data/app/assets/stylesheets/formol/partials/breadcrumb.css.scss +9 -0
  27. data/app/assets/stylesheets/formol/partials/categories/list.css.scss +101 -0
  28. data/app/assets/stylesheets/formol/partials/formol_lists.css.scss +6 -0
  29. data/app/assets/stylesheets/formol/partials/forums/list.css.scss +97 -0
  30. data/app/assets/stylesheets/formol/partials/polls/poll.css.scss +9 -0
  31. data/app/assets/stylesheets/formol/partials/posts/post.css.scss +116 -0
  32. data/app/assets/stylesheets/formol/posts.css.scss +8 -0
  33. data/app/assets/stylesheets/formol/topics.css.scss +42 -0
  34. data/app/assets/stylesheets/markitup/sets/markdown/images/block_code.png +0 -0
  35. data/app/assets/stylesheets/markitup/sets/markdown/images/bold.png +0 -0
  36. data/app/assets/stylesheets/markitup/sets/markdown/images/code.png +0 -0
  37. data/app/assets/stylesheets/markitup/sets/markdown/images/h1.png +0 -0
  38. data/app/assets/stylesheets/markitup/sets/markdown/images/h2.png +0 -0
  39. data/app/assets/stylesheets/markitup/sets/markdown/images/h3.png +0 -0
  40. data/app/assets/stylesheets/markitup/sets/markdown/images/h4.png +0 -0
  41. data/app/assets/stylesheets/markitup/sets/markdown/images/h5.png +0 -0
  42. data/app/assets/stylesheets/markitup/sets/markdown/images/h6.png +0 -0
  43. data/app/assets/stylesheets/markitup/sets/markdown/images/italic.png +0 -0
  44. data/app/assets/stylesheets/markitup/sets/markdown/images/link.png +0 -0
  45. data/app/assets/stylesheets/markitup/sets/markdown/images/list-bullet.png +0 -0
  46. data/app/assets/stylesheets/markitup/sets/markdown/images/list-numeric.png +0 -0
  47. data/app/assets/stylesheets/markitup/sets/markdown/images/picture.png +0 -0
  48. data/app/assets/stylesheets/markitup/sets/markdown/images/preview.png +0 -0
  49. data/app/assets/stylesheets/markitup/sets/markdown/images/quotes.png +0 -0
  50. data/app/assets/stylesheets/markitup/sets/markdown/style.css +57 -0
  51. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-container.png +0 -0
  52. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-bbcode.png +0 -0
  53. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-dotclear.png +0 -0
  54. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-html.png +0 -0
  55. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-json.png +0 -0
  56. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-markdown.png +0 -0
  57. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-textile.png +0 -0
  58. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-wiki.png +0 -0
  59. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor-xml.png +0 -0
  60. data/app/assets/stylesheets/markitup/skins/markitup/images/bg-editor.png +0 -0
  61. data/app/assets/stylesheets/markitup/skins/markitup/images/handle.png +0 -0
  62. data/app/assets/stylesheets/markitup/skins/markitup/images/menu.png +0 -0
  63. data/app/assets/stylesheets/markitup/skins/markitup/images/submenu.png +0 -0
  64. data/app/assets/stylesheets/markitup/skins/markitup/style.css +147 -0
  65. data/app/assets/stylesheets/markitup/skins/simple/images/handle.png +0 -0
  66. data/app/assets/stylesheets/markitup/skins/simple/images/menu.png +0 -0
  67. data/app/assets/stylesheets/markitup/skins/simple/images/submenu.png +0 -0
  68. data/app/assets/stylesheets/markitup/skins/simple/style.css +118 -0
  69. data/app/assets/stylesheets/pygments/autumn.css +58 -0
  70. data/app/assets/stylesheets/pygments/borland.css +46 -0
  71. data/app/assets/stylesheets/pygments/bw.css +34 -0
  72. data/app/assets/stylesheets/pygments/colorful.css +61 -0
  73. data/app/assets/stylesheets/pygments/default.css +62 -0
  74. data/app/assets/stylesheets/pygments/emacs.css +61 -0
  75. data/app/assets/stylesheets/pygments/friendly.css +61 -0
  76. data/app/assets/stylesheets/pygments/fruity.css +70 -0
  77. data/app/assets/stylesheets/pygments/manni.css +61 -0
  78. data/app/assets/stylesheets/pygments/murphy.css +61 -0
  79. data/app/assets/stylesheets/pygments/native.css +70 -0
  80. data/app/assets/stylesheets/pygments/pastie.css +60 -0
  81. data/app/assets/stylesheets/pygments/perldoc.css +58 -0
  82. data/app/assets/stylesheets/pygments/tango.css +69 -0
  83. data/app/assets/stylesheets/pygments/trac.css +59 -0
  84. data/app/assets/stylesheets/pygments/vim.css +70 -0
  85. data/app/assets/stylesheets/pygments/vs.css +33 -0
  86. data/app/controllers/formol/admin/admin_controller.rb +11 -0
  87. data/app/controllers/formol/admin/categories_controller.rb +60 -0
  88. data/app/controllers/formol/admin/forums_controller.rb +56 -0
  89. data/app/controllers/formol/application_controller.rb +47 -0
  90. data/app/controllers/formol/categories_controller.rb +21 -0
  91. data/app/controllers/formol/forums_controller.rb +19 -0
  92. data/app/controllers/formol/poll_votes_controller.rb +23 -0
  93. data/app/controllers/formol/posts_controller.rb +71 -0
  94. data/app/controllers/formol/preferences_controller.rb +32 -0
  95. data/app/controllers/formol/subscriptions_controller.rb +29 -0
  96. data/app/controllers/formol/topics_controller.rb +91 -0
  97. data/app/helpers/formol/admin/categories_helper.rb +4 -0
  98. data/app/helpers/formol/admin/forums_helper.rb +4 -0
  99. data/app/helpers/formol/application_helper.rb +35 -0
  100. data/app/helpers/formol/categories_helper.rb +15 -0
  101. data/app/helpers/formol/forums_helper.rb +19 -0
  102. data/app/helpers/formol/poll_votes_helper.rb +4 -0
  103. data/app/helpers/formol/posts_helper.rb +27 -0
  104. data/app/helpers/formol/preferences_helper.rb +4 -0
  105. data/app/helpers/formol/subscriptions_helper.rb +4 -0
  106. data/app/helpers/formol/topics_helper.rb +27 -0
  107. data/app/mailers/formol/subscription_mailer.rb +13 -0
  108. data/app/models/formol/category.rb +42 -0
  109. data/app/models/formol/forum.rb +77 -0
  110. data/app/models/formol/poll/option.rb +20 -0
  111. data/app/models/formol/poll/vote.rb +32 -0
  112. data/app/models/formol/poll.rb +107 -0
  113. data/app/models/formol/post.rb +162 -0
  114. data/app/models/formol/post_observer.rb +9 -0
  115. data/app/models/formol/topic/subscription.rb +16 -0
  116. data/app/models/formol/topic/track.rb +57 -0
  117. data/app/models/formol/topic.rb +185 -0
  118. data/app/models/formol/tracking.rb +45 -0
  119. data/app/models/formol/user_preference.rb +18 -0
  120. data/app/views/formol/admin/categories/_category.html.erb +9 -0
  121. data/app/views/formol/admin/categories/_form.html.erb +8 -0
  122. data/app/views/formol/admin/categories/edit.html.erb +3 -0
  123. data/app/views/formol/admin/categories/index.html.erb +3 -0
  124. data/app/views/formol/admin/categories/new.html.erb +3 -0
  125. data/app/views/formol/admin/forums/_form.html.erb +9 -0
  126. data/app/views/formol/admin/forums/_forum.html.erb +12 -0
  127. data/app/views/formol/admin/forums/edit.html.erb +3 -0
  128. data/app/views/formol/admin/forums/new.html.erb +3 -0
  129. data/app/views/formol/categories/_category.html.erb +5 -0
  130. data/app/views/formol/categories/index.html.erb +5 -0
  131. data/app/views/formol/categories/show.html.erb +3 -0
  132. data/app/views/formol/forums/_forum.html.erb +13 -0
  133. data/app/views/formol/forums/_list.html.erb +16 -0
  134. data/app/views/formol/forums/show.html.erb +18 -0
  135. data/app/views/formol/poll/options/_form.html.erb +1 -0
  136. data/app/views/formol/poll_votes/_form.html.erb +8 -0
  137. data/app/views/formol/polls/_form.html.erb +14 -0
  138. data/app/views/formol/polls/_poll.html.erb +24 -0
  139. data/app/views/formol/posts/_form.html.erb +7 -0
  140. data/app/views/formol/posts/_last_post.html.erb +9 -0
  141. data/app/views/formol/posts/_list.html.erb +3 -0
  142. data/app/views/formol/posts/_post.html.erb +47 -0
  143. data/app/views/formol/posts/edit.html.erb +10 -0
  144. data/app/views/formol/posts/new.html.erb +14 -0
  145. data/app/views/formol/preferences/show.html.erb +11 -0
  146. data/app/views/formol/subscription_mailer/topic_reply.en.html.erb +8 -0
  147. data/app/views/formol/subscription_mailer/topic_reply.fr.html.erb +8 -0
  148. data/app/views/formol/topics/_form.html.erb +20 -0
  149. data/app/views/formol/topics/_list.html.erb +16 -0
  150. data/app/views/formol/topics/_topic.html.erb +13 -0
  151. data/app/views/formol/topics/edit.html.erb +5 -0
  152. data/app/views/formol/topics/new.html.erb +5 -0
  153. data/app/views/formol/topics/show.html.erb +25 -0
  154. data/app/views/layouts/formol/mail.html.erb +9 -0
  155. data/config/locales/en.yml +3 -0
  156. data/config/locales/formol/views/forums/en.yml +13 -0
  157. data/config/locales/formol/views/forums/fr.yml +13 -0
  158. data/config/locales/formol/views/poll_votes/en.yml +5 -0
  159. data/config/locales/formol/views/polls/en.yml.yml +5 -0
  160. data/config/locales/formol/views/posts/en.yml +23 -0
  161. data/config/locales/formol/views/posts/fr.yml +20 -0
  162. data/config/locales/formol/views/topics/en.yml +23 -0
  163. data/config/locales/formol/views/topics/fr.yml +20 -0
  164. data/config/routes.rb +31 -0
  165. data/db/migrate/20111221101158_create_formol_categories.rb +7 -0
  166. data/db/migrate/20111221111417_create_formol_forums.rb +10 -0
  167. data/db/migrate/20111221121010_create_formol_topics.rb +9 -0
  168. data/db/migrate/20111221123515_create_formol_posts.rb +9 -0
  169. data/db/migrate/20111221151653_add_index_on_tables.rb +15 -0
  170. data/db/migrate/20120103163438_add_user_to_models.rb +13 -0
  171. data/db/migrate/20120104132631_add_counter_cache_to_models.rb +12 -0
  172. data/db/migrate/20120104163741_add_pinned_to_topics.rb +7 -0
  173. data/db/migrate/20120105133522_add_position_to_categories.rb +7 -0
  174. data/db/migrate/20120105190708_add_position_to_forums.rb +7 -0
  175. data/db/migrate/20120105212559_add_lock_to_topics.rb +7 -0
  176. data/db/migrate/20120106130944_add_first_post_and_last_post_to_topics.rb +8 -0
  177. data/db/migrate/20120106192951_change_forum_description_to_string.rb +9 -0
  178. data/db/migrate/20120107012517_add_last_post_to_forum.rb +7 -0
  179. data/db/migrate/20120107122921_add_missing_indexes.rb +12 -0
  180. data/db/migrate/20120107123654_add_default_on_forum_counters.rb +10 -0
  181. data/db/migrate/20120108114030_add_views_to_topics.rb +7 -0
  182. data/db/migrate/20120108131533_create_formol_topic_tracks.rb +13 -0
  183. data/db/migrate/20120109115141_create_formol_topic_subscriptions.rb +10 -0
  184. data/db/migrate/20120109220633_create_formol_user_preferences.rb +11 -0
  185. data/db/migrate/20120109223624_add_display_signature_to_posts.rb +7 -0
  186. data/db/migrate/20120109233054_add_display_signatures_and_avatars_to_user_preferences.rb +8 -0
  187. data/db/migrate/20120110152548_create_formol_polls.rb +14 -0
  188. data/db/migrate/20120110152618_create_formol_poll_options.rb +11 -0
  189. data/db/migrate/20120110152632_create_formol_poll_votes.rb +12 -0
  190. data/lib/formol/config.rb +12 -0
  191. data/lib/formol/controllers/auth.rb +9 -0
  192. data/lib/formol/controllers/nested/has_parent_category.rb +11 -0
  193. data/lib/formol/controllers/nested/has_parent_forum.rb +11 -0
  194. data/lib/formol/controllers/nested/has_parent_topic.rb +11 -0
  195. data/lib/formol/controllers.rb +5 -0
  196. data/lib/formol/engine.rb +18 -0
  197. data/lib/formol/formatters/redcarpet/formatter.rb +36 -0
  198. data/lib/formol/formatters/redcarpet/syntax_highlighter.rb +23 -0
  199. data/lib/formol/formatters.rb +2 -0
  200. data/lib/formol/generators/install/install_generator.rb +94 -0
  201. data/lib/formol/generators/install/templates/config/formol.yml +29 -0
  202. data/lib/formol/generators/install/templates/initializer/formol.rb.erb +21 -0
  203. data/lib/formol/generators/install/templates/lib/permissions/special_rights.rb +24 -0
  204. data/lib/formol/generators/install/templates/migrations/add_formol_profile_to_users.rb.erb +7 -0
  205. data/lib/formol/generators/install/templates/migrations/add_posts_count_to_user.rb.erb +7 -0
  206. data/lib/formol/generators.rb +1 -0
  207. data/lib/formol/integration/user.rb +76 -0
  208. data/lib/formol/integration.rb +1 -0
  209. data/lib/formol/permissions/controllers.rb +30 -0
  210. data/lib/formol/permissions/manager.rb +29 -0
  211. data/lib/formol/permissions/profile.rb +24 -0
  212. data/lib/formol/permissions/profiles.rb +44 -0
  213. data/lib/formol/permissions/user.rb +29 -0
  214. data/lib/formol/permissions.rb +5 -0
  215. data/lib/formol/rails/engine.rb +81 -0
  216. data/lib/formol/rails.rb +1 -0
  217. data/lib/formol/version.rb +10 -0
  218. data/lib/formol/view_helpers/assets_helper.rb +11 -0
  219. data/lib/formol/view_helpers/format_helper.rb +12 -0
  220. data/lib/formol/view_helpers.rb +2 -0
  221. data/lib/formol/will_paginate/list_link_renderer.rb +29 -0
  222. data/lib/formol/will_paginate.rb +1 -0
  223. data/lib/formol.rb +61 -0
  224. data/lib/formol_.rb +13 -0
  225. data/lib/tasks/formol_tasks.rake +0 -0
  226. metadata +348 -0
@@ -0,0 +1,60 @@
1
+ module Formol
2
+ class Admin::CategoriesController < Admin::AdminController
3
+ respond_to :html, :except => [:sort]
4
+ respond_to :js, :only => [:sort]
5
+
6
+ def index
7
+ categories
8
+ end
9
+
10
+ def new
11
+ category
12
+ end
13
+
14
+ def create
15
+ if category.save
16
+ respond_with(category, :status => :created, :location => admin_categories_path)
17
+ else
18
+ respond_with(category, :status => :unprocessable_entity) do |format|
19
+ format.html { render :new }
20
+ end
21
+ end
22
+ end
23
+
24
+ def edit
25
+ category
26
+ end
27
+
28
+ def update
29
+ if category.update_attributes(params[:category])
30
+ respond_with(category, :location => admin_categories_path)
31
+ else
32
+ respond_with(category) do |format|
33
+ format.html { render :edit }
34
+ end
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ category.destroy
40
+
41
+ respond_with(category, :location => admin_categories_path)
42
+ end
43
+
44
+ def sort
45
+ Formol::Category.sort(params[:categories])
46
+
47
+ render :nothing => true, :status => :ok
48
+ end
49
+
50
+ protected
51
+
52
+ def categories
53
+ @categories ||= Formol::Category.ready_for_listing.all
54
+ end
55
+
56
+ def category
57
+ @category ||= (params[:id].present? ? Formol::Category.find(params[:id]) : Formol::Category.new(params[:category]))
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,56 @@
1
+ module Formol
2
+ class Admin::ForumsController < Admin::AdminController
3
+ include Controllers::Nested::HasParentCategory
4
+
5
+ respond_to :html, :except => [:reorganize]
6
+ respond_to :js, :only => [:reorganize]
7
+
8
+ def new
9
+ forum
10
+
11
+ respond_with(forum)
12
+ end
13
+
14
+ def create
15
+ if forum.save
16
+ respond_with(forum, :status => :created, :location => admin_categories_path)
17
+ else
18
+ respond_with(forum, :status => :unprocessable_entity) do |format|
19
+ format.html { render :new }
20
+ end
21
+ end
22
+ end
23
+
24
+ def edit
25
+ forum
26
+ end
27
+
28
+ def update
29
+ if forum.update_attributes(params[:forum])
30
+ respond_with(forum, :location => admin_categories_path)
31
+ else
32
+ respond_with(forum) do |format|
33
+ format.html { render :edit }
34
+ end
35
+ end
36
+ end
37
+
38
+ def destroy
39
+ forum.destroy
40
+
41
+ respond_with(forum, :location => admin_categories_path)
42
+ end
43
+
44
+ def reorganize
45
+ Formol::Forum.reorganize(params[:forums], params[:category_id])
46
+
47
+ render :nothing => true, :status => :ok
48
+ end
49
+
50
+ protected
51
+
52
+ def forum
53
+ @forum ||= (params[:id].present? ? category.forums.find(params[:id]) : category.forums.new(params[:forum]))
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,47 @@
1
+ module Formol
2
+ class ApplicationController < ::ApplicationController
3
+ def per_page
4
+ 25
5
+ end
6
+
7
+ helper_method :per_page
8
+ helper_method :current_formol_user
9
+
10
+ def topic_post_page_number(topic, post)
11
+ #topic.posts.index is maybe not the most optimized
12
+ ((topic.posts(true).index(post) + 1).to_f / per_page.to_f).ceil
13
+ end
14
+
15
+ def paginated_forum_topic_post_path(forum, topic, post)
16
+ opts = {
17
+ :anchor => "formol_post_#{post.id}"
18
+ }
19
+
20
+ page = topic_post_page_number(topic, post)
21
+
22
+ opts[:page] = page if page > 1
23
+
24
+ forum_topic_path(forum, topic, opts)
25
+ end
26
+
27
+ helper_method :paginated_forum_topic_post_path
28
+
29
+ def topic_last_page_number(topic)
30
+ (topic.posts_count.to_f / per_page.to_f).ceil
31
+ end
32
+
33
+ def last_page_forum_topic_path(forum, topic, post)
34
+ opts = {
35
+ :anchor => "formol_post_#{post.id}"
36
+ }
37
+
38
+ page = topic_last_page_number(topic)
39
+
40
+ opts[:page] = page if page > 1
41
+
42
+ forum_topic_path(forum, topic, opts)
43
+ end
44
+
45
+ helper_method :last_page_forum_topic_path
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ module Formol
2
+ class CategoriesController < ApplicationController
3
+ respond_to :html
4
+
5
+ def index
6
+ formol_authorize!(current_formol_user, :read_category)
7
+
8
+ @categories = Formol::Category.ready_for_listing
9
+
10
+ respond_with(@categories)
11
+ end
12
+
13
+ def show
14
+ formol_authorize!(current_formol_user, :read_category)
15
+
16
+ @category = Formol::Category.find(params[:id])
17
+
18
+ respond_with(@category)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module Formol
2
+ class ForumsController < ApplicationController
3
+ respond_to :html
4
+
5
+ def show
6
+ formol_authorize!(current_formol_user, :read_forum)
7
+
8
+ @topics = forum.topics.ready_for_listing.page(params[:page]).per_page(per_page)
9
+
10
+ respond_with(forum)
11
+ end
12
+
13
+ protected
14
+
15
+ def forum
16
+ @forum ||= Formol::Forum.find(params[:id])
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Formol
2
+ class PollVotesController < ApplicationController
3
+ include Controllers::Nested::HasParentTopic
4
+
5
+ def create
6
+ formol_authorize!(current_formol_user, :vote_poll, topic)
7
+
8
+ poll.vote(current_formol_user, params['poll']['poll_vote_ids'])
9
+ rescue Formol::Poll::PollException => e
10
+ flash[:error] = t(e.message)
11
+ ensure
12
+ respond_to do |format|
13
+ format.html { redirect_to [topic.forum, topic] }
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def poll
20
+ @poll ||= topic.poll
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ module Formol
2
+ class PostsController < ApplicationController
3
+ include Controllers::Nested::HasParentTopic
4
+
5
+ respond_to :html
6
+
7
+ def show
8
+ formol_authorize!(current_formol_user, :read_topic)
9
+
10
+ post
11
+
12
+ #respond_with location doesn't work here :/
13
+ respond_with(post) do |format|
14
+ format.html { redirect_to paginated_forum_topic_post_path(post.topic.forum, post.topic, post) }
15
+ end
16
+ end
17
+
18
+ def new
19
+ formol_authorize!(current_formol_user, :create_post, topic)
20
+
21
+ post(:quote_id => params[:quote], :user => current_formol_user)
22
+ end
23
+
24
+ def create
25
+ formol_authorize!(current_formol_user, :create_post, topic)
26
+
27
+ post(:user => current_formol_user)
28
+
29
+ if post.save
30
+ #post.topic(true) needed to handle counter increment
31
+ respond_with(post, :status => :created, :location => last_page_forum_topic_path(topic.forum, post.topic(true), post))
32
+ else
33
+ respond_with(post, :status => :unprocessable_entity) do |format|
34
+ format.html { render :new }
35
+ end
36
+ end
37
+ end
38
+
39
+ def edit
40
+ formol_authorize!(current_formol_user, :edit_post, post)
41
+
42
+ post
43
+ end
44
+
45
+ def update
46
+ formol_authorize!(current_formol_user, :edit_post, post)
47
+
48
+ if post.update_attributes(params[:post])
49
+ respond_with(post, :location => paginated_forum_topic_post_path(topic.forum, topic, post))
50
+ else
51
+ respond_with(post) do |format|
52
+ format.html { render :edit }
53
+ end
54
+ end
55
+ end
56
+
57
+ def destroy
58
+ formol_authorize!(current_formol_user, :destroy_post)
59
+
60
+ post.destroy
61
+
62
+ respond_with(post, :location => [topic.forum, topic])
63
+ end
64
+
65
+ protected
66
+
67
+ def post(attrs = {})
68
+ @post ||= (params[:id].present? ? topic.posts.find(params[:id]) : topic.posts.new(attrs.merge(params[:post] || {})))
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,32 @@
1
+ module Formol
2
+ class PreferencesController < ApplicationController
3
+ respond_to :html
4
+
5
+ def show
6
+ preference
7
+
8
+ respond_with(preference)
9
+ end
10
+
11
+ def update
12
+ if preference.update_attributes(params[:user_preference])
13
+ respond_with(preference) do |format|
14
+ format.html { redirect_to preference_path }
15
+ end
16
+ # No wrong case to test for now
17
+ # just lower coverage
18
+ #
19
+ #else
20
+ # respond_with(preference) do |format|
21
+ # format.html { render :show }
22
+ # end
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def preference
29
+ @preference ||= current_formol_user.preference
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module Formol
2
+ class SubscriptionsController < ApplicationController
3
+ include Formol::Controllers::Nested::HasParentTopic
4
+
5
+ def create
6
+ formol_authorize!(current_formol_user, :subscribe_topic)
7
+
8
+ topic.register_subscriber(current_formol_user)
9
+
10
+ respond_to do |format|
11
+ format.js { render :nothing => true, :status => :ok }
12
+ end
13
+ rescue ActiveRecord::RecordInvalid
14
+ respond_to do |format|
15
+ format.js { render :nothing => true, :status => :unprocessable_entity }
16
+ end
17
+ end
18
+
19
+ def destroy
20
+ formol_authorize!(current_formol_user, :subscribe_topic)
21
+
22
+ topic.unregister_subscriber(current_formol_user)
23
+
24
+ respond_to do |format|
25
+ format.js { render :nothing => true, :status => :ok }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ module Formol
2
+ class TopicsController < ApplicationController
3
+ include Controllers::Nested::HasParentForum
4
+
5
+ respond_to :html
6
+
7
+ def show
8
+ formol_authorize!(current_formol_user, :read_topic)
9
+
10
+ topic.view!
11
+ topic.track_for_user!(current_formol_user)
12
+
13
+ @posts = topic.posts.ready_for_listing.page(params[:page]).per_page(per_page)
14
+
15
+ respond_with(topic)
16
+ end
17
+
18
+ def new
19
+ formol_authorize!(current_formol_user, :create_topic)
20
+
21
+ topic.posts.build(:register_user_as_subscriber => true)
22
+
23
+ unless topic.poll
24
+ topic.build_poll
25
+ topic.poll.options.build
26
+ end
27
+
28
+ respond_with(topic)
29
+ end
30
+
31
+ def create
32
+ formol_authorize!(current_formol_user, :create_topic)
33
+
34
+ topic.user = current_formol_user
35
+
36
+ if topic.save
37
+ respond_with(topic, :location => [forum, topic])
38
+ else
39
+ unless topic.poll
40
+ topic.build_poll
41
+ topic.poll.options.build
42
+ end
43
+
44
+ respond_with(topic, :status => :unprocessable_entity) do |format|
45
+ format.html { render :new }
46
+ end
47
+ end
48
+ end
49
+
50
+ def edit
51
+ formol_authorize!(current_formol_user, :edit_topic, topic)
52
+
53
+ respond_with(topic)
54
+ end
55
+
56
+ def update
57
+ formol_authorize!(current_formol_user, :edit_topic, topic)
58
+
59
+ if topic.update_attributes(params[:topic])
60
+ respond_with(topic, :location => [forum, topic])
61
+ else
62
+ respond_with(topic) do |format|
63
+ format.html { render :edit }
64
+ end
65
+ end
66
+ end
67
+
68
+ def destroy
69
+ formol_authorize!(current_formol_user, :destroy_topic)
70
+
71
+ topic.destroy
72
+
73
+ respond_with(topic, :location => forum)
74
+ end
75
+
76
+ protected
77
+
78
+ def topic
79
+ @topic ||= if params[:id].present?
80
+ if request.get?
81
+ forum.topics.ready_for_breadcrumb.includes(:poll => { :options => :votes }).find(params[:id])
82
+ else
83
+ # never eager load anything for update (brokes validates on associations)
84
+ forum.topics.find(params[:id])
85
+ end
86
+ else
87
+ forum.topics.new(params[:topic])
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,4 @@
1
+ module Formol
2
+ module Admin::CategoriesHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Formol
2
+ module Admin::ForumsHelper
3
+ end
4
+ end
@@ -0,0 +1,35 @@
1
+ module Formol
2
+ module ApplicationHelper
3
+ def breadcrumb_for(record)
4
+ items = [[t('home'), root_path]]
5
+
6
+ klass = record.class.to_s
7
+
8
+ case klass
9
+ when 'Formol::Category'
10
+ items << [record.label, category_path(record)]
11
+ when 'Formol::Forum'
12
+ items << [record.category.label, category_path(record.category)]
13
+ items << [record.label, forum_path(record)]
14
+ when 'Formol::Topic'
15
+ items << [record.forum.category.label, category_path(record.forum.category)]
16
+ items << [record.forum.label, forum_path(record.forum)]
17
+ items << [record.title, forum_topic_path(record.forum, record)]
18
+ end
19
+
20
+ content_tag(:ul, :class => 'breadcrumb') do
21
+ items.each do |(text, path)|
22
+ concat(content_tag(:li, :class => 'item'){ link_to(text, path) })
23
+ end
24
+ end
25
+ end
26
+
27
+ # When you want to link to last post, you don't need to calculate position
28
+ # of last post (wich need to load all topic's posts).
29
+ # Last post is obviously on last page, you can rely then on posts_count in
30
+ # topic object
31
+ def link_to_last_post(forum, topic, post, text = nil)
32
+ link_to (text || '>'), last_page_forum_topic_path(forum, topic, post), :title => t('formol.posts.last_post.go_to_last')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module Formol
2
+ module CategoriesHelper
3
+ def formol_forum_klass(forum, user)
4
+ klasses = []
5
+
6
+ if user.present?
7
+ klasses << (user.has_read_forum?(forum) ? 'read' : 'unread')
8
+ else
9
+ klasses << 'read'
10
+ end
11
+
12
+ klasses.join(' ')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Formol
2
+ module ForumsHelper
3
+ def formol_topic_klass(topic, user)
4
+ klasses = []
5
+
6
+ klasses << 'pinned' if topic.pinned
7
+ klasses << 'locked' if topic.locked
8
+
9
+ unless user.blank?
10
+ klasses << (user.has_read_topic?(topic) ? 'read' : 'unread')
11
+ else
12
+ klasses << 'read'
13
+ end
14
+
15
+
16
+ klasses.join(' ')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module Formol
2
+ module PollVotesHelper
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ module Formol
2
+ module PostsHelper
3
+ def link_to_post_position(post, index)
4
+ idx = ((params[:page] || 1) - 1) * per_page + index + 1
5
+
6
+ return link_to "##{idx}", "#formol_post_#{post.id}"
7
+ end
8
+
9
+ def link_to_last_fives_post_position(post, topic, index)
10
+ idx = topic.posts_count - index
11
+
12
+ return link_to "##{idx}", "#formol_post_#{post.id}"
13
+ end
14
+
15
+ def display_avatars?(user)
16
+ return true unless user
17
+
18
+ user.preference.display_avatars?
19
+ end
20
+
21
+ def display_signatures?(user, post)
22
+ s = post.display_signature? && !post.user.preference.signature.blank?
23
+
24
+ (s && user ? user.preference.display_signatures? : s)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ module Formol
2
+ module PreferencesHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Formol
2
+ module SubscriptionsHelper
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ module Formol
2
+ module TopicsHelper
3
+ include PostsHelper
4
+
5
+ def link_to_subscription(topic, user)
6
+ subscriber = topic.subscriber?(user)
7
+ method = subscriber ? :delete : :post
8
+ text = t('formol.topics.show.' + (subscriber ? 'unsubscribe' : 'subscribe'))
9
+ switch_text = t('formol.topics.show.' + (subscriber ? 'subscribe' : 'unsubscribe'))
10
+
11
+ link_to text, topic_subscriptions_path(topic), :method => method,
12
+ :remote => true,
13
+ :'data-switch-text' => switch_text
14
+ end
15
+
16
+ def attach_poll_checkbox(topic)
17
+ %{<input type="checkbox" name="topic[attach_poll]" id="topic_attach_poll" value="1" #{'checked="checked"' if topic.attach_poll} />}.html_safe
18
+ end
19
+
20
+ def display_poll_form?(poll, topic, user)
21
+ !poll.expired? &&
22
+ !topic.locked? &&
23
+ formol_authorized?(user, :vote_poll, topic) &&
24
+ !poll.voted?(user)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module Formol
2
+ class SubscriptionMailer < ActionMailer::Base
3
+ default :from => Formol.config.email_from
4
+
5
+ def topic_reply(post, subscriber)
6
+ @post = post
7
+
8
+ mail( :to => subscriber.email,
9
+ :subject => "New reply: #{post.topic.title}",
10
+ :layout => 'formol/mail')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ module Formol
2
+ class Category < ActiveRecord::Base
3
+ #Modules
4
+ include RankedModel
5
+
6
+ #Associations
7
+ has_many :forums, :dependent => :destroy,
8
+ :order => 'formol_forums.position'
9
+
10
+ #Validations
11
+ validates :label, :presence => true,
12
+ :length => { :in => 3..32,
13
+ :allow_blank => true },
14
+ :uniqueness => { :case_sensitive => false,
15
+ :allow_blank => true }
16
+
17
+ #Normalization
18
+ normalize_attributes :label, :with => [:squish, :blank]
19
+
20
+ #Sorting
21
+ ranks :position
22
+
23
+ #Scopes
24
+ class << self
25
+ # return categories conveniently ordered and eager-loaded for listing
26
+ def ready_for_listing
27
+ includes(:forums => { :last_post => [:user, :topic] }).rank(:position)
28
+ end
29
+ end
30
+
31
+ #Business
32
+ class << self
33
+ # Sorts categories from an array of ids
34
+ def sort(category_ids)
35
+ #TODO: should maybe optimize this?
36
+ (category_ids || []).each_with_index do |category_id, index|
37
+ find(category_id).update_attribute(:position, index)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end