diff options
| -rw-r--r-- | app/assets/stylesheets/leap.scss | 21 | ||||
| -rw-r--r-- | app/controllers/identities_controller.rb | 28 | ||||
| -rw-r--r-- | app/helpers/link_helper.rb | 25 | ||||
| -rw-r--r-- | app/helpers/table_helper.rb | 13 | ||||
| -rw-r--r-- | app/models/identity.rb | 20 | ||||
| -rw-r--r-- | app/views/common/_action.html.haml | 2 | ||||
| -rw-r--r-- | app/views/common/_navigation_item.html.haml | 3 | ||||
| -rw-r--r-- | app/views/common/_table.html.haml | 18 | ||||
| -rw-r--r-- | app/views/identities/_identity.html.haml | 7 | ||||
| -rw-r--r-- | app/views/identities/index.html.haml | 4 | ||||
| -rw-r--r-- | app/views/layouts/_admin_navigation_item.html.haml | 3 | ||||
| -rw-r--r-- | app/views/layouts/_header.html.haml | 6 | ||||
| -rw-r--r-- | config/locales/users.en.yml | 7 | ||||
| -rw-r--r-- | config/routes.rb | 2 | ||||
| -rw-r--r-- | engines/support/app/views/tickets/index.html.haml | 16 | ||||
| -rw-r--r-- | test/factories.rb | 6 | ||||
| -rw-r--r-- | test/functional/identities_controller_test.rb | 42 | ||||
| -rw-r--r-- | test/integration/browser/admin_test.rb | 24 | ||||
| -rw-r--r-- | test/support/browser_integration_test.rb | 2 | 
19 files changed, 227 insertions, 22 deletions
diff --git a/app/assets/stylesheets/leap.scss b/app/assets/stylesheets/leap.scss index 77104e5..3b7075c 100644 --- a/app/assets/stylesheets/leap.scss +++ b/app/assets/stylesheets/leap.scss @@ -145,6 +145,27 @@ input, textarea {  }  // +// IDENTITIES +// + +// Color code for the identity labels +.identity{ +  &.main_email .label { +    @extend .label-info +  } +  &.alias .label { +    @extend .label-success +  } +  &.forward .label { +    @extend .label-warning +  } +  &.disabled .label { +    @extend .label-default +  } +} + + +//  // BORING DEFAULT MASTHEAD  // diff --git a/app/controllers/identities_controller.rb b/app/controllers/identities_controller.rb new file mode 100644 index 0000000..624e38a --- /dev/null +++ b/app/controllers/identities_controller.rb @@ -0,0 +1,28 @@ +class IdentitiesController < ApplicationController + +  before_filter :require_login +  before_filter :require_admin +  before_filter :fetch_identity, only: :destroy +  before_filter :protect_main_email, only: :destroy + +  def index +    @identities = Identity.all +  end + +  def destroy +    @identity.destroy +    redirect_to identities_path +  end + +  protected +  def fetch_identity +    @identity = Identity.find(params[:id]) +  end + +  def protect_main_email +    if @identity.status == :main_email +      flash[:error] = "You cannot destroy the main email. Remove or Rename the user instead." +      redirect_to identities_path +    end +  end +end diff --git a/app/helpers/link_helper.rb b/app/helpers/link_helper.rb index 55e392b..ddb063e 100644 --- a/app/helpers/link_helper.rb +++ b/app/helpers/link_helper.rb @@ -1,5 +1,30 @@  module LinkHelper +  Action = Struct.new(:target, :verb, :options) do +    def to_partial_path; 'common/action'; end +    def label; options[:label]; end +    def class; verb; end +    def url +      case verb +      when :show, :destroy then target +      when :edit, :new then [verb, target] +      end +    end + +    def html_options +      if verb == :destroy +        {method: :delete} +      end +    end +  end + +  def actions(target) +    target.actions.map do |action| +      Action.new target, action, +        label: t(".#{action}", cascade: true) +    end +  end +    #    # markup for bootstrap button    # diff --git a/app/helpers/table_helper.rb b/app/helpers/table_helper.rb new file mode 100644 index 0000000..16a7019 --- /dev/null +++ b/app/helpers/table_helper.rb @@ -0,0 +1,13 @@ +module TableHelper + +  # we do the translation here so the .key lookup is relative +  # to the partial the helper was called from. +  def table(content, columns) +    render 'common/table', +      content: content, +      columns: columns, +      headers: columns.map {|h| t(".#{h}", cascading: true) }, +      none: t('.none', cascading: true) +  end + +end diff --git a/app/models/identity.rb b/app/models/identity.rb index e7b5785..e09d7f2 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -95,6 +95,18 @@ class Identity < CouchRest::Model::Base      }    end +  def status +    return :blocked if disabled? +    case destination +    when address +      :main_email +    when /@#{APP_CONFIG[:domain]}\Z/i, +      :alias +    else +      :forward +    end +  end +    def enabled?      self.user_id    end @@ -103,6 +115,14 @@ class Identity < CouchRest::Model::Base      !enabled?    end +  def actions +    if enabled? +      [] # [:show, :edit] +    else +      [:destroy] +    end +  end +    def disable      self.destination = nil      self.user_id = nil diff --git a/app/views/common/_action.html.haml b/app/views/common/_action.html.haml new file mode 100644 index 0000000..71ffd96 --- /dev/null +++ b/app/views/common/_action.html.haml @@ -0,0 +1,2 @@ +%li.action{class: action.class} +  =link_to action.label, action.url, action.html_options diff --git a/app/views/common/_navigation_item.html.haml b/app/views/common/_navigation_item.html.haml new file mode 100644 index 0000000..02c54c8 --- /dev/null +++ b/app/views/common/_navigation_item.html.haml @@ -0,0 +1,3 @@ +- item = navigation_item.to_s +%li{:class => ("active" if controller?(item))} +  = link_to t(".#{item}", cascade: true), polymorphic_url(item) diff --git a/app/views/common/_table.html.haml b/app/views/common/_table.html.haml new file mode 100644 index 0000000..3c6bf2e --- /dev/null +++ b/app/views/common/_table.html.haml @@ -0,0 +1,18 @@ +-#  +-# A simple table with headers, content and a message if there is no +-# content. +-# You can use the table helper method to translate the headers and +-# error message. +-# +%table.table.table-striped.table-bordered +  %thead +    %tr +      - headers.each do |header| +        %th= header +  %tbody +    - if content.any? +      = render content.all +    - else +      %tr +        %td{:colspan=>headers.count}= none + diff --git a/app/views/identities/_identity.html.haml b/app/views/identities/_identity.html.haml new file mode 100644 index 0000000..7c4aeda --- /dev/null +++ b/app/views/identities/_identity.html.haml @@ -0,0 +1,7 @@ +%tr[identity]{class: identity.status} +  %td +    %span.label= t(".#{identity.status}", cascading: true) +  %td= identity.login +  %td= identity.destination +  %td +    %ul.list-inline= render actions(identity) diff --git a/app/views/identities/index.html.haml b/app/views/identities/index.html.haml new file mode 100644 index 0000000..cabcccd --- /dev/null +++ b/app/views/identities/index.html.haml @@ -0,0 +1,4 @@ +-@show_navigation = false + += table @identities, %w(type username destination actions) +-# = paginate @identities diff --git a/app/views/layouts/_admin_navigation_item.html.haml b/app/views/layouts/_admin_navigation_item.html.haml new file mode 100644 index 0000000..8dd8a39 --- /dev/null +++ b/app/views/layouts/_admin_navigation_item.html.haml @@ -0,0 +1,3 @@ +- item = admin_navigation_item.to_s +%li{:class => ("active" if controller?(item))} +  = link_to t(".#{item}", cascade: true), path_for(item) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index e827f60..ff14af9 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -1,10 +1,6 @@  - if admin?    %ul.nav.nav-tabs -    = # this navigation isn't quite right. also, we will want to active for an identity controller once it is added. -    %li{:class => ("active" if controller?('users', 'overviews') || params[:user_id])} -      = link_to t(".users"), users_path -    %li{:class => ("active" if controller?('tickets') && !params[:user_id])} -      = link_to t(".tickets", cascade: true), tickets_path +    = render partial: 'common/navigation_item', collection: [:users, :identities, :tickets]      %li        = link_to t(:logout), logout_path, :method => :delete  - if @user && @show_navigation diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml index f0fcb3d..bebf1cf 100644 --- a/config/locales/users.en.yml +++ b/config/locales/users.en.yml @@ -1,6 +1,9 @@  en: -  layouts: -    users: "Users" +  common: +    navigation_item: +      users: "Users" +      identities: "Usernames" +      tickets: "Tickets"    user_control_panel: "user control panel"    account_settings: "Account Settings"    username: "Username" diff --git a/config/routes.rb b/config/routes.rb index 4ccfe62..468e14e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -48,6 +48,8 @@ LeapWeb::Application.routes.draw do        post 'deactivate', on: :member        post 'enable', on: :member      end + +    resources :identities, :only => [:index, :destroy]    end    get "/.well-known/host-meta" => 'webfinger#host_meta' diff --git a/engines/support/app/views/tickets/index.html.haml b/engines/support/app/views/tickets/index.html.haml index 526cd6d..d107ce2 100644 --- a/engines/support/app/views/tickets/index.html.haml +++ b/engines/support/app/views/tickets/index.html.haml @@ -1,19 +1,5 @@  - @show_navigation = params[:user_id].present?  = render 'tickets/tabs' - -%table.table.table-striped.table-bordered -  %thead -    %tr -      %th= t(".subject") -      %th= t(".created") -      %th= t(".updated") -      %th= t(".voices") -  %tbody -    - if @tickets.any? -      = render @tickets.all -    - else -      %tr -        %td{:colspan=>4}= t(".none") - += table @tickets, %w(subject created updated voices)  = paginate @tickets diff --git a/test/factories.rb b/test/factories.rb index bebda5c..a96d48c 100644 --- a/test/factories.rb +++ b/test/factories.rb @@ -29,6 +29,12 @@ FactoryGirl.define do    end +  # Identities can have a lot of different purposes - alias, forward, ... +  # So far this is a blocked handle only. +  factory :identity do +    address {Faker::Internet.user_name + '@' + APP_CONFIG[:domain]} +  end +    factory :token do      user    end diff --git a/test/functional/identities_controller_test.rb b/test/functional/identities_controller_test.rb new file mode 100644 index 0000000..fcdeaa2 --- /dev/null +++ b/test/functional/identities_controller_test.rb @@ -0,0 +1,42 @@ +require 'test_helper' + +class IdentitiesControllerTest < ActionController::TestCase + +  test "admin can list active and blocked ids" do +    login :is_admin? => true +    get :index +    assert_response :success +    assert ids = assigns(:identities) +  end + +  test "non-admin can't list usernames" do +    login +    get :index +    assert_access_denied +  end + +  test "requires login" do +    get :index +    assert_login_required +  end + +  test "admin can unblock username" do +    # an identity without user_id and destination is a blocked handle +    identity = FactoryGirl.create :identity +    login :is_admin? => true +    delete :destroy, id: identity.id +    assert_response :redirect +    assert_nil Identity.find(identity.id) +  end + +  test "admin cannot remove main identity" do +    user = FactoryGirl.create :user +    identity = FactoryGirl.create :identity, +      Identity.attributes_from_user(user) +    login :is_admin? => true +    delete :destroy, id: identity.id +    assert_response :redirect +    assert_equal identity, Identity.find(identity.id) +  end + +end diff --git a/test/integration/browser/admin_test.rb b/test/integration/browser/admin_test.rb new file mode 100644 index 0000000..2d3f988 --- /dev/null +++ b/test/integration/browser/admin_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class AdminTest < BrowserIntegrationTest + +  test "clear blocked handle" do +    id = FactoryGirl.create :identity +    submit_signup(id.login) +    assert page.has_content?('has already been taken') +    login +    with_config admins: [@user.login] do +      visit '/' +      click_on "Usernames" +      within "##{dom_id(id)}" do +        assert page.has_content? id.login +        click_on "Destroy" +      end +      assert page.has_no_content? id.login +      click_on 'Log Out' +    end +    submit_signup(id.login) +    assert page.has_content?("Welcome #{id.login}") +    click_on 'Log Out' +  end +end diff --git a/test/support/browser_integration_test.rb b/test/support/browser_integration_test.rb index 4fec59f..c01b8a1 100644 --- a/test/support/browser_integration_test.rb +++ b/test/support/browser_integration_test.rb @@ -5,6 +5,8 @@  #  class BrowserIntegrationTest < ActionDispatch::IntegrationTest +  # let's use dom_id inorder to identify sections +  include ActionController::RecordIdentifier    CONFIG_RU = (Rails.root + 'config.ru').to_s    OUTER_APP = Rack::Builder.parse_file(CONFIG_RU).first  | 
