Commit 9ce0dc764fd5347f985bb36017e4468d93d3f9b8
Exists in
master
and in
1 other branch
Merge pull request #289
Showing
10 changed files
with
182 additions
and
34 deletions
Show diff stats
app/assets/javascripts/apps.show.js
| 1 | $(function() { | 1 | $(function() { |
| 2 | + | ||
| 2 | $("#watchers_toggle").click(function() { | 3 | $("#watchers_toggle").click(function() { |
| 3 | $("#watchers_div").slideToggle("slow"); | 4 | $("#watchers_div").slideToggle("slow"); |
| 4 | }); | 5 | }); |
| 6 | + | ||
| 5 | $("#repository_toggle").click(function() { | 7 | $("#repository_toggle").click(function() { |
| 6 | $("#repository_div").slideToggle("slow"); | 8 | $("#repository_div").slideToggle("slow"); |
| 7 | }); | 9 | }); |
| 10 | + | ||
| 8 | $("#deploys_toggle").click(function() { | 11 | $("#deploys_toggle").click(function() { |
| 9 | $("#deploys_div").slideToggle("slow"); | 12 | $("#deploys_div").slideToggle("slow"); |
| 10 | }); | 13 | }); |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +class WatchersController < ApplicationController | ||
| 2 | + respond_to :html | ||
| 3 | + | ||
| 4 | + expose(:app) do | ||
| 5 | + App.find(params[:app_id]) | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + expose(:watcher) do | ||
| 9 | + app.watchers.where(:user_id => params[:id]).first | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + before_filter :require_watcher_edit_priviledges, :only => [:destroy] | ||
| 13 | + | ||
| 14 | + def destroy | ||
| 15 | + app.watchers.delete(watcher) | ||
| 16 | + flash[:success] = "That's sad. #{watcher.label} is no longer watcher." | ||
| 17 | + redirect_to root_path | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + private | ||
| 21 | + | ||
| 22 | + def require_watcher_edit_priviledges | ||
| 23 | + redirect_to(root_path) unless current_user == watcher.user || current_user.admin? | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | +end | ||
| 27 | + |
app/views/apps/show.html.haml
| 1 | - content_for :title, app.name | 1 | - content_for :title, app.name |
| 2 | - content_for :head do | 2 | - content_for :head do |
| 3 | - = auto_discovery_link_tag :atom, app_path(app, User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices for #{app.name} at #{request.host}" | 3 | + = auto_discovery_link_tag :atom, app_path(app, User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => t('.atom_title', :name => app.name, :host => request.host) |
| 4 | - content_for :meta do | 4 | - content_for :meta do |
| 5 | - %strong Errors Caught: | 5 | + %strong=t('.errors_caught') |
| 6 | = app.problems.count | 6 | = app.problems.count |
| 7 | - %strong Deploy Count: | 7 | + %strong=t('.deploy_count') |
| 8 | = app.deploys.count | 8 | = app.deploys.count |
| 9 | - %strong API Key: | 9 | + %strong=t('.api_key') |
| 10 | = app.api_key | 10 | = app.api_key |
| 11 | - content_for :action_bar do | 11 | - content_for :action_bar do |
| 12 | - if current_user.admin? | 12 | - if current_user.admin? |
| 13 | - = link_to 'edit', edit_app_path(app), :class => 'button' | 13 | + = link_to t('.edit'), edit_app_path(app), :class => 'button' |
| 14 | - if all_errs | 14 | - if all_errs |
| 15 | - = link_to 'unresolved errs', app_path(app), :class => 'button' | 15 | + = link_to t('.unresolved_errs'), app_path(app), :class => 'button' |
| 16 | - else | 16 | - else |
| 17 | - = link_to 'all errs', app_path(app, :all_errs => true), :class => 'button' | 17 | + = link_to t('.all_errs'), app_path(app, :all_errs => true), :class => 'button' |
| 18 | 18 | ||
| 19 | + - if current_user.watching?(app) | ||
| 20 | + = link_to t('.unwatch'), app_watcher_path({:app_id => app, :id => current_user.id}), :method => :delete, :class => 'button', :confirm => t('.are_you_sure') | ||
| 19 | %h3#watchers_toggle | 21 | %h3#watchers_toggle |
| 20 | - Watchers | ||
| 21 | - %span.click_span (show/hide) | 22 | + =t('.watchers') |
| 23 | + %span.click_span=t('.show_hide') | ||
| 22 | #watchers_div | 24 | #watchers_div |
| 23 | - if app.notify_all_users | 25 | - if app.notify_all_users |
| 24 | %table.watchers | 26 | %table.watchers |
| 25 | %thead | 27 | %thead |
| 26 | %tr | 28 | %tr |
| 27 | - %th All users will be notified when something happens. | 29 | + %th=t('.all_users_notified') |
| 28 | - else | 30 | - else |
| 29 | %table.watchers | 31 | %table.watchers |
| 30 | %thead | 32 | %thead |
| 31 | %tr | 33 | %tr |
| 32 | - %th User or Email | 34 | + %th=t('.user_or_email') |
| 33 | %tbody | 35 | %tbody |
| 34 | - app.watchers.each do |watcher| | 36 | - app.watchers.each do |watcher| |
| 35 | %tr | 37 | %tr |
| @@ -37,35 +39,35 @@ | @@ -37,35 +39,35 @@ | ||
| 37 | - if app.watchers.none? | 39 | - if app.watchers.none? |
| 38 | %tr | 40 | %tr |
| 39 | %td | 41 | %td |
| 40 | - %em Sadly, no one is watching this app | 42 | + %em= t('.no_watcher') |
| 41 | 43 | ||
| 42 | - if app.github_repo? | 44 | - if app.github_repo? |
| 43 | %h3#repository_toggle | 45 | %h3#repository_toggle |
| 44 | - Repository | ||
| 45 | - %span.click_span (show/hide) | 46 | + =t('.repository') |
| 47 | + %span.click_span=t('.show_hide') | ||
| 46 | #repository_div | 48 | #repository_div |
| 47 | %table.repository | 49 | %table.repository |
| 48 | %thead | 50 | %thead |
| 49 | %tr | 51 | %tr |
| 50 | - %th GitHub Repo | 52 | + %th=t('.github_repo') |
| 51 | %tbody | 53 | %tbody |
| 52 | %tr | 54 | %tr |
| 53 | %td= link_to(app.github_repo, app.github_url, :target => '_blank') | 55 | %td= link_to(app.github_repo, app.github_url, :target => '_blank') |
| 54 | 56 | ||
| 55 | %h3#deploys_toggle | 57 | %h3#deploys_toggle |
| 56 | - Latest Deploys | ||
| 57 | - %span.click_span (show/hide) | 58 | + =t('.latest_deploys') |
| 59 | + %span.click_span=t('.show_hide') | ||
| 58 | #deploys_div | 60 | #deploys_div |
| 59 | - if deploys.any? | 61 | - if deploys.any? |
| 60 | %table.deploys | 62 | %table.deploys |
| 61 | %thead | 63 | %thead |
| 62 | %tr | 64 | %tr |
| 63 | - %th When | ||
| 64 | - %th Environment | ||
| 65 | - %th Who | ||
| 66 | - %th Message | ||
| 67 | - %th Repository | ||
| 68 | - %th Revision | 65 | + %th=t('.when') |
| 66 | + %th=t('.environment') | ||
| 67 | + %th=t('.who') | ||
| 68 | + %th=t('.message') | ||
| 69 | + %th=t('.repository') | ||
| 70 | + %th=t('.revision') | ||
| 69 | 71 | ||
| 70 | %tbody | 72 | %tbody |
| 71 | - deploys.each do |deploy| | 73 | - deploys.each do |deploy| |
| @@ -76,20 +78,20 @@ | @@ -76,20 +78,20 @@ | ||
| 76 | %td.message #{deploy.message} | 78 | %td.message #{deploy.message} |
| 77 | %td.repository #{deploy.repository} | 79 | %td.repository #{deploy.repository} |
| 78 | %td.revision #{deploy.short_revision} | 80 | %td.revision #{deploy.short_revision} |
| 79 | - = link_to "All Deploys (#{app.deploys.count})", app_deploys_path(app), :class => 'button' | 81 | + = link_to t('.all_deploys', :count => app.deploys.count), app_deploys_path(app), :class => 'button' |
| 80 | - else | 82 | - else |
| 81 | - %h3 No deploys | 83 | + %h3=t('.no_deploys') |
| 82 | 84 | ||
| 83 | - if app.problems.any? | 85 | - if app.problems.any? |
| 84 | - %h3.clear Errors | 86 | + %h3.clear=t('.errors') |
| 85 | %section | 87 | %section |
| 86 | = form_tag search_problems_path(:all_errs => all_errs, :app_id => app.id), :method => :get, :remote => true do | 88 | = form_tag search_problems_path(:all_errs => all_errs, :app_id => app.id), :method => :get, :remote => true do |
| 87 | - = text_field_tag :search, params[:search], :placeholder => 'Search for issues' | 89 | + = text_field_tag :search, params[:search], :placeholder => t('.search_placeholder') |
| 88 | %br | 90 | %br |
| 89 | %section | 91 | %section |
| 90 | .problem_table{:id => 'problem_table'} | 92 | .problem_table{:id => 'problem_table'} |
| 91 | = render 'problems/table', :problems => problems | 93 | = render 'problems/table', :problems => problems |
| 92 | - else | 94 | - else |
| 93 | - %h3.clear No errs have been caught yet, make sure you setup your app | 95 | + %h3.clear=t('.no_error_yet') |
| 94 | = render 'configuration_instructions', :app => app | 96 | = render 'configuration_instructions', :app => app |
| 95 | 97 |
app/views/devise/sessions/new.html.haml
| @@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
| 22 | = f.label :remember_me | 22 | = f.label :remember_me |
| 23 | 23 | ||
| 24 | %div.buttons | 24 | %div.buttons |
| 25 | - %button{:type => 'submit', :class => 'sign_in'}= "Sign in" | 25 | + %button{:type => 'submit', :class => 'sign_in'}=t('.sign_in') |
| 26 | 26 | ||
| 27 | :javascript | 27 | :javascript |
| 28 | $('a#forgot_password').click(function(){ | 28 | $('a#forgot_password').click(function(){ |
config/locales/en.yml
| @@ -65,6 +65,9 @@ en: | @@ -65,6 +65,9 @@ en: | ||
| 65 | omniauth_callbacks: | 65 | omniauth_callbacks: |
| 66 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." | 66 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." |
| 67 | success: "Successfully authenticated from %{kind} account." | 67 | success: "Successfully authenticated from %{kind} account." |
| 68 | + sessions: | ||
| 69 | + new: | ||
| 70 | + sign_in: "Sign in" | ||
| 68 | 71 | ||
| 69 | apps: | 72 | apps: |
| 70 | index: | 73 | index: |
| @@ -78,3 +81,32 @@ en: | @@ -78,3 +81,32 @@ en: | ||
| 78 | new_app: Add a New App | 81 | new_app: Add a New App |
| 79 | no_apps: 'No apps here.' | 82 | no_apps: 'No apps here.' |
| 80 | click_to_create: 'Click here to create your first one' | 83 | click_to_create: 'Click here to create your first one' |
| 84 | + show: | ||
| 85 | + all_deploys: "All Deploys (%{count})" | ||
| 86 | + all_errs: all errs | ||
| 87 | + all_users_notified: "All users will be notified when something happens." | ||
| 88 | + api_key: "API Key:" | ||
| 89 | + are_you_sure: 'Are you sure?' | ||
| 90 | + atom_title: "Errbit notices for %{name} at %{host}" | ||
| 91 | + deploy_count: "Deploy Count:" | ||
| 92 | + edit: edit | ||
| 93 | + environment: Environment | ||
| 94 | + errors: Errors | ||
| 95 | + errors_caught: "Errors Caught:" | ||
| 96 | + github_repo: GitHub Repo | ||
| 97 | + latest_deploys: Latest Deploys | ||
| 98 | + message: Message | ||
| 99 | + no_deploys: No deploys | ||
| 100 | + no_error_yet: "No errs have been caught yet, make sure you setup your app" | ||
| 101 | + no_watcher: "Sadly, no one is watching this app" | ||
| 102 | + repository: Repository | ||
| 103 | + repository: Repository | ||
| 104 | + revision: Revision | ||
| 105 | + search_placeholder: 'Search for issues' | ||
| 106 | + show_hide: "(show/hide)" | ||
| 107 | + unresolved_errs: unresolved errs | ||
| 108 | + unwatch: unwatch | ||
| 109 | + user_or_email: User or Email | ||
| 110 | + watchers: Watchers | ||
| 111 | + when: When | ||
| 112 | + who: Who |
config/routes.rb
| @@ -37,8 +37,8 @@ Errbit::Application.routes.draw do | @@ -37,8 +37,8 @@ Errbit::Application.routes.draw do | ||
| 37 | delete :unlink_issue | 37 | delete :unlink_issue |
| 38 | end | 38 | end |
| 39 | end | 39 | end |
| 40 | - | ||
| 41 | resources :deploys, :only => [:index] | 40 | resources :deploys, :only => [:index] |
| 41 | + resources :watchers, :only => [:destroy] | ||
| 42 | end | 42 | end |
| 43 | 43 | ||
| 44 | namespace :api do | 44 | namespace :api do |
spec/acceptance/sign_in_with_github_spec.rb
| 1 | require 'acceptance/acceptance_helper' | 1 | require 'acceptance/acceptance_helper' |
| 2 | 2 | ||
| 3 | feature 'Sign in with GitHub' do | 3 | feature 'Sign in with GitHub' do |
| 4 | + | ||
| 4 | background do | 5 | background do |
| 5 | Errbit::Config.stub(:github_authentication) { true } | 6 | Errbit::Config.stub(:github_authentication) { true } |
| 6 | Fabricate(:user, :github_login => 'nashby') | 7 | Fabricate(:user, :github_login => 'nashby') |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +require 'acceptance/acceptance_helper' | ||
| 2 | + | ||
| 3 | +feature 'A user can watch and unwatch an application' do | ||
| 4 | + | ||
| 5 | + let!(:app) { Fabricate(:app) } | ||
| 6 | + let!(:user) do | ||
| 7 | + user = Fabricate(:user) | ||
| 8 | + app.watchers.create!( | ||
| 9 | + :user_id => user.id | ||
| 10 | + ) | ||
| 11 | + user.reload | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + scenario 'log in watch a project and unwatch it' do | ||
| 15 | + visit '/' | ||
| 16 | + fill_in :user_email, :with => user.email | ||
| 17 | + fill_in :user_password, :with => 'password' | ||
| 18 | + click_on I18n.t('devise.sessions.new.sign_in') | ||
| 19 | + click_on I18n.t('apps.show.unwatch') | ||
| 20 | + expect(page).to have_content(I18n.t('apps.index.no_apps') | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | +end |
| @@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe WatchersController do | ||
| 4 | + let(:app) do | ||
| 5 | + a = Fabricate(:app) | ||
| 6 | + Fabricate(:user_watcher, :app => a) | ||
| 7 | + a | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + describe "DELETE /apps/:app_id/watchers/:id/destroy" do | ||
| 11 | + | ||
| 12 | + context "with admin user" do | ||
| 13 | + before(:each) do | ||
| 14 | + sign_in Fabricate(:admin) | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + context "successful watcher deletion" do | ||
| 18 | + let(:problem) { Fabricate(:problem_with_comments) } | ||
| 19 | + let(:watcher) { app.watchers.first } | ||
| 20 | + | ||
| 21 | + before(:each) do | ||
| 22 | + delete :destroy, :app_id => app.id, :id => watcher.user.id.to_s | ||
| 23 | + problem.reload | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + it "should delete the watcher" do | ||
| 27 | + app.watchers.detect{|w| w.id.to_s == watcher.id }.should == nil | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + it "should redirect to index page" do | ||
| 31 | + response.should redirect_to(root_path) | ||
| 32 | + end | ||
| 33 | + end | ||
| 34 | + end | ||
| 35 | + end | ||
| 36 | +end |
spec/views/apps/show.html.haml_spec.rb
| @@ -2,17 +2,20 @@ require 'spec_helper' | @@ -2,17 +2,20 @@ require 'spec_helper' | ||
| 2 | 2 | ||
| 3 | describe "apps/show.html.haml" do | 3 | describe "apps/show.html.haml" do |
| 4 | let(:app) { stub_model(App) } | 4 | let(:app) { stub_model(App) } |
| 5 | + let(:user) { stub_model(User) } | ||
| 6 | + | ||
| 7 | + let(:action_bar) do | ||
| 8 | + view.content_for(:action_bar) | ||
| 9 | + end | ||
| 10 | + | ||
| 5 | before do | 11 | before do |
| 6 | view.stub(:app).and_return(app) | 12 | view.stub(:app).and_return(app) |
| 7 | view.stub(:all_errs).and_return(false) | 13 | view.stub(:all_errs).and_return(false) |
| 8 | view.stub(:deploys).and_return([]) | 14 | view.stub(:deploys).and_return([]) |
| 9 | - controller.stub(:current_user) { stub_model(User) } | 15 | + controller.stub(:current_user) { user } |
| 10 | end | 16 | end |
| 11 | 17 | ||
| 12 | describe "content_for :action_bar" do | 18 | describe "content_for :action_bar" do |
| 13 | - def action_bar | ||
| 14 | - view.content_for(:action_bar) | ||
| 15 | - end | ||
| 16 | 19 | ||
| 17 | it "should confirm the 'cancel' link" do | 20 | it "should confirm the 'cancel' link" do |
| 18 | render | 21 | render |
| @@ -28,5 +31,26 @@ describe "apps/show.html.haml" do | @@ -28,5 +31,26 @@ describe "apps/show.html.haml" do | ||
| 28 | rendered.should match(/No errs have been/) | 31 | rendered.should match(/No errs have been/) |
| 29 | end | 32 | end |
| 30 | end | 33 | end |
| 34 | + | ||
| 35 | + context "with user watch application" do | ||
| 36 | + before do | ||
| 37 | + user.stub(:watching?).with(app).and_return(true) | ||
| 38 | + end | ||
| 39 | + it 'see the unwatch button' do | ||
| 40 | + render | ||
| 41 | + expect(action_bar).to include(I18n.t('apps.show.unwatch')) | ||
| 42 | + end | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + context "with user not watch application" do | ||
| 46 | + before do | ||
| 47 | + user.stub(:watching?).with(app).and_return(false) | ||
| 48 | + end | ||
| 49 | + it 'not see the unwatch button' do | ||
| 50 | + render | ||
| 51 | + expect(action_bar).to_not include(I18n.t('apps.show.unwatch')) | ||
| 52 | + end | ||
| 53 | + end | ||
| 54 | + | ||
| 31 | end | 55 | end |
| 32 | 56 |