Skip to content

Commit

Permalink
add simple interface for deleting webmentions
Browse files Browse the repository at this point in the history
* show the last several webmentions received on the dashboard
* provides a form in the dashboard for entering a source URL to find webmentions from that URL to delete them
* can delete a specific webmention (source & target pair)
* can delete all webmentions from a source URL
* deleting either will prevent future webmentions from that source URL from being processed

closes #86
  • Loading branch information
aaronpk committed Jul 20, 2017
1 parent 3c9b6b3 commit fa4c971
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 7 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ gem 'hashie'
gem 'json'
gem 'dalli'
gem 'ratom', :require => 'atom'
gem 'jwt'

gem 'omniauth'
gem 'omniauth-indieauth'
Expand Down
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ GEM
indefinite_article (0.2.4)
activesupport
json (1.8.1)
jwt (1.5.6)
kgio (2.9.2)
libxml-ruby (2.6.0)
mechanize (2.7.3)
Expand Down Expand Up @@ -165,6 +166,7 @@ DEPENDENCIES
hashie
indefinite_article
json
jwt
mechanize
microformats2
minitest
Expand All @@ -186,4 +188,4 @@ DEPENDENCIES
webmock

BUNDLED WITH
1.10.6
1.14.4
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,8 @@ Counts that are tracked, one graph for webmention, another for pingback
* unknown_error



## License

Copyright 2016 by Aaron Parecki.
Copyright 2017 by Aaron Parecki.

Available under the BSD License. See LICENSE.txt
2 changes: 2 additions & 0 deletions controllers/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Controller < Sinatra::Base
links = targets.links.all(:verified => true).each{|link| link.id}.length
types = repository(:default).adapter.select('SELECT type, COUNT(1) AS num FROM links
WHERE page_id IN ('+targets.map{|t| t.id}.join(',')+')
AND deleted = 0
GROUP BY type')
types.each do |type|
if type.type
Expand Down Expand Up @@ -63,6 +64,7 @@ class Controller < Sinatra::Base

opts = {
:verified => true,
:deleted => false,
:order => [],
:offset => (pageNum * limit),
:limit => limit
Expand Down
77 changes: 77 additions & 0 deletions controllers/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ def require_login
@user.save
end

opts = {
:verified => true,
:deleted => false,
:order => [],
:limit => 40
}
opts[:order] << :created_at.send(:desc)

@recent = @user.sites.pages.links.all(opts)

title "Dashboard"
erb :dashboard
end
Expand All @@ -44,6 +54,73 @@ def require_login
erb :settings
end

get '/delete/?' do
require_login

opts = {
href: params[:source],
deleted: false
}
@links = @user.sites.links.all opts

if params[:id]
@link = @user.sites.links.first({ id: params[:id] })
else
@link = nil
end

title "Delete"
erb :delete
end

post '/delete/?' do
require_login
verify_csrf '/delete'

# Delete this single webmention
if params[:id]
# Check that this ID belongs to this user
link = Link.get params[:id]
if link && link.site.account_id = session[:user_id]
# Mark this particular webmention as deleted
link.deleted = true
link.save
# Add this source URL to the blacklist for just this site
blacklist = Blacklist.new
blacklist.site = link.site
blacklist.source = link.href
blacklist.created_at = Time.now
blacklist.save
else
redirect "/dashboard"
end
end

# Delete all webmentions from this source
if params[:source]
# Mark each webmention as deleted
opts = {
href: params[:source],
deleted: false
}
links = @user.sites.links.all opts
links.each do |link|
link.deleted = true
link.save
end
# Add this source URL to the blacklist for each site
@user.sites.each do |site|
blacklist = Blacklist.new
blacklist.site = site
blacklist.source = params[:source]
blacklist.created_at = Time.now
blacklist.save
end
end

redirect "/dashboard"
end

post '/webhook/configure' do
require_login

Expand Down
11 changes: 11 additions & 0 deletions database/201707191955.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE `blacklists` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`site_id` int(11) DEFAULT NULL,
`source` varchar(512) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE links
ADD COLUMN `deleted` tinyint(4) NOT NULL DEFAULT '0';

24 changes: 24 additions & 0 deletions helpers/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,29 @@ def request_headers
env.inject({}){|acc, (k,v)| acc[$1.downcase] = v if k =~ /^http_(.*)/i; acc}
end

def csrf_token(path=nil)
JWT.encode({user_id: session[:user_id], path: path, exp: Time.now.to_i + 3600}, SiteConfig.session_secret, 'HS256')
end

def verify_csrf(path=nil)
if params[:csrf].empty?
redirect "/"
end

begin
jwt = JWT.decode params[:csrf], SiteConfig.session_secret, true, { :algorithm => 'HS256' }
puts jwt[0].inspect
if path != jwt[0]['path'] || jwt[0]['user_id'] != session[:user_id]
redirect "/"
else
return true
end
rescue JWT::ExpiredSignature
redirect "/"
end

redirect "/"
end

end
end
11 changes: 10 additions & 1 deletion helpers/webmention_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ def process_mention(username, source, target, protocol, token, code=nil)
return nil, error
end

# Check that the source URL is not in the blacklist
site = Site.first :account => target_account, :domain => target_domain
if !site.nil?
bl = Blacklist.first :site => site, :source => source
if !bl.nil?
return nil, 'blocked'
end
end

source_data = nil

# Private Webmentions
Expand Down Expand Up @@ -175,7 +184,7 @@ def process_mention(username, source, target, protocol, token, code=nil)
end

# If a callback URL is defined for this site, send to the callback now
if site.callback_url
if !site.callback_url.blank?
begin
puts "Sending to callback URL: #{site.callback_url}"

Expand Down
9 changes: 9 additions & 0 deletions models/blacklist.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Blacklist
include DataMapper::Resource
property :id, Serial
property :created_at, DateTime

belongs_to :site

property :source, String, :length => 512
end
2 changes: 2 additions & 0 deletions models/link.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class Link

belongs_to :notification, :required => false

property :deleted, Boolean, :default => false

property :created_at, DateTime
property :updated_at, DateTime

Expand Down
1 change: 1 addition & 0 deletions models/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Site
belongs_to :account
has n, :pages
has n, :notifications
has n, :links

property :public_access, Boolean, :default => true
property :irc_channel, String, :length => 255
Expand Down
36 changes: 36 additions & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,39 @@ input {
border-radius: 3px;
border: 1px #ddd solid;
}


/* mention list */
ul.recent-webmentions {
margin: 0;
margin-left: 1em;
padding: 0;
}

.recent-webmentions li {
display: flex;
align-items: center;
}

.recent-webmentions a, .recent-webmentions span {
margin-right: 0.5em;
}

.recent-webmentions a.delete {
font-weight: bold;
color: #C62F34;
font-size: 16px;
}
.recent-webmentions a.delete:hover {
text-decoration: none;
color: #E64152;
}


.h-card img {
max-height: 24px;
max-width: 24px;
border-radius: 4px;
}


36 changes: 33 additions & 3 deletions views/dashboard.erb
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
<div class="container-narrow">
<%= partial :'partials/_topbar' %>
<div class="page">
<%= partial :'partials/_topbar' %>
<div class="page">

<section>
<p>Congrats, you've signed in. Now you can use the following tags to accept webmentions for this account.</p>

<p><pre><code>&lt;link rel="pingback" href="https://webmention.io/<%= @user.username %>/xmlrpc" /&gt;
&lt;link rel="webmention" href="https://webmention.io/<%= @user.username %>/webmention" /&gt;</code></pre>
&lt;link rel="webmention" href="https://webmention.io/<%= @user.username %>/webmention" /&gt;</code></pre></p>

<p>You can use this account on any number of websites. The webmentions can be queried per website <a href="https://github.com/aaronpk/webmention.io#api">using the API</a>.</p>
</section>

<section>
<h2>Recent Webmentions</h2>

<ul class="recent-webmentions">
<% @recent.each do |link| %>
<li>
<a href="<%= link.author_url %>" class="u-author h-card"><img src="<%= link.author_photo.blank? ? '/img/noise.jpg' : link.author_photo %>" class="u-photo" width="24" title="<%= link.author_name %>"></a>
<a href="<%= link.href %>"><%= link.href %></a>
<span>on</span>
<a href="<%= link.page.href %>"><%= link.page.href %></a>

<a href="/delete?source=<%= CGI.escape link.href %>&id=<%= link.id %>" class="delete">&times;</a>
</li>
<% end %>
</ul>

<% if @recent.length == 0 %>
<p>There are no Webmentions yet! Add the tags above to your site and wait for someone to send you a Webmention!</p>
<% end %>

<h3>Delete Webmention</h3>

<p>Paste the source URL for a webmention you want to delete. You'll be able to delete any webmentions from that URL in the next step.</p>

<form action="/delete" method="get">
<input type="url" name="source" placeholder="source url">
<button type="submit">Preview Delete</button>
</form>
</section>

</div>

Expand Down
58 changes: 58 additions & 0 deletions views/delete.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<div class="container-narrow">
<%= partial :'partials/_topbar' %>
<div class="page">

<section>

<% if @link %>
<div style="border: 1px #ddd solid; border-radius: 4px; margin-bottom: 20px; padding: 10px;">
<h2 style="margin-top: 0">Delete this Webmention</h2>

<ul class="recent-webmentions">
<li>
<a href="<%= @link.author_url %>" class="u-author h-card"><img src="<%= @link.author_photo.blank? ? '/img/noise.jpg' : @link.author_photo %>" class="u-photo" width="24" title="<%= @link.author_name %>"></a>
<a href="<%= @link.href %>"><%= @link.href %></a>
<span>on</span>
<a href="<%= @link.page.href %>"><%= @link.page.href %></a>
</li>
</ul>

<form action="/delete" method="post" style="margin-top: 6px;">
<button type="submit">Delete</button>
<input type="hidden" name="id" value="<%= @link.id %>">
<input type="hidden" name="csrf" value="<%= csrf_token('/delete') %>">
</form>
</div>
<% end %>
<% if @links.length > 1 %>
<div style="border: 1px #ddd solid; border-radius: 4px; margin-bottom: 20px; padding: 10px;">
<h2 style="margin-top: 0">Delete all Webmentions from this Source</h2>

<form action="/delete" method="post" style="margin-bottom: 10px;">
<button type="submit">Delete All</button>
<input type="hidden" name="source" value="<%= @links.first.href %>">
<input type="hidden" name="csrf" value="<%= csrf_token('/delete') %>">
</form>

<ul class="recent-webmentions">
<% @links.each do |link| %>
<li>
<a href="<%= link.author_url %>" class="u-author h-card"><img src="<%= link.author_photo.blank? ? '/img/noise.jpg' : link.author_photo %>" class="u-photo" width="24" title="<%= link.author_name %>"></a>
<a href="<%= link.href %>"><%= link.href %></a>
<span>on</span>
<a href="<%= link.page.href %>"><%= link.page.href %></a>
</li>
<% end %>
</ul>
</div>
<% end %>
<% if @link || @links.length > 1 %>
<p><small>Note: This will mark the specified webmentions as deleted, and they will no longer show up in the API. Future webmentions from this source URL will be ignored. If you have a callback URL configured, this will not send anything to the callback URL, so you will need to delete your own copy of it in that case.</small></p>
<% else %>
<p>No webmentions were found with that source.</p>
<% end %>
</section>
</div>
</div>

0 comments on commit fa4c971

Please sign in to comment.