diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index ccffa26f3..9b72945e5 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -45,6 +45,7 @@ class PeopleController < ApplicationController def show @person = Person.where(:id => params[:id]).first @post_type = :all + @aspect = :profile @share_with = (params[:share_with] == 'true') if @person @@ -57,7 +58,9 @@ class PeopleController < ApplicationController @aspects_with_person = [] if @contact @aspects_with_person = @contact.aspects + @aspect_ids = @aspects_with_person.map(&:id) @contacts_of_contact = @contact.contacts + else @contact ||= Contact.new @contacts_of_contact = [] diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 09b5b81ac..8c0e0e185 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -9,6 +9,22 @@ class StatusMessagesController < ApplicationController respond_to :mobile respond_to :json, :only => :show + def new + @person = Person.find(params[:person_id]) + @aspect = :profile + @contact = current_user.contact_for(@person) + @aspects_with_person = [] + if @contact + @aspects_with_person = @contact.aspects + @aspect_ids = @aspects_with_person.map(&:id) + @contacts_of_contact = @contact.contacts + + render :layout => nil + else + redirect_to :back + end + end + def create params[:status_message][:aspect_ids] = params[:aspect_ids] diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8bd10648b..2fdc0125b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -31,17 +31,22 @@ module ApplicationHelper end end - def aspect_badges aspects + def aspect_badges aspects, opts = {} str = '' aspects.each do |aspect| - str << aspect_badge(aspect) + str << aspect_badge(aspect, opts) end str.html_safe end - def aspect_badge aspect + def aspect_badge aspect, opts = {} str = "" - str << link_for_aspect(aspect, 'data-guid' => aspect.id, :class => 'hard_aspect_link').html_safe + link = opts.delete(:link) + if !link + str << link_to(aspect.name, "#", 'data-guid' => aspect.id, :class => 'hard_aspect_link').html_safe + else + str << link_for_aspect(aspect, 'data-guid' => aspect.id, :class => 'hard_aspect_link').html_safe + end str << "" end diff --git a/app/views/aspects/show.html.haml b/app/views/aspects/show.html.haml index 78fb386cb..81149f9bb 100644 --- a/app/views/aspects/show.html.haml +++ b/app/views/aspects/show.html.haml @@ -22,7 +22,7 @@ = render 'aspects/edit_aspect_pane', :contacts => @all_contacts, :aspect => @aspect .span-15.last - = render 'shared/publisher', :aspect => @aspect + = render 'shared/publisher', :aspect => @aspect, :aspect_ids => @aspect_ids #main_stream.stream{:data => {:guids => @aspect.id}} = render 'shared/stream', :posts => @posts diff --git a/app/views/aspects/show.mobile.haml b/app/views/aspects/show.mobile.haml index 5ac59fe3b..91ddc6dba 100644 --- a/app/views/aspects/show.mobile.haml +++ b/app/views/aspects/show.mobile.haml @@ -2,7 +2,7 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -= render 'shared/publisher', :aspect => @aspect += render 'shared/publisher', :aspect => @aspect, :aspect_ids => @aspect_ids = render 'shared/stream', :posts => @posts diff --git a/app/views/people/_aspect_list.haml b/app/views/people/_aspect_list.haml index a7df0a308..8c65d7361 100644 --- a/app/views/people/_aspect_list.haml +++ b/app/views/people/_aspect_list.haml @@ -19,7 +19,7 @@ - else .badges{:class => ("hidden" if !contact.persisted?)} - = aspect_badges(aspects_with_person) + = aspect_badges(aspects_with_person, :link => true) %p = link_to t('.edit_membership'), {:controller => "contacts", diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index ed3ec10b9..9acbd7e6f 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -45,8 +45,9 @@ - else - - if user_signed_in? && @contact.person + - if user_signed_in? && @contact.person && @contact.pending? == false .right + = link_to t('.mention'), new_status_message_path(:person_id => @person.id), :class => 'button', :rel => 'facebox' = link_to t('.message'), new_conversation_path(:contact_id => @contact.id, :name => @contact.person.name, :contact_id => @contact.id), :class => 'button', :rel => 'facebox' /- if @post_type == :photos @@ -70,6 +71,7 @@ = t('.you_have_no_tags') %span.add_tags = link_to t('.add_some'), edit_profile_path + %hr - if @posts.count > 0 diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml index b2c93bb9b..bcb23eaf7 100644 --- a/app/views/shared/_publisher.html.haml +++ b/app/views/shared/_publisher.html.haml @@ -13,7 +13,7 @@ = image_tag 'icons/doc_edit.png' %span= t('.whats_on_your_mind') - = t('aspects', :count => @aspect_ids.length) + = t('aspects', :count => aspect_ids.length) .content_creation = form_for(StatusMessage.new, :remote => true, :html => {"data-type" => "json"}) do |status| @@ -30,12 +30,18 @@ = status.text_area :fake_text, :rows => 2, :value => h(params[:prefill]) = status.hidden_field :text, :value => '', :class => 'clear_on_submit' - - for aspect_id in @aspect_ids + - for aspect_id in aspect_ids = hidden_field_tag 'aspect_ids[]', aspect_id.to_s .options_and_submit %div.mention_helper - %i= t('.mention_helper_text') + - if aspect != :profile + %i= t('.mention_helper_text') + - else + .badges + %i= 'publishing to: ' + = aspect_badges(aspects_with_person, :link => false) + .right #fileInfo @@ -58,5 +64,5 @@ = render 'shared/public_explain' #publisher_photo_upload - = render 'photos/new_photo', :aspect_ids => @aspect_ids.join(',') + = render 'photos/new_photo', :aspect_ids => aspect_ids.join(',') diff --git a/app/views/shared/_publisher.mobile.haml b/app/views/shared/_publisher.mobile.haml index 7f850d4ad..0694a5be1 100644 --- a/app/views/shared/_publisher.mobile.haml +++ b/app/views/shared/_publisher.mobile.haml @@ -4,7 +4,7 @@ :javascript $(document).ready(function(){ - $("#status_message_message").bind("focus", function(){ + $("#status_message_text").bind("focus", function(){ $("#publisher fieldset:first").removeClass('hidden'); }); }); diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index 1f31ea14d..d65ac48e3 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -26,7 +26,7 @@ = t('the_world') - elsif post.author.owner_id == current_user.id %span.aspect_badges - = aspect_badges(aspects_with_post(all_aspects, post)) + = aspect_badges(aspects_with_post(all_aspects, post), :link => true) %span.timeago = link_to(how_long_ago(post), status_message_path(post)) diff --git a/app/views/status_messages/new.haml b/app/views/status_messages/new.haml new file mode 100644 index 000000000..9e9d3c142 --- /dev/null +++ b/app/views/status_messages/new.haml @@ -0,0 +1,22 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + + += javascript_include_tag "publisher.js" + +:javascript + $(document).ready(function() + { + var person = {name: '#{@person.name}', handle: '#{@person.diaspora_handle}' }; + Publisher.autocompletion.onSelect($("#status_message_fake_text"),person,'#{@person.name}'); + $("#publisher #status_message_submit.button").click(function(){$.facebox.close();}); + }); + +#new_status_message_pane + .span-15 + #facebox_header + %h4 + = t('conversations.index.new_message') + + = render :partial => 'shared/publisher', :locals => { :aspect => @aspect, :aspect_ids => @aspect_ids, :aspects_with_person => @aspects_with_person, :person => @person} diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 97431fe03..25b4404f8 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -346,6 +346,7 @@ en: similar_contacts: "similar contacts" start_sharing: "start sharing" message: "Message" + mention: "Mention" profile_sidebar: remove_contact: "remove contact" edit_my_profile: "Edit my profile" diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml index 75e04f1c4..dc52cfc2b 100644 --- a/config/locales/javascript/javascript.en.yml +++ b/config/locales/javascript/javascript.en.yml @@ -29,4 +29,6 @@ en: shared: contact_list: cannot_remove: "Cannot remove person from last aspect. (If you want to disconnect from this person you must remove contact.)" + publisher: + at_least_one_aspect: "You must publish to at least one aspect" diff --git a/config/routes.rb b/config/routes.rb index 6ca436187..7efea6eb3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,7 +5,7 @@ Diaspora::Application.routes.draw do - resources :status_messages, :only => [:create, :destroy, :show] + resources :status_messages, :only => [:new, :create, :destroy, :show] resources :comments, :only => [:create] resources :requests, :only => [:destroy, :create] diff --git a/features/posts.feature b/features/posts.feature index 45fc98c56..1714b4ca6 100644 --- a/features/posts.feature +++ b/features/posts.feature @@ -4,25 +4,28 @@ Feature: posting As a rock star I want to tell the world I am eating a yogurt - Scenario: post to all aspects - Given that I am a rock star + Background: + Given a user with username "bob" + And a user with username "alice" + When I sign in as "bob@bob.bob" + And a user with username "bob" is connected with "alice" + And I have an aspect called "PostTo" + And I have an aspect called "DidntPostTo" + And I have user with username "alice" in an aspect called "PostTo" + And I have user with username "alice" in an aspect called "DidntPostTo" + And I have no open aspects saved - Given I am signed in - And I have an aspect called "Family" - And I am on the home page - And I expand the publisher + And I am on the home page + + Scenario: post to all aspects + Given I expand the publisher When I fill in "status_message_fake_text" with "I am eating a yogurt" And I press "Share" And I follow "Home" Then I should see "I am eating a yogurt" within ".stream_element" Scenario: delete a post - Given that I am a rock star - And I have no open aspects saved - And I am signed in - And I have an aspect called "Family" - And I am on the home page - And I expand the publisher + Given I expand the publisher When I fill in "status_message_fake_text" with "I am eating a yogurt" And I press "Share" And I follow "Home" @@ -33,12 +36,6 @@ Feature: posting Then I should not see "I am eating a yogurt" Scenario Outline: post to one aspect - Given that I am a rock star - And I have no open aspects saved - Given I am signed in - And I have an aspect called "PostTo" - And I have an aspect called "DidntPostTo" - And I am on the home page When I follow "PostTo" And I wait for the ajax to finish And I expand the publisher @@ -52,3 +49,34 @@ Feature: posting | aspect | see | | PostTo | see | | DidntPostTo | not see | + + Scenario Outline: posting to all aspects from the profile page + Given I am on "alice@alice.alice"'s page + And I have turned off jQuery effects + And I click "Mention" button + And I expand the publisher in the modal window + And I append "#publisher #status_message_text" with "I am eating a yogurt" in the modal window + And I press "Share" in the modal window + And I follow "" + Then I should "I am eating a yogurt" + + Examples: + | aspect | see | + | PostTo | see | + | DidntPostTo | see | + + Scenario Outline: posting to one aspect from the profile page + Given I am on "alice@alice.alice"'s page + And I have turned off jQuery effects + And I click "Mention" button + And I expand the publisher in the modal window + And I append "#publisher #status_message_text" with "I am eating a yogurt" in the modal window + And I follow "DidntPostTo" within "#publisher" in the modal window + And I press "Share" in the modal window + And I follow "" + Then I should "I am eating a yogurt" + + Examples: + | aspect | see | + | PostTo | see | + | DidntPostTo | not see | diff --git a/features/step_definitions/custom_web_steps.rb b/features/step_definitions/custom_web_steps.rb index f86a1d29f..21ac3264e 100644 --- a/features/step_definitions/custom_web_steps.rb +++ b/features/step_definitions/custom_web_steps.rb @@ -11,6 +11,13 @@ And /^I expand the publisher$/ do ') end + +When /^(?:|I )append "([^"]*)" with "([^"]*)"$/ do |field, value| + script = "$('#{ field }').val(function(index, value) { + return value + ' ' + '#{value}'; });" + page.execute_script(script) +end + And /^I hover over the post$/ do page.execute_script('$(".stream_element").first().mouseover()') end @@ -19,6 +26,10 @@ When /^I click to delete the first post$/ do page.execute_script('$(".stream_element").first().find(".delete").click()') end +And /^I click "([^"]*)" button$/ do |arg1| + page.execute_script('$(".button:contains('+arg1+')").click()') +end + And /^I preemptively confirm the alert$/ do page.evaluate_script("window.confirm = function() { return true; }") end diff --git a/features/step_definitions/user_steps.rb b/features/step_definitions/user_steps.rb index 192dcafeb..353aaeb6d 100644 --- a/features/step_definitions/user_steps.rb +++ b/features/step_definitions/user_steps.rb @@ -16,6 +16,13 @@ Given /^a user with email "([^\"]*)"$/ do |email| user.aspects.create(:name => "Unicorns") end +Given /^a user with username "([^\"]*)"$/ do |username| + user = Factory(:user, :email => username + "@" + username + '.' + username, :username => username, + :password => 'password', :password_confirmation => 'password', :getting_started => false) + user.aspects.create(:name => "Besties") + user.aspects.create(:name => "Unicorns") +end + Given /^a user named "([^\"]*)" with email "([^\"]*)"$/ do |name, email| first, last = name.split username = "#{first}_#{last}" if first @@ -45,6 +52,13 @@ Given /^I have an aspect called "([^\"]*)"$/ do |aspect_name| @me.reload end +When /^I have user with username "([^"]*)" in an aspect called "([^"]*)"$/ do |username, aspect| + user = User.find_by_username(username) + contact = @me.reload.contact_for(user.person) + contact.aspects << @me.aspects.find_by_name(aspect) +end + + Given /^I have one contact request$/ do other_user = Factory(:user) other_aspect = other_user.aspects.create!(:name => "meh") @@ -115,6 +129,12 @@ Given /^a user with email "([^"]*)" is connected with "([^"]*)"$/ do |arg1, arg2 connect_users(user1, user1.aspects.first, user2, user2.aspects.first) end +Given /^a user with username "([^"]*)" is connected with "([^"]*)"$/ do |arg1, arg2| + user1 = User.where(:username => arg1).first + user2 = User.where(:username => arg2).first + connect_users(user1, user1.aspects.first, user2, user2.aspects.first) +end + Given /^a user with email "([^\"]*)" has posted a status message "([^\"]*)" in all aspects$/ do |arg1, arg2| user = User.where(:email => arg1).first status_message = user.build_post(:status_message, :text => arg2) diff --git a/public/javascripts/publisher.js b/public/javascripts/publisher.js index 3e2eaa495..b322e9f80 100644 --- a/public/javascripts/publisher.js +++ b/public/javascripts/publisher.js @@ -306,6 +306,26 @@ var Publisher = { ''); }; }, + toggleAspectIds: function(aspectId) { + var hidden_field = $('#publisher [name="aspect_ids[]"][value="'+aspectId+'"]') + if(hidden_field.length > 0){ + hidden_field.remove(); + } else { + $("#publisher .content_creation form").append( + ''); + }; + }, + bindAspectToggles: function() { + $('#publisher .aspect_badge').bind("click", function(){ + var unremovedAspects = $(this).parent().children('.aspect_badge').length - $(this).parent().children(".aspect_badge.removed").length; + if(!$(this).hasClass('removed') && ( unremovedAspects == 1 )){ + alert(Diaspora.widgets.i18n.t('publisher.at_least_one_aspect')) + }else{ + Publisher.toggleAspectIds($(this).children('a').attr('data-guid')); + $(this).toggleClass("removed"); + }; + }); + }, initialize: function() { Publisher.cachedForm = false; Publisher.cachedInput = false; @@ -314,6 +334,7 @@ var Publisher = { Publisher.bindServiceIcons(); Publisher.bindPublicIcon(); + Publisher.bindAspectToggles(); if ($("#status_message_fake_text").val() == "") { Publisher.close(); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 0abadf07b..451de9f87 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -4,6 +4,7 @@ $blue: #3F8FBA +$red: #FF0000 $background: rgb(252,252,252) body @@ -2217,6 +2218,14 @@ h3,h4 &:active :top 0px +.aspect_badge.removed + :background + :color #FA2A00 + :text + :decoration line-through + &:hover + :background + :color #FA2A00 .stream .avatar :float left diff --git a/spec/controllers/people_controller_spec.rb b/spec/controllers/people_controller_spec.rb index a0c5f775c..78792edc9 100644 --- a/spec/controllers/people_controller_spec.rb +++ b/spec/controllers/people_controller_spec.rb @@ -172,6 +172,15 @@ describe PeopleController do get :show, :id => @person.id assigns(:posts).should =~ posts_user_can_see end + + it 'generates a jasmine fixture' do + contact = @user.contact_for(@person) + aspect = @user.aspects.create(:name => 'people') + contact.aspects << aspect + contact.save + get :show, :id => @person + save_fixture(html_for("body"), "person_show") + end end context "when the person is not a contact of the current user" do diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 46dd5037c..5c9875042 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -20,6 +20,14 @@ describe StatusMessagesController do @user1.reload end + describe '#new' do + it 'succeeds' do + get :new, + :person_id => @user2.person.id + response.should be_success + end + end + describe '#show' do before do @message = @user1.build_post :status_message, :text => "ohai", :to => @aspect1.id diff --git a/spec/javascripts/publisher-spec.js b/spec/javascripts/publisher-spec.js index c1ce93c9e..0dcbf40da 100644 --- a/spec/javascripts/publisher-spec.js +++ b/spec/javascripts/publisher-spec.js @@ -21,6 +21,81 @@ describe("Publisher", function() { }); }); + describe("bindAspectToggles", function() { + beforeEach( function(){ + spec.loadFixture('person_show'); + }); + + it('gets called on initialize', function(){ + spyOn(Publisher, 'bindAspectToggles'); + Publisher.initialize(); + expect(Publisher.bindAspectToggles).toHaveBeenCalled(); + }); + + it('toggles removed only on the clicked icon', function(){ + expect($("#publisher .aspect_badge").first().hasClass("removed")).toBeFalsy(); + expect($("#publihser .aspect_badge").last().hasClass("removed")).toBeFalsy(); + + Publisher.bindAspectToggles(); + $("#publisher .aspect_badge").last().click(); + + expect($("#publisher .aspect_badge").first().hasClass("removed")).toBeFalsy(); + expect($("#publisher .aspect_badge").last().hasClass("removed")).toBeTruthy(); + }); + + it('binds to the services icons and toggles the hidden field', function(){ + spyOn(Publisher, 'toggleAspectIds'); + Publisher.bindAspectToggles(); + var aspBadge = $("#publisher .aspect_badge a").last(); + var aspNum = aspBadge.attr('data-guid'); + aspBadge.click(); + + expect(Publisher.toggleAspectIds).toHaveBeenCalledWith(aspNum); + }); + + it('does not execute if it is the last non-removed aspect', function(){ + var aspects = $("#publisher .aspect_badge").length; + spyOn(Publisher, 'toggleAspectIds'); + + Publisher.bindAspectToggles(); + spyOn(window, 'alert');// click through the dialog if it happens + $("#publisher .aspect_badge a").each(function(){$(this).click()}); + + var lastAspectNum = $("#publisher .aspect_badge a").last().attr('data-guid'); + + expect($("#publisher .aspect_badge.removed").length).toBe(aspects-1); + expect(Publisher.toggleAspectIds.callCount).toBe(1); + }); + }); + describe('toggleAspectIds', function(){ + beforeEach( function(){ + spec.loadFixture('person_show'); + }); + + it('adds a hidden field to the form if there is not one already', function(){ + expect($('#publisher [name="aspect_ids[]"]').length).toBe(2); + Publisher.toggleAspectIds(42); + expect($('#publisher [name="aspect_ids[]"]').length).toBe(3); + expect($('#publisher [name="aspect_ids[]"]').last().attr('value')).toBe('42'); + }); + + it('removes the hidden field if its already there', function() { + Publisher.toggleAspectIds(42); + expect($('#publisher [name="aspect_ids[]"]').length).toBe(3); + + Publisher.toggleAspectIds(42); + expect($('#publisher [name="aspect_ids[]"]').length).toBe(2); + }); + + it('does not remove a hidden field with a different value', function() { + Publisher.toggleAspectIds(42); + expect($('#publisher [name="aspect_ids[]"]').length).toBe(3); + + Publisher.toggleAspectIds(99); + expect($('#publisher [name="aspect_ids[]"]').length).toBe(4); + }); + }); + describe("bindPublicIcon", function() { beforeEach( function(){ spec.loadFixture('aspects_index_services'); diff --git a/spec/javascripts/rails-spec.js b/spec/javascripts/rails-spec.js index 6d1911f7a..ae6424a21 100644 --- a/spec/javascripts/rails-spec.js +++ b/spec/javascripts/rails-spec.js @@ -3,7 +3,7 @@ describe("rails", function() { beforeEach(function() { $("#jasmine_content").html( '
' + - '' + + '' + '' + '' + '' + @@ -12,12 +12,12 @@ describe("rails", function() { }); it("should retain form values if ajax fails", function() { $('#form').trigger('ajax:failure'); - expect($('#status_message_message').val()).not.toEqual(""); + expect($('#status_message_text').val()).not.toEqual(""); }); it("should clear form on ajax:success", function() { $('#form').trigger('ajax:success'); - expect($('#status_message_message').val()).toEqual(""); + expect($('#status_message_text').val()).toEqual(""); }); it('should not clear normal hidden fields', function(){ diff --git a/spec/javascripts/view-spec.js b/spec/javascripts/view-spec.js index 0ba6550fc..013d788a7 100644 --- a/spec/javascripts/view-spec.js +++ b/spec/javascripts/view-spec.js @@ -98,7 +98,7 @@ describe("View", function() { $("#jasmine_content").html( '
' + '' + - '' + + '' + '' + '
' );