Page Menu
Home
GRNET
Search
Configure Global Search
Log In
Files
F461640
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, May 18, 9:22 AM
Size
18 KB
Mime Type
text/x-diff
Expires
Tue, May 20, 9:22 AM (1 d, 19 h)
Engine
blob
Format
Raw Data
Handle
220312
Attached To
rWEBDNS WebDNS (edet4)
View Options
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 3efd037..7f63099 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -1,51 +1,55 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any styles
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
* file per style scope.
*
*= require bootstrap.min
*= require dataTables.bootstrap.min
*= require bootstrap-editable
*= require_tree .
*= require_self
*/
/* Make sure navbar does not overlay body */
body {
padding-top: 70px;
}
.highlight , .highlight>td {
background-color: #b8e0b8 !important;
}
/* DataTable resets bootstrap's margin-bottom */
.datatable-wrapper {
margin-bottom: 20px;
}
/* Reset bootstrap's help cursor on control links */
table a abbr[title] {
cursor: pointer;
}
.tab-pane table {
margin-top: 20px;
}
#inline-record-form #record_ttl {
width: 80px;
}
#inline-record-form #record_prio {
width: 80px;
}
#inline-record-form #record_content {
width: 300px;
}
+
+#domains span.glyphicon-volume-up {
+ color: red;
+}
diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb
index 5c9903d..35f001f 100644
--- a/app/controllers/domains_controller.rb
+++ b/app/controllers/domains_controller.rb
@@ -1,104 +1,107 @@
+require 'set'
+
class DomainsController < ApplicationController
before_action :authenticate_user!
before_action :domain, only: [:show, :edit, :edit_dnssec, :update, :destroy, :full_destroy]
before_action :group, only: [:show, :edit, :edit_dnssec, :update, :destroy, :full_destroy]
helper_method :edit_group_scope
# GET /domains
def index
@domains = show_domain_scope.includes(:group, :soa).all
+ @optouts = Set.new current_user.subscriptions.pluck(:domain_id)
end
# GET /domains/1
def show
@record = Record.new(domain_id: @domain.id)
end
# GET /domains/new
def new
@domain = Domain.new(new_domain_params)
end
# GET /domains/1/edit
def edit
end
# GET /domains/1/edit_dnssec
def edit_dnssec
end
# POST /domains
def create
@domain = Domain.new(domain_params)
if @domain.save
notify_domain(@domain, :create)
redirect_to @domain, notice: "#{@domain.name} was successfully created."
else
render :new
end
end
# PATCH/PUT /domains/1
def update
if @domain.update(domain_params)
notify_domain(@domain, :update)
redirect_to @domain, notice: "#{@domain.name} was successfully updated."
else
if domain_params[:dnssec] # DNSSEC form
render :edit_dnssec
else
render :edit
end
end
end
# DELETE /domains/1
def destroy
if @domain.remove
notify_domain(@domain, :destroy)
redirect_to domains_url, notice: "#{@domain.name} is scheduled for removal."
else
redirect_to domains_url, alert: "#{@domain.name} cannot be deleted! (state '#{@domain.state}')"
end
end
# DELETE /domains/1/full_destroy
def full_destroy
if @domain.full_remove
notify_domain(@domain, :destroy)
redirect_to domains_url,
notice: "#{@domain.name} is scheduled for full removal. DS records will be dropped from the parent zone before proceeding"
else
redirect_to domains_url, alert: "#{@domain.name} cannot be deleted! (state '#{@domain.state}')"
end
end
private
def group
domain.group
end
def new_domain_params
params.permit(:group_id)
end
def domain_params
params.require(:domain).tap { |d|
# Make sure group id is permitted (belongs to edit_group_scope)
d[:group_id] = edit_group_scope.find_by_id(d[:group_id]).try(:id)
# Sometimes domain name might contain whitespace, make sure we remove
# them. Note that we use a regex to handle unicode whitespace characters as well.
d[:name] = d[:name].gsub(/\p{Space}/, '') if d[:name]
}.permit(:name, :type, :master, :group_id,
:dnssec, :dnssec_parent, :dnssec_parent_authority, :dnssec_policy_id)
end
def notify_domain(*args)
notification.notify_domain(current_user, *args) if WebDNS.settings[:notifications]
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 0d0c3eb..d22f844 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,53 +1,54 @@
class GroupsController < ApplicationController
before_action :authenticate_user!
before_action :group, only: [:show, :create_member, :destroy_member, :search_member]
before_action :user, only: [:destroy_member]
# GET /groups/1
def show
@domains = @group.domains
+ @optouts = Set.new current_user.subscriptions.where(domain_id: @domains).pluck(:domain_id)
end
# POST /groups/1/members/
def create_member
@user = User.find_by_email(params[:email])
if !@user
redirect_to group_path(@group, anchor: 'tab-members'), alert: "User '#{params[:email]}' not found!"
return
end
membership = @group.memberships.find_or_create_by!(user_id: @user.id)
redirect_to group_path(@group, anchor: 'tab-members'), notice: "#{membership.user.email} is now a member of #{@group.name}"
end
# DELETE /groups/1/member/1
def destroy_member
membership = @group.memberships.find_by!(user_id: user.id)
membership.destroy!
redirect_to @group, notice: "#{membership.user.email} was successfully removed from #{@group.name}"
end
def search_member
results = []
if params[:q].present?
uids = group.users.pluck(:id)
results = User
.where('email like ?', "#{params[:q]}%")
.where.not(id: uids) # Exclude group members
.limit(10)
end
render json: results.map { |r| Hash[:id, r.id, :email, r.email] }
end
private
def user
@user ||= User.find(params[:user_id])
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index ef79c33..01aae98 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,31 +1,48 @@
class UsersController < ApplicationController
before_action :authenticate_user!
- before_action :user, only: [:token, :generate_token]
+ before_action :user, only: [:mute, :unmute, :token, :generate_token]
# GET /users/1/token
def token
end
# POST /users/1/generate_token
def generate_token
@user.token = SecureRandom.hex(10)
@user.save!
redirect_to token_user_path(@user)
end
+ # PUT /users/1/unsubscribe/2
+ def mute
+ domain = show_domain_scope.find(params[:domain_id])
+ @user.subscriptions.find_or_create_by!(domain: domain)
+
+ redirect_to domains_url, notice: "Successfully unsubscribed from #{domain.name} notifications!"
+ end
+
+ # PUT /users/1/subscribe/2
+ def unmute
+ domain = show_domain_scope.find(params[:domain_id])
+ # Drop all opt-outs
+ @user.subscriptions.where(domain: domain).delete_all
+
+ redirect_to domains_url, notice: "Successfully subscribed to #{domain.name} notifications!"
+ end
+
private
def user
- @user ||= User.find(params[:id])
+ @user ||= User.find(params[:user_id] || params[:id])
# Guard access to other user tokens
if current_user.id != @user.id && !admin?
redirect_to(root_path, alert: 'You need admin rights for that!')
end
@user
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index b48b410..7a7b4d0 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,57 +1,65 @@
module ApplicationHelper
TIME_PERIODS = {
1.second => 'second',
1.minute => 'minute',
1.hour => 'hour',
1.day => 'day',
1.week => 'week',
1.month => 'month',
1.year.to_i => 'year',
}
def can_edit?(object)
return true unless object.respond_to?(:editable?)
by = admin? ? :admin : :user
object.editable?(by)
end
def seconds_to_human(seconds)
acc = {}
remaining = seconds
TIME_PERIODS.to_a.reverse_each do |p, human|
period_count, remaining = remaining.divmod(p)
acc[human] = period_count if not period_count.zero?
end
acc.map { |singular, count|
human = count < 2 ? singular : "#{singular}s"
"#{count} #{human}"
}.join(', ')
end
def link_to_edit(*args, &block)
link_to(abbr_glyph(:pencil, 'Edit'), *args, &block)
end
def link_to_destroy(*args, &block)
link_to(abbr_glyph(:remove, 'Remove'), *args, &block)
end
def link_to_enable(*args, &block)
link_to(abbr_glyph(:'eye-close', 'Enable'), *args, &block)
end
def link_to_disable(*args, &block)
link_to(abbr_glyph(:'eye-open', 'Disable'), *args, &block)
end
+ def link_to_mute(*args, &block)
+ link_to(abbr_glyph(:'volume-off', 'Disable notifications'), *args, &block)
+ end
+
+ def link_to_unmute(*args, &block)
+ link_to(abbr_glyph(:'volume-up', 'Renable notifications'), *args, &block)
+ end
+
def glyph(icon)
content_tag(:span, '', class: "glyphicon glyphicon-#{icon}")
end
def abbr_glyph(icon, title)
content_tag(:abbr, glyph(icon), title: title)
end
end
diff --git a/app/views/domains/index.html.erb b/app/views/domains/index.html.erb
index a95799f..22beec0 100644
--- a/app/views/domains/index.html.erb
+++ b/app/views/domains/index.html.erb
@@ -1,56 +1,61 @@
<% if current_user.memberships.empty? %>
<div class="jumbotron">
<h2>Wellcome to WebDNS!</h2>
<p>
In order to manage domains you have to be a member of a group.
</p>
<p>
You can either contact an admin to create a new group for you, or ask another user for an invite to an existing group.
</p>
</div>
<% end %>
<div class="datatable-wrapper">
<table id="domains" class="table table-striped">
<thead>
<tr>
<th>Domain</th>
<th>Serial</th>
<th>Group</th>
<th>State</th>
<th>Slave</th>
<th>DNSSEC</th>
<th class="no-order-and-search">Controls</th>
</tr>
</thead>
<tbody>
<% @domains.group_by(&:group).each do |group, domains| %>
<% domains.each do |domain| %>
<tr class="group-<%= group.id =%>">
<td><%= link_to domain.name, domain %></td>
<td><%= domain.serial %></td>
<td><%= link_to group.name, group_path(group) %></td>
<td><%= human_state(domain.state) %></td>
<td><%= domain.slave? ? domain.master : '-' %></td>
<td><%= domain.dnssec? ? 'secure' : '-' %></td>
<td>
<%= link_to_edit edit_domain_path(domain) %>
+ <% if @optouts.include? domain.id %>
+ <%= link_to_unmute user_domain_unmute_path(current_user, domain), method: :put %>
+ <% else %>
+ <%= link_to_mute user_domain_mute_path(current_user, domain), method: :put %>
+ <% end %>
<%= link_to_destroy domain, method: :delete, data: { confirm: 'Are you sure?' } if domain.can_remove? %>
<%= link_to_full_destroy full_destroy_domain_path(domain),
method: :delete, data: { confirm: 'Are you sure?' } if domain.can_remove? && domain.dnssec? %>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
<p>
<% if current_user.memberships.any? %>
<%= link_to 'Add Domain', new_domain_path, class: 'btn btn-primary' %>
<% else %>
<%= link_to 'Add Domain', new_domain_path, class: 'btn btn-primary disabled' %>
<% end %>
</p>
diff --git a/app/views/groups/show.html.erb b/app/views/groups/show.html.erb
index 7874355..d81de70 100644
--- a/app/views/groups/show.html.erb
+++ b/app/views/groups/show.html.erb
@@ -1,87 +1,92 @@
<% content_for :more_breadcrumbs do %>
<li>
<%= link_to_edit edit_admin_group_path(@group) %>
<%= link_to_destroy admin_group_path(@group), method: :delete, data: { confirm: 'Are you sure?' } %>
</li>
<% end if admin? %>
<ul class="nav nav-tabs">
<li role="presentation" class="active"><a id="tab-link-domains" data-toggle="tab" href="#domains_tab">Domains</a></li>
<li role="presentation"><a id="tab-link-members" data-toggle="tab" href="#members_tab">Members</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="domains_tab">
<table id="domains" class="table table-striped">
<thead>
<tr>
<th>Domain</th>
<th>Serial</th>
<th>Group</th>
<th>State</th>
<th>Slave</th>
<th>DNSSEC</th>
<th class="no-order-and-search">Controls</th>
</tr>
</thead>
<tbody>
<% @domains.group_by(&:group).each do |group, domains| %>
<% domains.each do |domain| %>
<tr class="group-<%= group.id =%>">
<td><%= link_to domain.name, domain %></td>
<td><%= domain.serial %></td>
<td><%= link_to group.name, group_path(group) %></td>
<td><%= human_state(domain.state) %></td>
<td><%= domain.slave? ? domain.master : '-' %></td>
<td><%= domain.dnssec? ? 'secure' : '-' %></td>
<td>
<%= link_to_edit edit_domain_path(domain) %>
+ <% if @optouts.include? domain.id %>
+ <%= link_to_unmute user_domain_unmute_path(current_user, domain), method: :put %>
+ <% else %>
+ <%= link_to_mute user_domain_mute_path(current_user, domain), method: :put %>
+ <% end %>
<%= link_to_destroy domain, method: :delete, data: { confirm: 'Are you sure?' } if domain.can_remove? %>
<%= link_to_full_destroy full_destroy_domain_path(domain),
method: :delete, data: { confirm: 'Are you sure?' } if domain.can_remove? && domain.dnssec? %>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<p>
<% if current_user.memberships.any? %>
<%= link_to 'Add Domain', new_domain_path(group_id: @group.id), class: 'btn btn-primary' %>
<% else %>
<%= link_to 'Add Domain', new_domain_path(group_id: @group.id), class: 'btn btn-primary disabled' %>
<% end %>
</p>
</div>
<div role="tabpanel" class="tab-pane" id="members_tab">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Member</th>
<th>Controls</th>
</tr>
</thead>
<tbody>
<% @group.memberships.includes(:user).each do |membership| %>
<tr>
<td><%= membership.user.email %><%= " (you)" if current_user == membership.user %></td>
<td>
<%= link_to_destroy destroy_member_group_path(@group, membership.user_id), method: :delete %>
</td>
</tr>
<% end %>
</tbody>
</table>
<p>
<%= bootstrap_form_tag(url: create_member_group_path(@group), layout: :inline) do |f| %>
<%= f.text_field :email, prepend: 'Add Member', hide_label: true, id: 'js-search-member', data: { group: @group.id } %>
<%= f.submit 'Add', class: 'btn btn-primary' %>
<% end %>
</p>
</div>
</div>
diff --git a/config/routes.rb b/config/routes.rb
index cb736ad..6c277dc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,82 +1,86 @@
Rails.application.routes.draw do
# Override devise user removal
devise_scope :users do
delete :users, to: redirect('/')
end
devise_for :users
get '/auth/saml', to: 'auth#saml'
root to: redirect('/domains')
resources :users, only: [] do
get :token, to: 'users#token', on: :member
post :generate_token, to: 'users#generate_token', on: :member
+ resources :domains, only: [] do
+ put :mute, to: 'users#mute'
+ put :unmute, to: 'users#unmute'
+ end
end
resources :groups, only: [:show] do
get :search_member,
to: 'groups#search_member', on: :member
post :members,
to: 'groups#create_member', as: :create_member, on: :member
delete 'member/:user_id',
to: 'groups#destroy_member', as: :destroy_member, on: :member
end
resources :domains do
get :edit_dnssec, to: 'domains#edit_dnssec', on: :member
delete :full_destroy, to: 'domains#full_destroy', on: :member
resources :records, except: [:index, :show] do
# Reuse records#update instead of introducing new controller actions
#
# rubocop:disable Style/AlignHash
put :disable, to: 'records#update', on: :member,
defaults: { record: { disabled: true } }
put :enable, to: 'records#update', on: :member,
defaults: { record: { disabled: false } }
put :editable, to: 'records#editable', on: :collection
post :valid, to: 'records#valid', on: :collection
post :bulk, to: 'records#bulk', on: :collection
# rubocop:enable Style/AlignHash
end
end
get '/records/search', to: 'records#search'
# Admin
namespace :admin do
root to: redirect('/admin/groups')
resources :groups, except: [:show]
resources :jobs, only: [:index, :destroy] do
put :done, to: 'jobs#update', on: :member,
defaults: { job: { status: 1 } }
put :pending, to: 'jobs#update', on: :member,
defaults: { job: { status: 0 } }
get '/type/:category', to: 'jobs#index', on: :collection,
constraints: proc { |req| ['completed', 'pending'].include?(req.params[:category]) }
end
resources :users, only: [:destroy] do
get :orphans, to: 'users#orphans', on: :collection
put :update_groups, to: 'users#update_groups', on: :collection
end
end
# API
scope '/api' do
get :ping, to: 'api#ping'
get :whoami, to: 'api#whoami'
get '/domain/:domain/list', to: 'api#list', constraints: { domain: /[^\/]+/}
post '/domain/:domain/bulk', to: 'api#bulk', constraints: { domain: /[^\/]+/}
get :domains, to: 'api#domains'
end if WebDNS.settings[:api]
# Private
put 'private/replace_ds', to: 'private#replace_ds'
put 'private/trigger_event', to: 'private#trigger_event'
get 'private/zones', to: 'private#zones'
get 'help/api', to: 'help#api'
end
Event Timeline
Log In to Comment