diff --git a/app/assets/images/buttons/bday@2x-white.png b/app/assets/images/buttons/bday@2x-white.png new file mode 100644 index 000000000..ab5940c25 Binary files /dev/null and b/app/assets/images/buttons/bday@2x-white.png differ diff --git a/app/assets/images/buttons/service-icons/fb@2x-white.png b/app/assets/images/buttons/service-icons/fb@2x-white.png new file mode 100644 index 000000000..2fb0b930c Binary files /dev/null and b/app/assets/images/buttons/service-icons/fb@2x-white.png differ diff --git a/app/assets/images/buttons/service-icons/tumblr@2x-white.png b/app/assets/images/buttons/service-icons/tumblr@2x-white.png new file mode 100644 index 000000000..48ce0dc3c Binary files /dev/null and b/app/assets/images/buttons/service-icons/tumblr@2x-white.png differ diff --git a/app/assets/images/buttons/service-icons/twitter@2x-white.png b/app/assets/images/buttons/service-icons/twitter@2x-white.png new file mode 100644 index 000000000..7b6aec6a0 Binary files /dev/null and b/app/assets/images/buttons/service-icons/twitter@2x-white.png differ diff --git a/app/assets/javascripts/app/forms/picture_form.js b/app/assets/javascripts/app/forms/picture_form.js index 92640363b..21f3ba64c 100644 --- a/app/assets/javascripts/app/forms/picture_form.js +++ b/app/assets/javascripts/app/forms/picture_form.js @@ -1,11 +1,35 @@ -app.forms.Picture = app.views.Base.extend({ - templateName : "picture-form", - +app.forms.PictureBase = app.views.Base.extend({ events : { 'ajax:complete .new_photo' : "photoUploaded", "change input[name='photo[user_file]']" : "submitForm" }, + onSubmit : $.noop, + uploadSuccess : $.noop, + + postRenderTemplate : function(){ + this.$("input[name=authenticity_token]").val($("meta[name=csrf-token]").attr("content")) + }, + + submitForm : function (){ + this.$("form").submit(); + this.onSubmit(); + }, + + photoUploaded : function(evt, xhr) { + resp = JSON.parse(xhr.responseText) + if(resp.success) { + this.uploadSuccess(resp) + } else { + alert("Upload failed! Please try again. " + resp.error); + } + } +}); + +/* multi photo uploader */ +app.forms.Picture = app.forms.PictureBase.extend({ + templateName : "picture-form", + initialize : function() { this.photos = new Backbone.Collection() this.photos.bind("add", this.render, this) @@ -17,18 +41,12 @@ app.forms.Picture = app.views.Base.extend({ this.renderPhotos(); }, - submitForm : function (){ - this.$("form").submit(); + onSubmit : function (){ this.$(".photos").append($('')) }, - photoUploaded : function(evt, xhr) { - resp = JSON.parse(xhr.responseText) - if(resp.success) { - this.photos.add(new Backbone.Model(resp.data)) - } else { - alert("Upload failed! Please try again. " + resp.error); - } + uploadSuccess : function(resp) { + this.photos.add(new Backbone.Model(resp.data)) }, renderPhotos : function(){ @@ -38,4 +56,13 @@ app.forms.Picture = app.views.Base.extend({ photoContainer.append(photoView) }) } +}); + +/* wallpaper uploader */ +app.forms.Wallpaper = app.forms.PictureBase.extend({ + templateName : "wallpaper-form", + + uploadSuccess : function(resp) { + $("#profile").css("background-image", "url(" + resp.data.wallpaper + ")") + } }); \ No newline at end of file diff --git a/app/assets/javascripts/app/pages/profile.js b/app/assets/javascripts/app/pages/profile.js index 1915d7dc2..ae3b97f4c 100644 --- a/app/assets/javascripts/app/pages/profile.js +++ b/app/assets/javascripts/app/pages/profile.js @@ -2,13 +2,15 @@ //= require ../views/profile_info_view app.pages.Profile = app.views.Base.extend({ - className : "container", + + id : "profile", templateName : "profile", subviews : { "#profile-info" : "profileInfo", - "#canvas" : "canvasView" + "#canvas" : "canvasView", + "#wallpaper-upload" : "wallpaperForm" }, events : { @@ -37,7 +39,7 @@ app.pages.Profile = app.views.Base.extend({ this.model = new app.models.Profile.findByGuid(options.personId) this.stream = options && options.stream || new app.models.Stream() - this.model.bind("change", this.setPageTitle, this) + this.model.bind("change", this.setPageTitleAndBackground, this) /* binds for getting started pulsation */ this.stream.bind("fetched", this.pulsateNewPostControl, this) @@ -46,6 +48,7 @@ app.pages.Profile = app.views.Base.extend({ this.stream.preloadOrFetch(); this.canvasView = new app.views.Canvas({ model : this.stream }) + this.wallpaperForm = new app.forms.Wallpaper() // send in isOwnProfile data this.profileInfo = new app.views.ProfileInfo({ model : this.model.set({isOwnProfile : this.isOwnProfile()}) }) @@ -59,9 +62,11 @@ app.pages.Profile = app.views.Base.extend({ ]("pulse") }, - setPageTitle : function() { - if(this.model.get("name")) + setPageTitleAndBackground : function() { + if(this.model.get("name")) { document.title = this.model.get("name") + this.$el.css("background-image", "url(" + this.model.get("wallpaper") + ")") + } }, toggleEdit : function(evt) { diff --git a/app/assets/javascripts/app/views/canvas_view.js b/app/assets/javascripts/app/views/canvas_view.js index a3e2f8ef5..709abdeff 100644 --- a/app/assets/javascripts/app/views/canvas_view.js +++ b/app/assets/javascripts/app/views/canvas_view.js @@ -29,6 +29,7 @@ app.views.Canvas = app.views.Base.extend(_.extend({}, app.views.infiniteScrollMi itemSelector : '.canvas-frame', visibleStyle : {scale : 1}, hiddenStyle : {scale : 0.001}, + containerStyle : {position : "relative"}, masonry : { columnWidth : 292.5 } diff --git a/app/assets/stylesheets/new_styles/_base.scss b/app/assets/stylesheets/new_styles/_base.scss index db268f266..719aaea8b 100644 --- a/app/assets/stylesheets/new_styles/_base.scss +++ b/app/assets/stylesheets/new_styles/_base.scss @@ -1,3 +1,7 @@ +body { + background-image : image_url("pattern.png"); +} + /* new link color */ a { color : rgb(42,156,235) } @@ -437,19 +441,6 @@ div[data-template=flow] { right : 10px; top : 10px; - - .label { - padding : 2px 5px; - padding-bottom : 3px; - - span { - display : inline-block; - position : relative; - top : 1px; - font-family : Roboto-Bold; - } - } - & > a { @include transition(opacity); @include opacity(0.4); @@ -461,4 +452,17 @@ div[data-template=flow] { text-decoration : none; } } -} \ No newline at end of file +} + +/* bootstrap label fixes for Roboto */ +.label { + padding : 2px 5px; + padding-bottom : 3px; + + span { + display : inline-block; + position : relative; + top : 1px; + font-family : Roboto-Bold; + } +} diff --git a/app/assets/stylesheets/new_styles/_canvas.scss b/app/assets/stylesheets/new_styles/_canvas.scss index fc8455bec..ab80a4e6d 100644 --- a/app/assets/stylesheets/new_styles/_canvas.scss +++ b/app/assets/stylesheets/new_styles/_canvas.scss @@ -1,18 +1,13 @@ - @mixin wide() { width : $two-column-width + px; min-width : $two-column-width + px; max-width : $two-column-width + px; } -body { - background-color : #F6F6F6; - background-image : image_url('pattern.png') -} - .canvas-frame { float : left; margin : 10px; + margin-bottom : 18px; /* expand / contract cursor declarations */ &.x2 .content { @@ -28,12 +23,12 @@ body { .content { @include transition(box-shadow); - @include box-shadow(0,1px,3px,rgba(0,0,0,0.2)); + @include box-shadow(0, 8px, 50px, rgba(0,0,0,0.9)); background-image : image_url("paper-texture-1.jpg"); &:hover { - @include box-shadow(0,1px,5px,rgba(0,0,0,0.5)); + @include box-shadow(0, 3px, 10px,rgba(0,0,0,0.9)); .info { top : 0; @@ -59,11 +54,9 @@ body { max-width : $column-width + px; overflow : hidden; - //padding : 20px; /* used in masking photos with overflow: hidden; */ .image-container { - overflow : hidden; width : 100%; @@ -128,6 +121,8 @@ body { background-color : rgba(255,255,255,0.8); border-bottom : 1px solid #fff; + color : #000; + position : absolute; top : -32px; right : 0; diff --git a/app/assets/stylesheets/new_styles/_composer.scss b/app/assets/stylesheets/new_styles/_composer.scss index 7b8e8114a..89e5af3d3 100644 --- a/app/assets/stylesheets/new_styles/_composer.scss +++ b/app/assets/stylesheets/new_styles/_composer.scss @@ -11,9 +11,7 @@ #photo_upload_button { position: relative; - margin : { - bottom : 9px; - } + margin-bottom : 9px; input{ @include opacity(0); @@ -21,6 +19,7 @@ top: 0; left: 0; height:100%; + cursor : pointer; } } diff --git a/app/assets/stylesheets/new_styles/_profile.scss b/app/assets/stylesheets/new_styles/_profile.scss index de74b9a73..0f769d14c 100644 --- a/app/assets/stylesheets/new_styles/_profile.scss +++ b/app/assets/stylesheets/new_styles/_profile.scss @@ -1,3 +1,25 @@ +#profile { + color : #fff; + background : { + color : #333; + + /* The background-image property will be user-generated and set in `app.pages.Profile` (app/assets/javascripts/app/pages/profile.js) + and should ONLY be set once the image is fully loaded. + + Optimal imagemagick manipulation settings for uploaded image (via trial & error): + + `convert -brightness-contrast -40x-50` + + NOTE: I noticed that just turning the brightness down had an adverse affect on contrast, + thus the "washing out" at -50 contrast. For more info on this specific command, read the documentation + on it here: http://www.imagemagick.org/script/command-line-options.php#brightness-contrast */ + //image : url('/imagetest.jpg'); + + size : cover; + attachment : fixed; + } +} + /* getting started pulse animation */ #composer-button.pulse { -webkit-animation: opacity-pulse 1s infinite; diff --git a/app/assets/templates/profile-info.jst.hbs b/app/assets/templates/profile-info.jst.hbs index 5b3aa27b4..305bdba72 100644 --- a/app/assets/templates/profile-info.jst.hbs +++ b/app/assets/templates/profile-info.jst.hbs @@ -8,7 +8,7 @@ {{#if location}} - + {{location}} @@ -17,7 +17,7 @@ {{#if birthday}} - + {{birthday}} @@ -25,20 +25,20 @@ - + - + - + {{#if isOwnProfile}} - + {{/if}} diff --git a/app/assets/templates/profile.jst.hbs b/app/assets/templates/profile.jst.hbs index ebc58f3bb..03711e064 100644 --- a/app/assets/templates/profile.jst.hbs +++ b/app/assets/templates/profile.jst.hbs @@ -1,60 +1,66 @@ -
- {{#if showFollowButton}} - - - - - FOLLOW - - - - {{/if}} +{{#if isOwnProfile}} +
+{{/if}} - {{#if current_user.guid}} - - - - - HOME +
+
+ {{#if showFollowButton}} + + + + + FOLLOW + - - - {{else}} - - - - DIASPORA - - - - {{/if}} + + {{/if}} - {{#if isOwnProfile}} - - - - - - {{/if}} + {{#if current_user.guid}} + + + + + HOME + + + + {{else}} + + + + DIASPORA + + + + {{/if}} + + {{#if isOwnProfile}} + + + + + + {{/if}} +
+ +
+ + + +
+ {{#if isOwnProfile}} + + + + + + + {{/if}} +
+ +
+ +
- -
- - - -
- {{#if isOwnProfile}} - - - - - - - {{/if}} -
- -
- -
\ No newline at end of file diff --git a/app/assets/templates/wallpaper-form.jst.hbs b/app/assets/templates/wallpaper-form.jst.hbs new file mode 100644 index 000000000..8af11ab1e --- /dev/null +++ b/app/assets/templates/wallpaper-form.jst.hbs @@ -0,0 +1,13 @@ +
+ +
+ +
+ + +
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 390a99a4a..f9862416c 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -16,14 +16,14 @@ class ProfilesController < ApplicationController respond_to do |format| format.json { public_json = @person.as_api_response(:backbone) - extra_json = {} + extra_json = {:wallpaper => @person.profile.wallpaper.url} if(current_user && (current_user.person == @person || current_user.contacts.receiving.where(:person_id => @person.id).first)) - extra_json = { + extra_json = extra_json.merge({ :location => @person.profile.location, :birthday => @person.profile.formatted_birthday, :bio => @person.profile.bio - } + }) end render :json => public_json.merge(extra_json) @@ -77,6 +77,30 @@ class ProfilesController < ApplicationController end end + def upload_wallpaper_image + unless params[:photo].present? + respond_to do |format| + format.json { render :json => {"success" => false} } + end + return + end + + if remotipart_submitted? + profile = current_user.person.profile + + profile.wallpaper.store! params[:photo][:user_file] + if profile.save + respond_to do |format| + format.json { render :json => {"success" => true, "data" => {"wallpaper" => profile.wallpaper.url}} } + end + else + respond_to do |format| + format.json { render :json => {"success" => false} } + end + end + end + end + protected def munge_tag_string diff --git a/app/models/profile.rb b/app/models/profile.rb index bf41aa31b..8dc6b120a 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -5,6 +5,8 @@ class Profile < ActiveRecord::Base self.include_root_in_json = false + mount_uploader :wallpaper, WallpaperUploader + include Diaspora::Federated::Base include Diaspora::Taggable diff --git a/app/uploaders/wallpaper_uploader.rb b/app/uploaders/wallpaper_uploader.rb new file mode 100644 index 000000000..4d78e6e80 --- /dev/null +++ b/app/uploaders/wallpaper_uploader.rb @@ -0,0 +1,27 @@ +class WallpaperUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick + + def store_dir + "uploads/images/wallpaper" + end + + def extension_white_list + %w(jpg jpeg png tiff) + end + + # Filename is associated with the user's diaspora handle, ensuring uniqueness + # and that only one copy is kept in the filesystem. + def filename + Digest::MD5.hexdigest(model.diaspora_handle) + File.extname(@filename) if @filename + end + + process :darken + + def darken + manipulate! do |img| + img.brightness_contrast "-40x-50" + img = yield(img) if block_given? + img + end + end +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 26a500e58..7911f699c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -54,6 +54,8 @@ Diaspora::Application.routes.draw do put :make_profile_photo end + post "upload_wallpaper" => 'profiles#upload_wallpaper_image' + # ActivityStreams routes scope "/activity_streams", :module => "activity_streams", :as => "activity_streams" do resources :photos, :controller => "photos", :only => [:create] diff --git a/db/migrate/20120506053156_add_wallpaper_to_profile.rb b/db/migrate/20120506053156_add_wallpaper_to_profile.rb new file mode 100644 index 000000000..b190cb8b6 --- /dev/null +++ b/db/migrate/20120506053156_add_wallpaper_to_profile.rb @@ -0,0 +1,5 @@ +class AddWallpaperToProfile < ActiveRecord::Migration + def change + add_column :profiles, :wallpaper, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 1df52e628..c3d5a727e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120427152648) do +ActiveRecord::Schema.define(:version => 20120506053156) do create_table "account_deletions", :force => true do |t| t.string "diaspora_handle" @@ -369,6 +369,7 @@ ActiveRecord::Schema.define(:version => 20120427152648) do t.string "location" t.string "full_name", :limit => 70 t.boolean "nsfw", :default => false + t.string "wallpaper" end add_index "profiles", ["full_name", "searchable"], :name => "index_profiles_on_full_name_and_searchable" diff --git a/public/imagetest-unmodified.jpg b/public/imagetest-unmodified.jpg new file mode 100644 index 000000000..db16d1c05 Binary files /dev/null and b/public/imagetest-unmodified.jpg differ diff --git a/public/imagetest.jpg b/public/imagetest.jpg new file mode 100644 index 000000000..85df6071e Binary files /dev/null and b/public/imagetest.jpg differ diff --git a/public/imagetest2.jpg b/public/imagetest2.jpg new file mode 100644 index 000000000..f47d1ad51 Binary files /dev/null and b/public/imagetest2.jpg differ diff --git a/public/img/glyphicons-halflings-white.png b/public/img/glyphicons-halflings-white.png deleted file mode 100644 index a20760bfd..000000000 Binary files a/public/img/glyphicons-halflings-white.png and /dev/null differ diff --git a/public/img/glyphicons-halflings.png b/public/img/glyphicons-halflings.png deleted file mode 100644 index 92d4445df..000000000 Binary files a/public/img/glyphicons-halflings.png and /dev/null differ diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb index 42dc6a70b..9916df70d 100644 --- a/spec/controllers/profiles_controller_spec.rb +++ b/spec/controllers/profiles_controller_spec.rb @@ -6,37 +6,36 @@ require 'spec_helper' describe ProfilesController do before do - @user = eve - sign_in :user, @user + sign_in :user, eve end describe '#show' do it "returns the user as json" do - get :show, :id => @user.person.guid, :format => :json - JSON.parse(response.body).should include(JSON.parse(@user.person.as_api_response(:backbone).to_json)) + get :show, :id => eve.person.guid, :format => :json + JSON.parse(response.body).should include(JSON.parse(eve.person.as_api_response(:backbone).to_json)) end it "returns the user's public information if a user is not logged in" do sign_out :user - get :show, :id => @user.person.guid, :format => :json - JSON.parse(response.body).should include(JSON.parse(@user.person.as_api_response(:backbone).to_json)) + get :show, :id => eve.person.guid, :format => :json + JSON.parse(response.body).should include(JSON.parse(eve.person.as_api_response(:backbone).to_json)) end it "returns the user's public information if a user is logged in and the visiting user is not receiving" do sign_in :user, alice - get :show, :id => @user.person.guid, :format => :json + get :show, :id => eve.person.guid, :format => :json response.body.should_not match(/.location./) end it "returns the user's private information if a user is logged in and the visiting user is receiving" do sign_in :user, bob - get :show, :id => @user.person.guid, :format => :json + get :show, :id => eve.person.guid, :format => :json response.body.should match(/.location./) end it "returns the user's private information if a user is logged in as herself" do sign_in :user, eve - get :show, :id => @user.person.guid, :format => :json + get :show, :id => eve.person.guid, :format => :json response.body.should match(/.location./) end end @@ -49,7 +48,7 @@ describe ProfilesController do it 'sets the profile to the current users profile' do get :edit - assigns[:profile].should == @user.person.profile + assigns[:profile].should == eve.person.profile end it 'sets the aspect to "person_edit" ' do @@ -59,7 +58,7 @@ describe ProfilesController do it 'sets the person to the current users person' do get :edit - assigns[:person].should == @user.person + assigns[:person].should == eve.person end end @@ -74,48 +73,48 @@ describe ProfilesController do end it "sets nsfw" do - @user.person(true).profile.nsfw.should == false - put :update, :profile => { :id => @user.person.id, :nsfw => "1" } - @user.person(true).profile.nsfw.should == true + eve.person(true).profile.nsfw.should == false + put :update, :profile => { :id => eve.person.id, :nsfw => "1" } + eve.person(true).profile.nsfw.should == true end it "unsets nsfw" do - @user.person.profile.nsfw = true - @user.person.profile.save + eve.person.profile.nsfw = true + eve.person.profile.save - @user.person(true).profile.nsfw.should == true - put :update, :profile => { :id => @user.person.id } - @user.person(true).profile.nsfw.should == false + eve.person(true).profile.nsfw.should == true + put :update, :profile => { :id => eve.person.id } + eve.person(true).profile.nsfw.should == false end it 'sets tags' do - params = { :id => @user.person.id, + params = { :id => eve.person.id, :tags => '#apples #oranges'} put :update, params - @user.person(true).profile.tag_list.to_set.should == ['apples', 'oranges'].to_set + eve.person(true).profile.tag_list.to_set.should == ['apples', 'oranges'].to_set end it 'sets plaintext tags' do - params = { :id => @user.person.id, + params = { :id => eve.person.id, :tags => ',#apples,#oranges,', :profile => {:tag_string => '#pears'} } put :update, params - @user.person(true).profile.tag_list.to_set.should == ['apples', 'oranges', 'pears'].to_set + eve.person(true).profile.tag_list.to_set.should == ['apples', 'oranges', 'pears'].to_set end it 'sets plaintext tags without #' do - params = { :id => @user.person.id, + params = { :id => eve.person.id, :tags => ',#apples,#oranges,', :profile => {:tag_string => 'bananas'} } put :update, params - @user.person(true).profile.tag_list.to_set.should == ['apples', 'oranges', 'bananas'].to_set + eve.person(true).profile.tag_list.to_set.should == ['apples', 'oranges', 'bananas'].to_set end it 'sets valid birthday' do - params = { :id => @user.person.id, + params = { :id => eve.person.id, :profile => { :date => { :year => '2001', @@ -123,13 +122,13 @@ describe ProfilesController do :day => '28' } } } put :update, params - @user.person(true).profile.birthday.year.should == 2001 - @user.person(true).profile.birthday.month.should == 2 - @user.person(true).profile.birthday.day.should == 28 + eve.person(true).profile.birthday.year.should == 2001 + eve.person(true).profile.birthday.month.should == 2 + eve.person(true).profile.birthday.day.should == 28 end it 'displays error for invalid birthday' do - params = { :id => @user.person.id, + params = { :id => eve.person.id, :profile => { :date => { :year => '2001', @@ -142,21 +141,21 @@ describe ProfilesController do context 'with a profile photo set' do before do - @params = { :id => @user.person.id, + @params = { :id => eve.person.id, :profile => {:image_url => "", - :last_name => @user.person.profile.last_name, - :first_name => @user.person.profile.first_name }} + :last_name => eve.person.profile.last_name, + :first_name => eve.person.profile.first_name }} - @user.person.profile.image_url = "http://tom.joindiaspora.com/images/user/tom.jpg" - @user.person.profile.save + eve.person.profile.image_url = "http://tom.joindiaspora.com/images/user/tom.jpg" + eve.person.profile.save end it "doesn't overwrite the profile photo when an empty string is passed in" do - image_url = @user.person.profile.image_url + image_url = eve.person.profile.image_url put :update, @params - Person.find(@user.person.id).profile.image_url.should == image_url + Person.find(eve.person.id).profile.image_url.should == image_url end end @@ -168,7 +167,7 @@ describe ProfilesController do end it 'person_id' do - person = @user.person + person = eve.person profile = person.profile put :update, @profile_params profile.reload.person_id.should == person.id @@ -176,8 +175,28 @@ describe ProfilesController do it 'diaspora handle' do put :update, @profile_params - Person.find(@user.person.id).profile[:diaspora_handle].should_not == 'abc@a.com' + Person.find(eve.person.id).profile[:diaspora_handle].should_not == 'abc@a.com' end end end + + describe '#upload_wallpaper_image' do + it 'returns a success=false response if the photo param is not present' do + post :upload_wallpaper_image, :format => :json + JSON.parse(response.body).should include("success" => false) + end + + it 'stores the wallpaper for the current_user' do + # we should have another test here asserting that the wallpaper is set... i was having problems testing + # this behavior though :( + + @controller.stub!(:current_user).and_return(eve) + @controller.stub!(:remotipart_submitted?).and_return(true) + @controller.stub!(:file_handler).and_return(uploaded_photo) + @params = {:photo => {:user_file => uploaded_photo} } + + eve.person.profile.wallpaper.should_receive(:store!) + post :upload_wallpaper_image, @params.merge(:format => :json) + end + end end \ No newline at end of file