diff --git a/Gemfile b/Gemfile
index 95fd046295..db6cb7f448 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,6 +45,7 @@ gem 'rack-timeout-puma'
gem 'sidekiq'
gem 'ledermann-rails-settings'
gem 'pg_search'
+gem 'simple-navigation'
gem 'react-rails'
gem 'browserify-rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index cc7751ae19..c3ddf33fca 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -353,6 +353,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
redis (~> 3.2, >= 3.2.1)
+ simple-navigation (4.0.3)
+ activesupport (>= 2.3.2)
simple_form (3.2.1)
actionpack (> 4, < 5.1)
activemodel (> 4, < 5.1)
@@ -458,6 +460,7 @@ DEPENDENCIES
sass-rails (~> 5.0)
sdoc (~> 0.4.0)
sidekiq
+ simple-navigation
simple_form
simplecov
uglifier (>= 1.3.0)
diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/assets/javascripts/components/features/notifications/index.jsx
index 00feeece78..218196cfd3 100644
--- a/app/assets/javascripts/components/features/notifications/index.jsx
+++ b/app/assets/javascripts/components/features/notifications/index.jsx
@@ -26,6 +26,12 @@ const Notifications = React.createClass({
trackScroll: React.PropTypes.bool
},
+ getDefaultProps () {
+ return {
+ trackScroll: true
+ };
+ },
+
mixins: [PureRenderMixin],
componentWillMount () {
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
new file mode 100644
index 0000000000..6e4234d13d
--- /dev/null
+++ b/app/assets/stylesheets/admin.scss
@@ -0,0 +1,105 @@
+.admin-wrapper {
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ background: #1a1c23;
+ overflow-y: scroll;
+
+ .sidebar {
+ width: 240px;
+ position: fixed;
+ left: 0;
+ height: 100%;
+ background: #282c37;
+
+ .logo {
+ display: block;
+ margin: 40px auto;
+ width: 100px;
+ height: 100px;
+ }
+
+ ul {
+ list-style: none;
+
+ a {
+ display: block;
+ padding: 15px 25px;
+ color: rgba(255, 255, 255, 0.7);
+ text-decoration: none;
+ transition: all 200ms linear;
+
+ i.fa {
+ margin-right: 5px;
+ }
+
+ &:hover {
+ color: #fff;
+ background-color: darken(#282c37, 5%);
+ transition: all 100ms linear;
+ }
+
+ &.selected {
+ color: #fff;
+ background-color: #2b90d9;
+
+ &:hover {
+ background-color: lighten(#2b90d9, 5%);
+ }
+ }
+ }
+ }
+ }
+
+ .content {
+ margin-left: 240px;
+ padding: 15px;
+ }
+}
+
+.filters {
+ display: flex;
+ margin-bottom: 20px;
+ padding-left: 8px;
+
+ .filter-subset {
+ flex: 0 0 auto;
+ margin-right: 40px;
+
+ ul {
+ margin-top: 5px;
+ list-style: none;
+
+ li {
+ display: inline-block;
+ margin-right: 5px;
+ }
+ }
+
+ strong {
+ font-weight: 500;
+ text-transform: uppercase;
+ font-size: 12px;
+ }
+
+ a {
+ display: inline-block;
+ color: rgba(255, 255, 255, 0.7);
+ text-decoration: none;
+ text-transform: uppercase;
+ font-size: 12px;
+ font-weight: 500;
+ border-bottom: 2px solid #282c37;
+
+ &:hover {
+ color: #fff;
+ border-bottom: 2px solid lighten(#282c37, 5%);
+ }
+
+ &.selected {
+ color: #2b90d9;
+ border-bottom: 2px solid #2b90d9;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index bbbeafefe3..609b307264 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -235,3 +235,4 @@ body {
@import 'components';
@import 'about';
@import 'tables';
+@import 'admin';
diff --git a/app/assets/stylesheets/tables.scss b/app/assets/stylesheets/tables.scss
index b28b911912..a378707867 100644
--- a/app/assets/stylesheets/tables.scss
+++ b/app/assets/stylesheets/tables.scss
@@ -7,15 +7,15 @@
th, td {
padding: 8px;
- line-height: 1.42857143;
+ line-height: 18px;
vertical-align: top;
- border-top: 1px solid #ddd;
+ border-top: 1px solid #282c37;
text-align: left;
}
& > thead > tr > th {
vertical-align: bottom;
- border-bottom: 2px solid #ddd;
+ border-bottom: 2px solid #282c37;
border-top: 0;
font-weight: 500;
}
@@ -24,6 +24,10 @@
font-weight: 500;
}
+ & > tbody > tr:nth-child(odd) > td, & > tbody > tr:nth-child(odd) > th {
+ background: lighten(#1a1c23, 2%);
+ }
+
a {
color: #2b90d9;
text-decoration: underline;
@@ -38,20 +42,20 @@ samp {
font-family: 'Roboto Mono', monospace;
}
-.filters {
- list-style: none;
- margin-bottom: 20px;
+a.table-action-link {
+ text-decoration: none;
+ display: inline-block;
+ margin-right: 5px;
+ padding: 0 10px;
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
- li {
- display: inline-block;
+ &:hover {
+ color: #fff;
}
- a {
- color: #2b90d9;
- text-decoration: underline;
-
- &:hover {
- text-decoration: none;
- }
+ i.fa {
+ font-weight: 400;
+ margin-right: 5px;
}
}
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 55436d253b..95107b3dc5 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -4,7 +4,7 @@ class Admin::AccountsController < ApplicationController
before_action :require_admin!
before_action :set_account, except: :index
- layout 'public'
+ layout 'admin'
def index
@accounts = Account.alphabetic.paginate(page: params[:page], per_page: 40)
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
new file mode 100644
index 0000000000..e362957e73
--- /dev/null
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class Admin::DomainBlocksController < ApplicationController
+ before_action :require_admin!
+
+ layout 'admin'
+
+ def index
+ @blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
+ end
+
+ def create
+ end
+end
diff --git a/app/controllers/admin/pubsubhubbub_controller.rb b/app/controllers/admin/pubsubhubbub_controller.rb
index 7e6bc75ea3..b9e840ffe4 100644
--- a/app/controllers/admin/pubsubhubbub_controller.rb
+++ b/app/controllers/admin/pubsubhubbub_controller.rb
@@ -3,7 +3,7 @@
class Admin::PubsubhubbubController < ApplicationController
before_action :require_admin!
- layout 'public'
+ layout 'admin'
def index
@subscriptions = Subscription.order('id desc').includes(:account).paginate(page: params[:page], per_page: 40)
diff --git a/app/helpers/admin/accounts_helper.rb b/app/helpers/admin/accounts_helper.rb
index 5b9cbbacdc..57cd972fa1 100644
--- a/app/helpers/admin/accounts_helper.rb
+++ b/app/helpers/admin/accounts_helper.rb
@@ -1,2 +1,15 @@
+# frozen_string_literal: true
+
module Admin::AccountsHelper
+ def filter_params(more_params)
+ params.permit(:local, :remote, :by_domain, :silenced, :suspended, :recent).merge(more_params)
+ end
+
+ def filter_link_to(text, more_params)
+ link_to text, filter_params(more_params), class: params.merge(more_params).compact == params.compact ? 'selected' : ''
+ end
+
+ def table_link_to(icon, text, path)
+ link_to safe_join([fa_icon(icon), text]), path, class: 'table-action-link'
+ end
end
diff --git a/app/helpers/admin/domain_blocks_helper.rb b/app/helpers/admin/domain_blocks_helper.rb
new file mode 100644
index 0000000000..d66c8d5e14
--- /dev/null
+++ b/app/helpers/admin/domain_blocks_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module Admin::DomainBlocksHelper
+end
diff --git a/app/helpers/admin/pubsubhubbub_helper.rb b/app/helpers/admin/pubsubhubbub_helper.rb
index 41c874a62f..c2fc2e7da5 100644
--- a/app/helpers/admin/pubsubhubbub_helper.rb
+++ b/app/helpers/admin/pubsubhubbub_helper.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
module Admin::PubsubhubbubHelper
end
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index a2a3628d67..99c2af576f 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -1,9 +1,24 @@
-%ul.filters
- %li= link_to 'Local', admin_accounts_path(local: '1')
- %li= link_to 'Remote', admin_accounts_path(remote: '1')
- %li= link_to 'Silenced', admin_accounts_path(silenced: '1')
- %li= link_to 'Suspended', admin_accounts_path(suspended: '1')
- %li= link_to 'Most recent', admin_accounts_path(recent: '1')
+- content_for :page_title do
+ Accounts
+
+.filters
+ .filter-subset
+ %strong Location
+ %ul
+ %li= filter_link_to 'All', local: nil, remote: nil
+ %li= filter_link_to 'Local', local: '1', remote: nil
+ %li= filter_link_to 'Remote', remote: '1', local: nil
+ .filter-subset
+ %strong Moderation
+ %ul
+ %li= filter_link_to 'All', silenced: nil, suspended: nil
+ %li= filter_link_to 'Silenced', silenced: '1'
+ %li= filter_link_to 'Suspended', suspended: '1'
+ .filter-subset
+ %strong Order
+ %ul
+ %li= filter_link_to 'Alphabetic', recent: nil
+ %li= filter_link_to 'Most recent', recent: '1'
%table.table
%thead
@@ -38,6 +53,9 @@
%i.fa.fa-check
- else
%i.fa.fa-times
- %td= link_to 'Edit', admin_account_path(account.id)
+ %td
+ = table_link_to 'circle', 'Open in web', web_path("accounts/#{account.id}")
+ = table_link_to 'globe', 'Open public', TagManager.instance.url_for(account)
+ = table_link_to 'pencil', 'Edit', admin_account_path(account.id)
= will_paginate @accounts, pagination_options
diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml
new file mode 100644
index 0000000000..aedf163f71
--- /dev/null
+++ b/app/views/admin/domain_blocks/index.html.haml
@@ -0,0 +1,14 @@
+- content_for :page_title do
+ Domain Blocks
+
+%table.table
+ %thead
+ %tr
+ %th Domain
+ %tbody
+ - @blocks.each do |block|
+ %tr
+ %td
+ %samp= block.domain
+
+= will_paginate @blocks, pagination_options
diff --git a/app/views/admin/pubsubhubbub/index.html.haml b/app/views/admin/pubsubhubbub/index.html.haml
index ad8cf51983..cb11a502c9 100644
--- a/app/views/admin/pubsubhubbub/index.html.haml
+++ b/app/views/admin/pubsubhubbub/index.html.haml
@@ -1,3 +1,6 @@
+- content_for :page_title do
+ PubSubHubbub
+
%table.table
%thead
%tr
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
new file mode 100644
index 0000000000..2fc116f45b
--- /dev/null
+++ b/app/views/layouts/admin.html.haml
@@ -0,0 +1,11 @@
+- content_for :content do
+ .admin-wrapper
+ .sidebar
+ = link_to root_path do
+ = image_tag 'logo.png', class: 'logo'
+
+ = render_navigation
+ .content
+ = yield
+
+= render template: "layouts/application"
diff --git a/config/navigation.rb b/config/navigation.rb
new file mode 100644
index 0000000000..1b6615ed02
--- /dev/null
+++ b/config/navigation.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+SimpleNavigation::Configuration.run do |navigation|
+ navigation.items do |primary|
+ primary.item :accounts, safe_join([fa_icon('users fw'), 'Accounts']), admin_accounts_url
+ primary.item :pubsubhubbubs, safe_join([fa_icon('paper-plane-o fw'), 'PubSubHubbub']), admin_pubsubhubbub_index_url
+ primary.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url
+ primary.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url
+ primary.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index cde84d3bc8..2d70bdcea6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,8 +6,8 @@ Rails.application.routes.draw do
mount ActionCable.server, at: 'cable'
authenticate :user, lambda { |u| u.admin? } do
- mount Sidekiq::Web, at: 'sidekiq'
- mount PgHero::Engine, at: 'pghero'
+ mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
+ mount PgHero::Engine, at: 'pghero', as: :pghero
end
use_doorkeeper do
@@ -46,6 +46,7 @@ Rails.application.routes.draw do
namespace :admin do
resources :pubsubhubbub, only: [:index]
+ resources :domain_blocks, only: [:index, :create]
resources :accounts, only: [:index, :show, :update] do
member do
diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb
new file mode 100644
index 0000000000..9d8735ef24
--- /dev/null
+++ b/spec/controllers/admin/domain_blocks_controller_spec.rb
@@ -0,0 +1,14 @@
+require 'rails_helper'
+
+RSpec.describe Admin::DomainBlocksController, type: :controller do
+ before do
+ sign_in Fabricate(:user, admin: true), scope: :user
+ end
+
+ describe 'GET #index' do
+ it 'returns http success' do
+ get :index
+ expect(response).to have_http_status(:success)
+ end
+ end
+end
diff --git a/spec/helpers/admin/accounts_helper_spec.rb b/spec/helpers/admin/accounts_helper_spec.rb
index 92e29a2229..b91f258b3a 100644
--- a/spec/helpers/admin/accounts_helper_spec.rb
+++ b/spec/helpers/admin/accounts_helper_spec.rb
@@ -1,15 +1,5 @@
require 'rails_helper'
-# Specs in this file have access to a helper object that includes
-# the Admin::AccountsHelper. For example:
-#
-# describe Admin::AccountsHelper do
-# describe "string concat" do
-# it "concats two strings with spaces" do
-# expect(helper.concat_strings("this","that")).to eq("this that")
-# end
-# end
-# end
RSpec.describe Admin::AccountsHelper, type: :helper do
- pending "add some examples to (or delete) #{__FILE__}"
+
end
diff --git a/spec/helpers/admin/domain_blocks_helper_spec.rb b/spec/helpers/admin/domain_blocks_helper_spec.rb
new file mode 100644
index 0000000000..cc7ead84e3
--- /dev/null
+++ b/spec/helpers/admin/domain_blocks_helper_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Admin::DomainBlocksHelper, type: :helper do
+
+end
diff --git a/spec/helpers/admin/pubsubhubbub_helper_spec.rb b/spec/helpers/admin/pubsubhubbub_helper_spec.rb
index 6603e6dc0e..673236a7ec 100644
--- a/spec/helpers/admin/pubsubhubbub_helper_spec.rb
+++ b/spec/helpers/admin/pubsubhubbub_helper_spec.rb
@@ -1,15 +1,5 @@
require 'rails_helper'
-# Specs in this file have access to a helper object that includes
-# the Admin::PubsubhubbubHelper. For example:
-#
-# describe Admin::PubsubhubbubHelper do
-# describe "string concat" do
-# it "concats two strings with spaces" do
-# expect(helper.concat_strings("this","that")).to eq("this that")
-# end
-# end
-# end
RSpec.describe Admin::PubsubhubbubHelper, type: :helper do
- pending "add some examples to (or delete) #{__FILE__}"
+
end
diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb
index 927211938a..7b8259fa62 100644
--- a/spec/lib/formatter_spec.rb
+++ b/spec/lib/formatter_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Formatter do
end
it 'contains a link' do
- expect(subject).to match('google.com')
+ expect(subject).to match('google.com')
end
end