/modules/mod_comment/mod_comment.erl

https://code.google.com/p/zotonic/ · Erlang · 185 lines · 140 code · 19 blank · 26 comment · 2 complexity · dbf31403573d365d24c795809c918e7c MD5 · raw file

  1. %% @author Marc Worrell <marc@worrell.nl>
  2. %% @copyright 2010 Marc Worrell
  3. %% Date: 2010-01-15
  4. %% @doc Simple comment module. Adds comments to any rsc.
  5. %% Copyright 2010 Marc Worrell
  6. %%
  7. %% Licensed under the Apache License, Version 2.0 (the "License");
  8. %% you may not use this file except in compliance with the License.
  9. %% You may obtain a copy of the License at
  10. %%
  11. %% http://www.apache.org/licenses/LICENSE-2.0
  12. %%
  13. %% Unless required by applicable law or agreed to in writing, software
  14. %% distributed under the License is distributed on an "AS IS" BASIS,
  15. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. %% See the License for the specific language governing permissions and
  17. %% limitations under the License.
  18. -module(mod_comment).
  19. -author("Marc Worrell <marc@worrell.nl>").
  20. -mod_title("Comments").
  21. -mod_description("Comments for pages. Implements a simple comment system with comments stored locally.").
  22. %% gen_server exports
  23. -export([init/1]).
  24. %% interface functions
  25. -export([
  26. event/2,
  27. observe_search_query/2
  28. ]).
  29. -include_lib("zotonic.hrl").
  30. %% @doc Handle the submit event of a new comment
  31. event({submit, {newcomment, Args}, TriggerId, _TargetId}, Context) ->
  32. {id, Id} = proplists:lookup(id, Args),
  33. case z_auth:is_auth(Context) of
  34. false ->
  35. Name = z_context:get_q_validated("name", Context),
  36. Email = z_context:get_q_validated("mail", Context);
  37. true ->
  38. Name = "",
  39. Email = ""
  40. end,
  41. Message = z_context:get_q_validated("message", Context),
  42. case m_comment:insert(Id, Name, Email, Message, Context) of
  43. {ok, CommentId} ->
  44. CommentsListElt = proplists:get_value(comments_list, Args, "comments-list"),
  45. CommentTemplate = proplists:get_value(comment_template, Args, "_comments_comment.tpl"),
  46. Comment = m_comment:get(CommentId, Context),
  47. Props = [
  48. {id, Id},
  49. {comment, Comment},
  50. {creator, m_rsc:p(Id, creator_id, Context)},
  51. {hidden, true}
  52. ],
  53. Html = z_template:render(CommentTemplate, Props, Context),
  54. Context1 = z_render:insert_bottom(CommentsListElt, Html, Context),
  55. Context2 = z_render:wire([
  56. {set_value, [{selector, "#"++TriggerId++" textarea[name=\"message\"]"}, {value, ""}]},
  57. {set_value, [{selector, "#"++TriggerId++" input[name=\"message\"]"}, {value, ""}]},
  58. {fade_in, [{target, "comment-"++integer_to_list(CommentId)}]}
  59. ], Context1),
  60. case z_convert:to_bool(proplists:get_value(do_redirect, Args, true)) of
  61. true -> z_render:wire({redirect, [{location, "#comment-"++integer_to_list(CommentId)}]}, Context2);
  62. false -> Context2
  63. end;
  64. {error, _} ->
  65. Context
  66. end.
  67. %% @doc Return the list of recent comments. Returned values are the complete records.
  68. observe_search_query({search_query, {recent_comments, []}, OffsetLimit}, Context) ->
  69. m_comment:search({recent_comments, []}, OffsetLimit, Context);
  70. observe_search_query(_, _Context) ->
  71. undefined.
  72. %% @doc Check the installation of the comment table. A bit more complicated because 0.1 and 0.2 had a table
  73. %% in the default installer, this module installs a different table.
  74. init(Context) ->
  75. ok = z_db:transaction(fun install1/1, Context),
  76. z_depcache:flush(Context),
  77. ok.
  78. install1(Context) ->
  79. ok = remove_old_comment_rsc_fields(Context),
  80. ok = remove_old_rating_table(Context),
  81. ok = install_comment_table(z_db:table_exists(comment, Context), Context),
  82. ok.
  83. remove_old_rating_table(Context) ->
  84. case z_db:table_exists(rating, Context) of
  85. false ->
  86. ok;
  87. true ->
  88. case z_db:column_names(rating, Context) of
  89. [comment_id,created,id,ip_address,rsc_id,visitor_id] ->
  90. z_db:q("drop table rating", Context),
  91. ok;
  92. _ ->
  93. ok
  94. end
  95. end.
  96. install_comment_table(true, Context) ->
  97. % Check for old table
  98. case z_db:column_names(comment, Context) of
  99. [created,creator_id,id,ip_address,notify_id,props,rating,rsc_id] ->
  100. z_db:q("drop table comment", Context),
  101. install_comment_table(false, Context);
  102. [created,id,email,gravatar_code,ip_address,is_visible,keep_informed,
  103. name,props,rsc_id,user_agent,user_id,visitor_id] ->
  104. z_db:q("alter table comment drop column visitor_id cascade, "
  105. "add column persistent_id character varying (32), "
  106. "add constraint fk_comment_persistent_id foreign key (persistent_id) "
  107. " references persistent(id) on delete set null on update cascade", Context),
  108. z_db:q("create index fki_comment_persistent_id on comment(persistent_id)", Context),
  109. ok;
  110. _ ->
  111. % todo: add list of current fields here
  112. ok
  113. end;
  114. install_comment_table(false, Context) ->
  115. z_db:q("
  116. create table comment (
  117. id serial not null,
  118. is_visible boolean not null default true,
  119. rsc_id int not null,
  120. user_id int,
  121. persistent_id character varying(32),
  122. gravatar_code character varying(40) not null default ''::character varying,
  123. email character varying(80) not null default ''::character varying,
  124. name character varying(80) not null default ''::character varying,
  125. user_agent character varying(250) not null default ''::character varying,
  126. ip_address character varying(40) not null default ''::character varying,
  127. keep_informed boolean not null default false,
  128. props bytea,
  129. created timestamp with time zone not null default now(),
  130. constraint comment_pkey primary key (id),
  131. constraint fk_comment_rsc_id foreign key (rsc_id)
  132. references rsc(id)
  133. on delete cascade on update cascade,
  134. constraint fk_comment_user_id foreign key (user_id)
  135. references rsc(id)
  136. on delete set null on update cascade,
  137. constraint fk_comment_persistent_id foreign key (persistent_id)
  138. references persistent(id)
  139. on delete set null on update cascade
  140. )
  141. ", Context),
  142. Indices = [
  143. {"fki_comment_rsc_id", "rsc_id"},
  144. {"fki_comment_user_id", "user_id"},
  145. {"fki_comment_persistent_id", "persistent_id"},
  146. {"fki_comment_ip_address", "ip_address"},
  147. {"comment_rsc_created_key", "rsc_id, created"},
  148. {"comment_created_key", "created"}
  149. ],
  150. [ z_db:q("create index "++Name++" on comment ("++Cols++")", Context) || {Name, Cols} <- Indices ],
  151. ok.
  152. %% @doc In the 0.1.0 and 0.2.0 releases we had some pivot information in the rsc table. Remove this.
  153. remove_old_comment_rsc_fields(Context) ->
  154. Cols = z_db:column_names(rsc, Context),
  155. R = [],
  156. R1 = case lists:member(comment_by, Cols) of true -> ["drop column comment_by"|R]; false -> R end,
  157. R2 = case lists:member(comments, Cols) of true -> ["drop column comments"|R1]; false -> R1 end,
  158. R3 = case lists:member(rating, Cols) of true -> ["drop column rating"|R2]; false -> R2 end,
  159. R4 = case lists:member(rating_count, Cols) of true -> ["drop column rating_count"|R3]; false -> R3 end,
  160. case R4 of
  161. [] ->
  162. ok;
  163. L ->
  164. z_db:q("alter table rsc " ++ string:join(L, ", "), Context),
  165. ok
  166. end.