Commit 5853fd51ddcd232938ada80c239451ee3dedffe0

Authored by Antonio Terceiro
1 parent f49dbb2e

UI to mark comments as ham

Additionally, I have made the "Mark as SPAM" and "Remove" links in the
page view comment listing work through AJAX, so that now it is much more
practical to remove comments or mark them as spam.

ActionItem2306
app/controllers/my_profile/spam_controller.rb 0 → 100644
@@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
  1 +class SpamController < MyProfileController
  2 +
  3 + protect :moderate_comments, :profile
  4 +
  5 + def index
  6 + if request.post?
  7 + begin
  8 + # FIXME duplicated logic
  9 + #
  10 + # This logic more or less replicates what is already in
  11 + # ContentViewerController#view_page,
  12 + # ContentViewerController#remove_comment and
  13 + # ContentViewerController#mark_comment_as_spam
  14 + if params[:remove_comment]
  15 + profile.comments_received.find(params[:remove_comment]).destroy
  16 + end
  17 + if params[:mark_comment_as_ham]
  18 + profile.comments_received.find(params[:mark_comment_as_ham]).ham!
  19 + end
  20 + if request.xhr?
  21 + json_response(true)
  22 + else
  23 + redirect_to :action => :index
  24 + end
  25 + rescue
  26 + json_response(false)
  27 + end
  28 + return
  29 + end
  30 +
  31 + @spam = profile.comments_received.spam.paginate({:page => params[:page]})
  32 + end
  33 +
  34 + protected
  35 +
  36 + def json_response(status)
  37 + render :text => {'ok' => status }.to_json, :content_type => 'application/json'
  38 + end
  39 +
  40 +end
app/controllers/public/content_viewer_controller.rb
@@ -155,7 +155,7 @@ class ContentViewerController &lt; ApplicationController @@ -155,7 +155,7 @@ class ContentViewerController &lt; ApplicationController
155 @comment.destroy 155 @comment.destroy
156 session[:notice] = _('Comment succesfully deleted') 156 session[:notice] = _('Comment succesfully deleted')
157 end 157 end
158 - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] 158 + finish_comment_handling
159 end 159 end
160 160
161 def mark_comment_as_spam 161 def mark_comment_as_spam
@@ -164,7 +164,15 @@ class ContentViewerController &lt; ApplicationController @@ -164,7 +164,15 @@ class ContentViewerController &lt; ApplicationController
164 @comment.spam! 164 @comment.spam!
165 session[:notice] = _('Comment succesfully marked as SPAM') 165 session[:notice] = _('Comment succesfully marked as SPAM')
166 end 166 end
167 - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] 167 + finish_comment_handling
  168 + end
  169 +
  170 + def finish_comment_handling
  171 + if request.xhr?
  172 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  173 + else
  174 + redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
  175 + end
168 end 176 end
169 177
170 def per_page 178 def per_page
app/models/profile.rb
@@ -101,6 +101,8 @@ class Profile &lt; ActiveRecord::Base @@ -101,6 +101,8 @@ class Profile &lt; ActiveRecord::Base
101 has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC", :dependent => :destroy 101 has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC", :dependent => :destroy
102 belongs_to :template, :class_name => 'Profile', :foreign_key => 'template_id' 102 belongs_to :template, :class_name => 'Profile', :foreign_key => 'template_id'
103 103
  104 + has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments
  105 +
104 # FIXME ugly workaround 106 # FIXME ugly workaround
105 def self.human_attribute_name(attrib) 107 def self.human_attribute_name(attrib)
106 _(self.superclass.human_attribute_name(attrib)) 108 _(self.superclass.human_attribute_name(attrib))
app/views/content_viewer/_comment.rhtml
1 <li id="<%= comment.anchor %>" class="article-comment"> 1 <li id="<%= comment.anchor %>" class="article-comment">
2 <div class="article-comment-inner"> 2 <div class="article-comment-inner">
3 3
4 - <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (@page.profile.name == comment.author.name) ) %>"> 4 + <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (profile == comment.author) ) %>">
5 5
6 <% if comment.author %> 6 <% if comment.author %>
7 <%= link_to image_tag(profile_icon(comment.author, :minor)) + 7 <%= link_to image_tag(profile_icon(comment.author, :minor)) +
@@ -53,21 +53,29 @@ @@ -53,21 +53,29 @@
53 <% end %> 53 <% end %>
54 <%= report_abuse(comment.author, :comment_link, comment) if comment.author %> 54 <%= report_abuse(comment.author, :comment_link, comment) if comment.author %>
55 55
56 - <% if (logged_in? && (user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))) %>  
57 - <%= link_to(_('Mark as SPAM'), { :mark_comment_as_spam => comment.id }, :method => :post, :confirm => _('Are you sure you want to mark this comment as SPAM?'), :class => 'comment-footer comment-footer-link comment-footer-hide') %> 56 + <% if comment.spam? %>
58 &nbsp; 57 &nbsp;
  58 + <%= link_to_function(_('Mark as NOT SPAM'), 'remove_comment(this, %s); return false;' % url_for(:mark_comment_as_ham => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide') %>
  59 + <% else %>
  60 + <% if (logged_in? && (user == profile || user.has_permission?(:moderate_comments, profile))) %>
  61 + &nbsp;
  62 + <%= link_to_function(_('Mark as SPAM'), 'remove_comment(this, %s, %s); return false;' % [url_for(:mark_comment_as_spam => comment.id).to_json, _('Are you sure you want to mark this comment as SPAM?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide') %>
  63 + <% end %>
59 <% end %> 64 <% end %>
60 65
61 - <% if logged_in? && (user == @page.profile || user == comment.author || user.has_permission?(:moderate_comments, @page.profile)) %>  
62 - <%= link_to(_('Remove'), { :profile => params[:profile], :remove_comment => comment.id, :view => params[:view] }, :method => :post, :confirm => _('Are you sure you want to remove this comment and all its replies?'), :class => 'comment-footer comment-footer-link comment-footer-hide') %> 66 + <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %>
63 &nbsp; 67 &nbsp;
  68 + <%= link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => params[:profile], :remove_comment => comment.id, :view => params[:view]).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide') %>
64 <% end %> 69 <% end %>
65 70
66 - <%= link_to_function _('Reply'), 71 + <% unless comment.spam? %>
  72 + &nbsp;
  73 + <%= link_to_function _('Reply'),
67 "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, 74 "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id,
68 :class => 'comment-footer comment-footer-link comment-footer-hide', 75 :class => 'comment-footer comment-footer-link comment-footer-hide',
69 :id => 'comment-reply-to-' + comment.id.to_s 76 :id => 'comment-reply-to-' + comment.id.to_s
70 - %> 77 + %>
  78 + <% end %>
71 </div> 79 </div>
72 80
73 <% end %> 81 <% end %>
app/views/profile_editor/index.rhtml
@@ -66,6 +66,8 @@ @@ -66,6 +66,8 @@
66 66
67 <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %> 67 <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %>
68 68
  69 + <%= control_panel_button(_('Manage SPAM'), 'manage-spam', :controller => 'spam', :action => 'index') %>
  70 +
69 <% @plugins.dispatch(:control_panel_buttons).each do |button| %> 71 <% @plugins.dispatch(:control_panel_buttons).each do |button| %>
70 <%= control_panel_button(button[:title], button[:icon], button[:url]) %> 72 <%= control_panel_button(button[:title], button[:icon], button[:url]) %>
71 <% end %> 73 <% end %>
app/views/spam/index.rhtml 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +<h1><%= _('Manage SPAM') %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  5 +<% end %>
  6 +
  7 +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
  8 +<div id='article'>
  9 + <div class="comments" id="comments_list">
  10 + <ul class="article-comments-list">
  11 + <%= render :partial => 'content_viewer/comment', :collection => @spam %>
  12 + </ul>
  13 + </div>
  14 +</div>
  15 +
  16 +<%= pagination_links @spam %>
  17 +
  18 +<% button_bar do %>
  19 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  20 +<% end %>
public/images/control-panel/mail-mark-junk.png 0 → 100644

3.94 KB

public/images/control-panel/mail-mark-junk.svg 0 → 100644
@@ -0,0 +1,504 @@ @@ -0,0 +1,504 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +<svg
  4 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  5 + xmlns:cc="http://creativecommons.org/ns#"
  6 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  7 + xmlns:svg="http://www.w3.org/2000/svg"
  8 + xmlns="http://www.w3.org/2000/svg"
  9 + xmlns:xlink="http://www.w3.org/1999/xlink"
  10 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  11 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  12 + inkscape:export-ydpi="90.000000"
  13 + inkscape:export-xdpi="90.000000"
  14 + inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
  15 + width="48px"
  16 + height="48px"
  17 + id="svg11300"
  18 + sodipodi:version="0.32"
  19 + inkscape:version="0.46"
  20 + sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
  21 + sodipodi:docname="mail-mark-junk.svg"
  22 + inkscape:output_extension="org.inkscape.output.svg.inkscape">
  23 + <defs
  24 + id="defs3">
  25 + <inkscape:perspective
  26 + sodipodi:type="inkscape:persp3d"
  27 + inkscape:vp_x="0 : 24 : 1"
  28 + inkscape:vp_y="0 : 1000 : 0"
  29 + inkscape:vp_z="48 : 24 : 1"
  30 + inkscape:persp3d-origin="24 : 16 : 1"
  31 + id="perspective72" />
  32 + <linearGradient
  33 + inkscape:collect="always"
  34 + id="linearGradient5166">
  35 + <stop
  36 + style="stop-color:white;stop-opacity:1;"
  37 + offset="0"
  38 + id="stop5168" />
  39 + <stop
  40 + style="stop-color:white;stop-opacity:0;"
  41 + offset="1"
  42 + id="stop5170" />
  43 + </linearGradient>
  44 + <linearGradient
  45 + id="linearGradient5196">
  46 + <stop
  47 + style="stop-color:#dfe2dc;stop-opacity:1;"
  48 + offset="0"
  49 + id="stop5198" />
  50 + <stop
  51 + style="stop-color:#86917a;stop-opacity:1;"
  52 + offset="1"
  53 + id="stop5200" />
  54 + </linearGradient>
  55 + <linearGradient
  56 + id="linearGradient5188">
  57 + <stop
  58 + style="stop-color:white;stop-opacity:1;"
  59 + offset="0"
  60 + id="stop5190" />
  61 + <stop
  62 + style="stop-color:#aeaea3;stop-opacity:1;"
  63 + offset="1"
  64 + id="stop5192" />
  65 + </linearGradient>
  66 + <linearGradient
  67 + inkscape:collect="always"
  68 + id="linearGradient5176">
  69 + <stop
  70 + style="stop-color:black;stop-opacity:1;"
  71 + offset="0"
  72 + id="stop5178" />
  73 + <stop
  74 + style="stop-color:black;stop-opacity:0;"
  75 + offset="1"
  76 + id="stop5180" />
  77 + </linearGradient>
  78 + <linearGradient
  79 + id="linearGradient5162">
  80 + <stop
  81 + style="stop-color:#babdb6;stop-opacity:1;"
  82 + offset="0"
  83 + id="stop5164" />
  84 + <stop
  85 + style="stop-color:white;stop-opacity:1;"
  86 + offset="1"
  87 + id="stop5166" />
  88 + </linearGradient>
  89 + <linearGradient
  90 + inkscape:collect="always"
  91 + id="linearGradient5150">
  92 + <stop
  93 + style="stop-color:black;stop-opacity:1;"
  94 + offset="0"
  95 + id="stop5152" />
  96 + <stop
  97 + style="stop-color:black;stop-opacity:0;"
  98 + offset="1"
  99 + id="stop5154" />
  100 + </linearGradient>
  101 + <linearGradient
  102 + id="linearGradient11520">
  103 + <stop
  104 + id="stop11522"
  105 + offset="0.0000000"
  106 + style="stop-color:#ffffff;stop-opacity:1.0000000;" />
  107 + <stop
  108 + id="stop11524"
  109 + offset="1.0000000"
  110 + style="stop-color:#dcdcdc;stop-opacity:1.0000000;" />
  111 + </linearGradient>
  112 + <linearGradient
  113 + id="linearGradient11508"
  114 + inkscape:collect="always">
  115 + <stop
  116 + id="stop11510"
  117 + offset="0"
  118 + style="stop-color:#000000;stop-opacity:1;" />
  119 + <stop
  120 + id="stop11512"
  121 + offset="1"
  122 + style="stop-color:#000000;stop-opacity:0;" />
  123 + </linearGradient>
  124 + <linearGradient
  125 + id="linearGradient11494"
  126 + inkscape:collect="always">
  127 + <stop
  128 + id="stop11496"
  129 + offset="0"
  130 + style="stop-color:#ef2929;stop-opacity:1;" />
  131 + <stop
  132 + id="stop11498"
  133 + offset="1"
  134 + style="stop-color:#ef2929;stop-opacity:0;" />
  135 + </linearGradient>
  136 + <linearGradient
  137 + id="linearGradient11415">
  138 + <stop
  139 + id="stop11417"
  140 + offset="0.0000000"
  141 + style="stop-color:#204a87;stop-opacity:0.0000000;" />
  142 + <stop
  143 + style="stop-color:#204a87;stop-opacity:1.0000000;"
  144 + offset="0.50000000"
  145 + id="stop11423" />
  146 + <stop
  147 + id="stop11419"
  148 + offset="1"
  149 + style="stop-color:#204a87;stop-opacity:0;" />
  150 + </linearGradient>
  151 + <linearGradient
  152 + id="linearGradient11399"
  153 + inkscape:collect="always">
  154 + <stop
  155 + id="stop11401"
  156 + offset="0"
  157 + style="stop-color:#000000;stop-opacity:1;" />
  158 + <stop
  159 + id="stop11403"
  160 + offset="1"
  161 + style="stop-color:#000000;stop-opacity:0;" />
  162 + </linearGradient>
  163 + <linearGradient
  164 + gradientTransform="translate(-60.28571,-0.285714)"
  165 + y2="34.462429"
  166 + x2="43.615788"
  167 + y1="3.7744560"
  168 + x1="15.828360"
  169 + gradientUnits="userSpaceOnUse"
  170 + id="linearGradient11425"
  171 + xlink:href="#linearGradient11415"
  172 + inkscape:collect="always" />
  173 + <linearGradient
  174 + gradientTransform="translate(-60.57143,0.000000)"
  175 + y2="39.033859"
  176 + x2="35.679932"
  177 + y1="9.3458843"
  178 + x1="9.6957054"
  179 + gradientUnits="userSpaceOnUse"
  180 + id="linearGradient11427"
  181 + xlink:href="#linearGradient11415"
  182 + inkscape:collect="always" />
  183 + <linearGradient
  184 + y2="33.462429"
  185 + x2="26.758644"
  186 + y1="19.774456"
  187 + x1="13.267134"
  188 + gradientTransform="translate(-60.85714,0.428571)"
  189 + gradientUnits="userSpaceOnUse"
  190 + id="linearGradient11439"
  191 + xlink:href="#linearGradient11415"
  192 + inkscape:collect="always" />
  193 + <radialGradient
  194 + r="8.5000000"
  195 + fy="39.142857"
  196 + fx="12.071428"
  197 + cy="39.142857"
  198 + cx="12.071428"
  199 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.487395,0.000000,20.06483)"
  200 + gradientUnits="userSpaceOnUse"
  201 + id="radialGradient11441"
  202 + xlink:href="#linearGradient11399"
  203 + inkscape:collect="always" />
  204 + <radialGradient
  205 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  206 + gradientUnits="userSpaceOnUse"
  207 + r="3.8335034"
  208 + fy="15.048258"
  209 + fx="27.577173"
  210 + cy="15.048258"
  211 + cx="27.577173"
  212 + id="radialGradient11500"
  213 + xlink:href="#linearGradient11494"
  214 + inkscape:collect="always" />
  215 + <radialGradient
  216 + r="3.8335034"
  217 + fy="16.049133"
  218 + fx="27.577173"
  219 + cy="16.049133"
  220 + cx="27.577173"
  221 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  222 + gradientUnits="userSpaceOnUse"
  223 + id="radialGradient11504"
  224 + xlink:href="#linearGradient11494"
  225 + inkscape:collect="always" />
  226 + <radialGradient
  227 + gradientUnits="userSpaceOnUse"
  228 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.166583e-14,29.48178)"
  229 + r="6.5659914"
  230 + fy="44.565483"
  231 + fx="30.203562"
  232 + cy="44.565483"
  233 + cx="30.203562"
  234 + id="radialGradient11514"
  235 + xlink:href="#linearGradient11508"
  236 + inkscape:collect="always" />
  237 + <radialGradient
  238 + gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
  239 + gradientUnits="userSpaceOnUse"
  240 + r="20.530962"
  241 + fy="35.878170"
  242 + fx="24.445690"
  243 + cy="35.878170"
  244 + cx="24.445690"
  245 + id="radialGradient11526"
  246 + xlink:href="#linearGradient11520"
  247 + inkscape:collect="always" />
  248 + <radialGradient
  249 + r="6.5659914"
  250 + fy="44.565483"
  251 + fx="30.203562"
  252 + cy="44.565483"
  253 + cx="30.203562"
  254 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,3.185827e-15,29.48178)"
  255 + gradientUnits="userSpaceOnUse"
  256 + id="radialGradient11532"
  257 + xlink:href="#linearGradient11508"
  258 + inkscape:collect="always" />
  259 + <radialGradient
  260 + inkscape:collect="always"
  261 + xlink:href="#linearGradient11508"
  262 + id="radialGradient1348"
  263 + gradientUnits="userSpaceOnUse"
  264 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,-1.353344e-14,29.48178)"
  265 + cx="30.203562"
  266 + cy="44.565483"
  267 + fx="30.203562"
  268 + fy="44.565483"
  269 + r="6.5659914" />
  270 + <radialGradient
  271 + inkscape:collect="always"
  272 + xlink:href="#linearGradient11520"
  273 + id="radialGradient1350"
  274 + gradientUnits="userSpaceOnUse"
  275 + gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
  276 + cx="24.445690"
  277 + cy="35.878170"
  278 + fx="24.445690"
  279 + fy="35.878170"
  280 + r="20.530962" />
  281 + <radialGradient
  282 + inkscape:collect="always"
  283 + xlink:href="#linearGradient11494"
  284 + id="radialGradient1352"
  285 + gradientUnits="userSpaceOnUse"
  286 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  287 + cx="27.577173"
  288 + cy="16.049133"
  289 + fx="27.577173"
  290 + fy="16.049133"
  291 + r="3.8335034" />
  292 + <radialGradient
  293 + inkscape:collect="always"
  294 + xlink:href="#linearGradient11494"
  295 + id="radialGradient1354"
  296 + gradientUnits="userSpaceOnUse"
  297 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  298 + cx="27.577173"
  299 + cy="15.048258"
  300 + fx="27.577173"
  301 + fy="15.048258"
  302 + r="3.8335034" />
  303 + <radialGradient
  304 + inkscape:collect="always"
  305 + xlink:href="#linearGradient11508"
  306 + id="radialGradient1356"
  307 + gradientUnits="userSpaceOnUse"
  308 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.220359e-14,29.48178)"
  309 + cx="30.203562"
  310 + cy="44.565483"
  311 + fx="30.203562"
  312 + fy="44.565483"
  313 + r="6.5659914" />
  314 + <radialGradient
  315 + inkscape:collect="always"
  316 + xlink:href="#linearGradient11520"
  317 + id="radialGradient1366"
  318 + gradientUnits="userSpaceOnUse"
  319 + gradientTransform="matrix(2.049266,-1.696401e-32,0.000000,2.049266,-25.65002,-37.31089)"
  320 + cx="24.445690"
  321 + cy="35.878170"
  322 + fx="24.445690"
  323 + fy="35.878170"
  324 + r="20.530962" />
  325 + <radialGradient
  326 + inkscape:collect="always"
  327 + xlink:href="#linearGradient5150"
  328 + id="radialGradient5156"
  329 + cx="24.837126"
  330 + cy="40.663769"
  331 + fx="24.837126"
  332 + fy="40.663769"
  333 + r="21.478369"
  334 + gradientTransform="matrix(1,0,0,0.325103,2.211772e-16,27.44386)"
  335 + gradientUnits="userSpaceOnUse" />
  336 + <linearGradient
  337 + inkscape:collect="always"
  338 + xlink:href="#linearGradient5162"
  339 + id="linearGradient5168"
  340 + x1="24.365993"
  341 + y1="20.246058"
  342 + x2="32.600704"
  343 + y2="28.554564"
  344 + gradientUnits="userSpaceOnUse" />
  345 + <linearGradient
  346 + inkscape:collect="always"
  347 + xlink:href="#linearGradient5162"
  348 + id="linearGradient5170"
  349 + gradientUnits="userSpaceOnUse"
  350 + x1="22.008699"
  351 + y1="36.509514"
  352 + x2="23.585091"
  353 + y2="14.412428" />
  354 + <linearGradient
  355 + inkscape:collect="always"
  356 + xlink:href="#linearGradient5176"
  357 + id="linearGradient5182"
  358 + x1="25.632622"
  359 + y1="10.611729"
  360 + x2="38.714096"
  361 + y2="18.389904"
  362 + gradientUnits="userSpaceOnUse" />
  363 + <radialGradient
  364 + inkscape:collect="always"
  365 + xlink:href="#linearGradient5188"
  366 + id="radialGradient5361"
  367 + gradientUnits="userSpaceOnUse"
  368 + gradientTransform="matrix(2.135667,1.912751e-16,-1.890308e-16,2.110607,-26.90176,-15.66914)"
  369 + cx="23.688078"
  370 + cy="14.210698"
  371 + fx="23.688078"
  372 + fy="14.210698"
  373 + r="22.597087" />
  374 + <radialGradient
  375 + inkscape:collect="always"
  376 + xlink:href="#linearGradient5196"
  377 + id="radialGradient5363"
  378 + gradientUnits="userSpaceOnUse"
  379 + gradientTransform="matrix(1.790269,1.339577e-16,-1.323859e-16,1.769263,-15.81394,-11.94997)"
  380 + cx="20.089987"
  381 + cy="10.853651"
  382 + fx="20.089987"
  383 + fy="10.853651"
  384 + r="22.597087" />
  385 + <linearGradient
  386 + inkscape:collect="always"
  387 + xlink:href="#linearGradient5166"
  388 + id="linearGradient5172"
  389 + x1="19.450956"
  390 + y1="14.463861"
  391 + x2="23.71875"
  392 + y2="48.404987"
  393 + gradientUnits="userSpaceOnUse" />
  394 + </defs>
  395 + <sodipodi:namedview
  396 + stroke="#ef2929"
  397 + fill="#eeeeec"
  398 + id="base"
  399 + pagecolor="#ffffff"
  400 + bordercolor="#666666"
  401 + borderopacity="0.25490196"
  402 + inkscape:pageopacity="0.0"
  403 + inkscape:pageshadow="2"
  404 + inkscape:zoom="1"
  405 + inkscape:cx="-91.650069"
  406 + inkscape:cy="-6.8095951"
  407 + inkscape:current-layer="layer1"
  408 + showgrid="false"
  409 + inkscape:grid-bbox="true"
  410 + inkscape:document-units="px"
  411 + inkscape:showpageshadow="false"
  412 + inkscape:window-width="872"
  413 + inkscape:window-height="688"
  414 + inkscape:window-x="441"
  415 + inkscape:window-y="160" />
  416 + <metadata
  417 + id="metadata4">
  418 + <rdf:RDF>
  419 + <cc:Work
  420 + rdf:about="">
  421 + <dc:format>image/svg+xml</dc:format>
  422 + <dc:type
  423 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  424 + <dc:creator>
  425 + <cc:Agent>
  426 + <dc:title>Jakub Steiner</dc:title>
  427 + </cc:Agent>
  428 + </dc:creator>
  429 + <dc:source>http://jimmac.musichall.cz</dc:source>
  430 + <cc:license
  431 + rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
  432 + <dc:title>Mark mail as Junk</dc:title>
  433 + <dc:subject>
  434 + <rdf:Bag>
  435 + <rdf:li>mail</rdf:li>
  436 + <rdf:li>spam</rdf:li>
  437 + <rdf:li>junk</rdf:li>
  438 + </rdf:Bag>
  439 + </dc:subject>
  440 + </cc:Work>
  441 + <cc:License
  442 + rdf:about="http://creativecommons.org/licenses/publicdomain/">
  443 + <cc:permits
  444 + rdf:resource="http://creativecommons.org/ns#Reproduction" />
  445 + <cc:permits
  446 + rdf:resource="http://creativecommons.org/ns#Distribution" />
  447 + <cc:permits
  448 + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
  449 + </cc:License>
  450 + </rdf:RDF>
  451 + </metadata>
  452 + <g
  453 + id="layer1"
  454 + inkscape:label="Layer 1"
  455 + inkscape:groupmode="layer">
  456 + <path
  457 + sodipodi:type="arc"
  458 + style="opacity:0.3258427;color:black;fill:url(#radialGradient5156);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  459 + id="path4275"
  460 + sodipodi:cx="24.837126"
  461 + sodipodi:cy="40.663769"
  462 + sodipodi:rx="21.478369"
  463 + sodipodi:ry="6.9826794"
  464 + d="M 46.315495 40.663769 A 21.478369 6.9826794 0 1 1 3.358757,40.663769 A 21.478369 6.9826794 0 1 1 46.315495 40.663769 z"
  465 + transform="matrix(1.106996,0,0,1.106996,-3.364576,-5.411516)" />
  466 + <path
  467 + style="opacity:1;color:black;fill:url(#radialGradient5361);fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient5363);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  468 + d="M 9.0156115,37.570175 L 7.2478445,40.398602 L 10.960155,41.989592 L 13.611806,39.868272 L 25.455844,40.752155 L 29.168155,45.701903 L 32.526912,40.221825 L 36.769553,42.519922 L 42.426407,41.812816 L 41.012193,38.807612 L 44.901281,34.918524 L 39.421203,28.73134 L 44.724504,29.438447 L 47.022601,27.317127 L 44.017397,27.847457 L 40.835417,22.367379 L 36.239223,21.306719 L 39.951533,20.069282 L 40.12831,16.887302 L 32.880465,10.523341 L 30.228815,2.0380592 L 18.208,5.5735931 L 15.202796,12.644661 L 14.142136,10.346564 L 11.136932,10.523341 L 11.136932,13.351768 L 7.6013979,9.2859037 L 2.8284271,14.412428 L 2.8284271,21.660272 L 11.136932,28.908117 L 5.833631,31.913321 L 6.0104076,34.918524 L 9.0156115,37.570175 z "
  469 + id="path4273" />
  470 + <path
  471 + style="opacity:0.76966292;color:black;fill:url(#linearGradient5170);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  472 + d="M 19.445437,22.720933 L 18.561553,29.26167 L 20.682873,32.620427 L 16.086679,34.034641 L 13.435029,39.337942 L 25.455844,40.221825 L 28.284271,44.287689 L 30.582368,38.100505 L 26.162951,35.272078 L 19.445437,37.216622 L 22.097087,33.681088 L 20.682873,27.493903 L 19.445437,22.720933 z "
  473 + id="path5158" />
  474 + <path
  475 + style="opacity:0.61797753;color:black;fill:url(#linearGradient5168);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  476 + d="M 20.506097,23.781593 L 24.925514,30.675884 L 32.173359,25.726136 L 36.592776,28.20101 L 40.12831,25.195806 L 43.31029,28.024233 L 40.305087,23.074486 L 33.410795,21.483496 L 24.748737,17.240855 C 24.748737,17.240855 12.374369,20.953166 13.081475,20.953166 C 13.788582,20.953166 23.334524,21.129942 23.334524,21.129942 L 24.925514,18.831845 L 29.168155,20.776389 L 20.506097,23.781593 z "
  477 + id="path5160" />
  478 + <path
  479 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  480 + d="M 29.521708,3.4522727 L 19.091883,6.9878066 L 17.67767,10.876894 L 23.688077,15.119535 L 29.344931,9.6394571 L 29.521708,3.4522727 z "
  481 + id="path5172" />
  482 + <path
  483 + style="opacity:0.13483146;color:black;fill:url(#linearGradient5182);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  484 + d="M 29.698485,3.8058261 L 31.996582,11.230447 L 36.239223,14.942758 L 27.577165,12.998214 C 27.577165,12.998214 25.102291,15.826641 26.162951,15.826641 C 27.223611,15.826641 38.714096,17.240855 38.714096,17.240855 L 38.53732,19.538952 L 22.273864,16.533748 L 29.344931,9.9930105 L 29.698485,3.8058261 z "
  485 + id="path5174" />
  486 + <path
  487 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  488 + d="M 7.4246212,14.235651 L 6.5407377,23.074486 L 8.4852814,17.064078 L 13.611806,18.655069 L 14.849242,16.003418 L 21.036427,16.887302 L 22.45064,15.649865 L 13.435029,15.296311 L 12.551145,17.771185 L 8.6620581,15.826641 L 7.4246212,14.235651 z "
  489 + id="path5184" />
  490 + <path
  491 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  492 + d="M 22.45064,27.317127 L 25.455844,30.852661 L 32.350135,26.256466 L 36.592776,28.554563 L 43.487067,35.095301 L 35.885669,29.26167 L 32.173359,28.20101 L 24.925514,33.150758 L 22.45064,27.317127 z "
  493 + id="path5186" />
  494 + <path
  495 + sodipodi:type="inkscape:offset"
  496 + inkscape:radius="-0.83777463"
  497 + inkscape:original="M 30.21875 2.03125 L 18.21875 5.5625 L 15.1875 12.65625 L 14.15625 10.34375 L 11.125 10.53125 L 11.125 13.34375 L 7.59375 9.28125 L 2.84375 14.40625 L 2.84375 21.65625 L 11.125 28.90625 L 5.84375 31.90625 L 6 34.90625 L 9 37.5625 L 7.25 40.40625 L 10.96875 42 L 13.625 39.875 L 25.46875 40.75 L 29.15625 45.6875 L 32.53125 40.21875 L 36.78125 42.53125 L 42.4375 41.8125 L 41 38.8125 L 44.90625 34.90625 L 39.40625 28.71875 L 44.71875 29.4375 L 47.03125 27.3125 L 44.03125 27.84375 L 40.84375 22.375 L 36.25 21.3125 L 39.9375 20.0625 L 40.125 16.875 L 32.875 10.53125 L 30.21875 2.03125 z "
  498 + xlink:href="#path4273"
  499 + style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient5172);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  500 + id="path5359"
  501 + inkscape:href="#path4273"
  502 + d="M 29.65625,3.09375 L 18.84375,6.25 L 15.96875,13 C 15.834262,13.30255 15.53422,13.497528 15.203125,13.497528 C 14.87203,13.497528 14.571988,13.30255 14.4375,13 L 13.65625,11.21875 L 11.96875,11.3125 L 11.96875,13.34375 C 11.973462,13.69495 11.758625,14.011813 11.430625,14.13743 C 11.102625,14.263047 10.731089,14.170753 10.5,13.90625 L 7.5625,10.53125 L 3.6875,14.71875 L 3.6875,21.28125 L 11.6875,28.28125 C 11.883657,28.462891 11.981357,28.727236 11.950479,28.992788 C 11.919601,29.25834 11.763852,29.493214 11.53125,29.625 L 6.71875,32.375 L 6.8125,34.5 L 9.5625,36.9375 C 9.8645501,37.207345 9.9303261,37.654622 9.71875,38 L 8.46875,40 L 10.84375,41.03125 L 13.09375,39.21875 C 13.260233,39.0823 13.472846,39.015159 13.6875,39.03125 L 25.53125,39.90625 C 25.769158,39.930835 25.985219,40.055923 26.125,40.25 L 29.09375,44.21875 L 31.8125,39.78125 C 32.041236,39.389926 32.539705,39.251463 32.9375,39.46875 L 36.96875,41.65625 L 41.1875,41.125 L 40.25,39.1875 C 40.087762,38.864671 40.150741,38.474201 40.40625,38.21875 L 43.75,34.875 L 38.78125,29.28125 C 38.529387,29.019826 38.474521,28.625949 38.645349,28.305645 C 38.816178,27.985341 39.17384,27.811478 39.53125,27.875 L 43.40625,28.40625 C 43.369413,28.35773 43.337977,28.305337 43.3125,28.25 L 40.3125,23.09375 L 36.0625,22.125 C 35.709163,22.034799 35.454745,21.726417 35.433331,21.362378 C 35.411917,20.998338 35.628425,20.662254 35.96875,20.53125 L 39.125,19.46875 L 39.25,17.21875 L 32.3125,11.15625 C 32.19648,11.05643 32.110019,10.926737 32.0625,10.78125 L 29.65625,3.09375 z " />
  503 + </g>
  504 +</svg>
public/javascripts/application.js
@@ -674,6 +674,21 @@ function add_comment_reply_form(button, comment_id) { @@ -674,6 +674,21 @@ function add_comment_reply_form(button, comment_id) {
674 return f; 674 return f;
675 } 675 }
676 676
  677 +function remove_comment(button, url, msg) {
  678 + var $ = jQuery;
  679 + var $button = $(button);
  680 + if (msg && !confirm(msg)) {
  681 + $button.removeClass('comment-button-loading');
  682 + return;
  683 + }
  684 + $button.addClass('comment-button-loading');
  685 + $.post(url, function(data) {
  686 + if (data.ok) {
  687 + $button.closest('.article-comment').slideUp();
  688 + }
  689 + });
  690 +}
  691 +
677 function original_image_dimensions(src) { 692 function original_image_dimensions(src) {
678 var img = new Image(); 693 var img = new Image();
679 img.src = src; 694 img.src = src;
public/stylesheets/application.css
@@ -1262,6 +1262,11 @@ a.comment-picture { @@ -1262,6 +1262,11 @@ a.comment-picture {
1262 } 1262 }
1263 /* * * Comment Box * * */ 1263 /* * * Comment Box * * */
1264 1264
  1265 +.comment-button-loading {
  1266 + padding-left: 20px;
  1267 + background: transparent url(../images/loading-small.gif) no-repeat left center;
  1268 +}
  1269 +
1265 .post_comment_box { 1270 .post_comment_box {
1266 text-align: center; 1271 text-align: center;
1267 padding: 0px 15px 5px 15px; 1272 padding: 0px 15px 5px 15px;
@@ -3840,6 +3845,9 @@ h1#agenda-title { @@ -3840,6 +3845,9 @@ h1#agenda-title {
3840 .controller-profile_editor .msie6 a.control-panel-edit-location { 3845 .controller-profile_editor .msie6 a.control-panel-edit-location {
3841 background-image: url(../images/control-panel/set-geolocation.gif) 3846 background-image: url(../images/control-panel/set-geolocation.gif)
3842 } 3847 }
  3848 +.controller-profile_editor a.control-panel-manage-spam {
  3849 + background-image: url(../images/control-panel/mail-mark-junk.png)
  3850 +}
3843 /* ==> public/stylesheets/controller_profile_members.css <== */ 3851 /* ==> public/stylesheets/controller_profile_members.css <== */
3844 .controller-profile_members .no-boxes { 3852 .controller-profile_members .no-boxes {
3845 margin: 30px 3853 margin: 30px
test/functional/content_viewer_controller_test.rb
@@ -92,7 +92,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -92,7 +92,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
92 92
93 login_as 'testuser' 93 login_as 'testuser'
94 get :view_page, :profile => 'testuser', :page => [ 'test' ] 94 get :view_page, :profile => 'testuser', :page => [ 'test' ]
95 - assert_tag :tag => 'a', :attributes => { :href => '/testuser/test?remove_comment=' + comment.id.to_s } 95 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id}.quot) }
96 end 96 end
97 97
98 should 'display remove comment button with param view when image' do 98 should 'display remove comment button with param view when image' do
@@ -106,8 +106,9 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -106,8 +106,9 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
106 106
107 login_as 'testuser' 107 login_as 'testuser'
108 get :view_page, :profile => 'testuser', :page => [ image.filename ], :view => true 108 get :view_page, :profile => 'testuser', :page => [ image.filename ], :view => true
109 - assert_tag :tag => 'a', :attributes => { :href => "/testuser/#{image.filename}?remove_comment=" + comment.id.to_s + '&amp;view=true'}  
110 - end 109 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/#{image.filename}\?remove_comment=#{comment.id}.*amp;view=true.quot) }
  110 +end
  111 +
111 112
112 should 'not add unneeded params for remove comment button' do 113 should 'not add unneeded params for remove comment button' do
113 profile = create_user('testuser').person 114 profile = create_user('testuser').person
@@ -117,8 +118,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -117,8 +118,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
117 comment.save! 118 comment.save!
118 119
119 login_as 'testuser' 120 login_as 'testuser'
120 - get :view_page, :profile => 'testuser', :page => [ 'test' ], :random_param => 'bli' # <<<<<<<<<<<<<<<  
121 - assert_tag :tag => 'a', :attributes => { :href => '/testuser/test?remove_comment=' + comment.id.to_s } 121 + get :view_page, :profile => 'testuser', :page => [ 'test' ], :random_param => 'bli'
  122 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id.to_s}.quot) }
122 end 123 end
123 124
124 should 'be able to remove comment' do 125 should 'be able to remove comment' do
test/functional/spam_controller_test.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class SpamControllerTest < ActionController::TestCase
  4 +
  5 + def setup
  6 + @profile = create_user.person
  7 + @article = fast_create(TextileArticle, :profile_id => @profile.id)
  8 + @spam = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')
  9 +
  10 + login_as @profile.identifier
  11 + end
  12 +
  13 + test "should only list spammy comments" do
  14 + ham = fast_create(Comment, :source_id => @article.id)
  15 +
  16 + get :index, :profile => @profile.identifier
  17 +
  18 + assert_equivalent [@spam], assigns(:spam)
  19 + end
  20 +
  21 + test "should mark comments as ham" do
  22 + post :index, :profile => @profile.identifier, :mark_comment_as_ham => @spam.id
  23 +
  24 + @spam.reload
  25 + assert @spam.ham?
  26 + end
  27 +
  28 + test "should remove comments" do
  29 + post :index, :profile => @profile.identifier, :remove_comment => @spam.id
  30 +
  31 + assert !Comment.exists?(@spam.id)
  32 + end
  33 +
  34 +
  35 +end