diff --git a/README.md b/README.md
index 8a629c41f..65d2c9bb3 100644
--- a/README.md
+++ b/README.md
@@ -18,27 +18,32 @@ With Diaspora you can:
Documentation is available on our [wiki](https://github.com/diaspora/diaspora/wiki)
+[Pull Request Guidelines](https://github.com/diaspora/diaspora/wiki/Pull-Request-Guidelines)
+
+Before submitting code, feel free to sign our [Contributor License Agreement](https://github.com/diaspora/diaspora/wiki/New-CLA--12-13-10) [Sign Here](https://spreadsheets.google.com/a/joindiaspora.com/spreadsheet/viewform?formkey=dFdRTnY0TGtfaklKQXZNUndsMlJ2eGc6MQ)
+
## Quick Start:
Here's how you can get a development environment up and running. You can check out system-specific guides [here](https://github.com/diaspora/diaspora/wiki/Installation-Guides).
### Step 1: Download the script
-```wget https://raw.github.com/diaspora/diaspora/ec5289bd3b9b5608d339b28e1e30272f380a9211/script/install.sh
+```
+ curl https://raw.github.com/diaspora/diaspora/master/script/install.sh | /bin/sh
```
-### Step 2: Set permissions and run
-```chmod +x install.sh && install.sh
+### Step 2: Follow the instructions
+
+
+### Step 3: Run the the development server
+```
+ rails s
```
-### Step 3: Follow the instructions
+then visit 'http://localhost:3000' in your browser.
-
-### Step 4: Run the test server
-```rails s
+### Step 4: Run tests
```
-
-### Step 5: Run tests
-```rake tests
+ rake
```
## Resources:
diff --git a/app/assets/javascripts/app/helpers/text_formatter.js b/app/assets/javascripts/app/helpers/text_formatter.js
index fe4069911..0759a6665 100644
--- a/app/assets/javascripts/app/helpers/text_formatter.js
+++ b/app/assets/javascripts/app/helpers/text_formatter.js
@@ -29,6 +29,8 @@
text = text.replace(linkRegex, function() {
var unicodeUrl = arguments[3];
var addr = parse_url(unicodeUrl);
+ if( !addr.host ) addr.host = ""; // must not be 'undefined'
+
var asciiUrl = // rebuild the url
(!addr.scheme ? '' : addr.scheme +
( (addr.scheme.toLowerCase()=="mailto") ? ':' : '://')) +
diff --git a/app/assets/javascripts/app/router.js b/app/assets/javascripts/app/router.js
index 064cdd523..23a6dfbf9 100644
--- a/app/assets/javascripts/app/router.js
+++ b/app/assets/javascripts/app/router.js
@@ -39,6 +39,8 @@ app.Router = Backbone.Router.extend({
photos : function() {
app.photos = new app.models.Stream([], {collection: app.collections.Photos});
app.page = new app.views.Photos({model : app.photos});
+
+
$("#main_stream").html(app.page.render().el);
},
diff --git a/app/assets/javascripts/app/views.js b/app/assets/javascripts/app/views.js
index 0e6f2aaf0..fc7329390 100644
--- a/app/assets/javascripts/app/views.js
+++ b/app/assets/javascripts/app/views.js
@@ -21,7 +21,8 @@ app.views.Base = Backbone.View.extend({
},
defaultPresenter : function(){
- var modelJson = this.model ? _.clone(this.model.attributes) : {}
+ var modelJson = this.model && this.model.attributes ? _.clone(this.model.attributes) : {}
+
return _.extend(modelJson, {
current_user : app.currentUser.attributes,
loggedIn : app.currentUser.authenticated()
@@ -71,3 +72,66 @@ app.views.Base = Backbone.View.extend({
$(".tooltip").remove();
}
});
+
+// Mixin to render a collection that fetches more via infinite scroll, for a view that has no template.
+// Requires:
+// a stream model, bound as this.stream
+// a stream's posts, bound as this.collection
+// a postClass to be declared
+// a #paginate div in the layout
+// a call to setupInfiniteScroll
+
+app.views.infiniteScrollMixin = {
+ setupInfiniteScroll : function() {
+ this.postViews = this.postViews || []
+
+ this.bind("loadMore", this.fetchAndshowLoader, this)
+ this.stream.bind("fetched", this.hideLoader, this)
+ this.stream.bind("allPostsLoaded", this.unbindInfScroll, this)
+ this.collection.bind("add", this.addPost, this);
+
+ var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
+ $(window).scroll(throttledScroll);
+ },
+
+ renderTemplate : function() {
+ if(this.stream.isFetching()) { this.showLoader() }
+ },
+
+ addPost : function(post) {
+ var postView = new this.postClass({ model: post })
+ , placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append";
+
+ this.$el[placeInStream](postView.render().el);
+ this.postViews.push(postView)
+ },
+
+ unbindInfScroll : function() {
+ $(window).unbind("scroll");
+ },
+
+ fetchAndshowLoader : function(){
+ if(this.stream.isFetching()) { return false }
+ this.stream.fetch()
+ this.showLoader()
+ },
+
+ showLoader: function(){
+ $("#paginate .loader").removeClass("hidden")
+ },
+
+ hideLoader: function() {
+ $("#paginate .loader").addClass("hidden")
+ },
+
+ infScroll : function() {
+ var $window = $(window)
+ , distFromTop = $window.height() + $window.scrollTop()
+ , distFromBottom = $(document).height() - distFromTop
+ , bufferPx = 500;
+
+ if(distFromBottom < bufferPx) {
+ this.trigger("loadMore")
+ }
+ }
+};
diff --git a/app/assets/javascripts/app/views/photos_view.js b/app/assets/javascripts/app/views/photos_view.js
index d20b1e224..dff7052d3 100644
--- a/app/assets/javascripts/app/views/photos_view.js
+++ b/app/assets/javascripts/app/views/photos_view.js
@@ -1,52 +1,16 @@
-app.views.Photos = Backbone.View.extend({
-
- events : {},
-
+app.views.Photos = Backbone.View.extend(_.extend({
initialize : function(options) {
- this.photos = this.model;
- this.collection = this.model.items;
+ this.stream = this.model;
+ this.collection = this.stream.items;
- this.setupEvents();
- this.setupLightbox();
+ // viable for extraction
+ this.stream.fetch();
+
+ this.setupLightbox()
+ this.setupInfiniteScroll()
},
- setupEvents : function(){
- this.photos.bind("fetched", this.removeLoader, this)
- this.collection.bind("add", this.addPhoto, this);
- },
-
- addPhoto : function(photo) {
- var photoView = new app.views.Photo({ model: photo });
-
- $(this.el)[
- (this.collection.at(0).id == photo.id)
- ? "prepend"
- : "append"
- ](photoView.render().el);
-
- return this;
- },
-
- render : function(evt) {
- if(evt) {evt.preventDefault(); }
-
- if(this.model.fetch()) {
- this.appendLoader();
- };
-
- return this;
- },
-
- appendLoader: function(){
- $("#paginate").html($("", {
- src : "/assets/static-loader.png",
- "class" : "loader"
- }));
- },
-
- removeLoader: function() {
- $("#paginate").empty();
- },
+ postClass : app.views.Photo,
setupLightbox : function(){
this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
@@ -55,6 +19,5 @@ app.views.Photos = Backbone.View.extend({
imageSelector: 'img.photo'
});
$(this.el).delegate("a.photo-link", "click", this.lightbox.lightboxImageClicked);
- },
-
-});
+ }
+}, app.views.infiniteScrollMixin));
diff --git a/app/assets/javascripts/app/views/stream_view.js b/app/assets/javascripts/app/views/stream_view.js
index bf4de92f3..5df7574ca 100644
--- a/app/assets/javascripts/app/views/stream_view.js
+++ b/app/assets/javascripts/app/views/stream_view.js
@@ -1,77 +1,27 @@
-app.views.Stream = Backbone.View.extend({
+app.views.Stream = Backbone.View.extend(_.extend({
initialize: function(options) {
this.stream = this.model
- this.collection = this.model.items
+ this.collection = this.stream.items
- this.setupEvents()
- this.setupInfiniteScroll()
- this.setupLightbox()
this.postViews = []
+
+ this.setupNSFW()
+ this.setupLightbox()
+ this.setupInfiniteScroll()
},
- setupEvents : function(){
- this.stream.bind("fetched", this.removeLoader, this)
- this.stream.bind("allItemsLoaded", this.unbindInfScroll, this)
- this.collection.bind("add", this.addPost, this);
-
- app.currentUser.bind("nsfwChanged", reRenderPostViews, this)
- function reRenderPostViews() {
- _.map(this.postViews, function(view){ view.render() })
- }
- },
-
- addPost : function(post) {
- var postView = new app.views.StreamPost({ model: post })
- , placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append";
-
- this.$el[placeInStream](postView.render().el);
- this.postViews.push(postView)
- },
-
- unbindInfScroll : function() {
- $(window).unbind("scroll");
- },
-
- render : function() {
- if(this.stream.isFetching()) { this.appendLoader() }
- return this;
- },
-
- fetchAndAppendLoader : function(){
- if(this.stream.isFetching()) { return false }
- this.stream.fetch()
- this.appendLoader()
- },
-
- appendLoader: function(){
- $("#paginate").html($("
", {
- src : "/assets/static-loader.png",
- "class" : "loader"
- }));
- },
-
- removeLoader: function() {
- $("#paginate").empty();
- },
+ postClass : app.views.StreamPost,
setupLightbox : function(){
this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
this.$el.delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked);
},
- setupInfiniteScroll : function() {
- var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
- $(window).scroll(throttledScroll);
- },
+ setupNSFW : function(){
+ app.currentUser.bind("nsfwChanged", reRenderPostViews, this)
- infScroll : function() {
- var $window = $(window)
- , distFromTop = $window.height() + $window.scrollTop()
- , distFromBottom = $(document).height() - distFromTop
- , bufferPx = 500;
-
- if(distFromBottom < bufferPx) {
- this.fetchAndAppendLoader()
+ function reRenderPostViews() {
+ _.map(this.postViews, function(view){ view.render() })
}
}
-});
+}, app.views.infiniteScrollMixin));
diff --git a/app/assets/stylesheets/application.css.sass b/app/assets/stylesheets/application.css.sass
index f224ebb7a..47158db90 100644
--- a/app/assets/stylesheets/application.css.sass
+++ b/app/assets/stylesheets/application.css.sass
@@ -608,6 +608,8 @@ form.new_comment
img
:border none
+ :min-height 20px
+
#photo_container
:text
:align center
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 5dee5bb46..60d0ec9f3 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -7,7 +7,7 @@ require Rails.root.join("app", "presenters", "post_presenter")
class PostsController < ApplicationController
include PostsHelper
- before_filter :authenticate_user!, :except => [:show, :iframe]
+ before_filter :authenticate_user!, :except => [:show, :iframe, :oembed]
before_filter :set_format_if_malformed_from_status_net, :only => :show
layout 'post'
@@ -24,13 +24,7 @@ class PostsController < ApplicationController
end
def show
- key = params[:id].to_s.length <= 8 ? :id : :guid
-
- if user_signed_in?
- @post = current_user.find_visible_shareable_by_id(Post, params[:id], :key => key)
- else
- @post = Post.where(key => params[:id], :public => true).includes(:author, :comments => :author).first
- end
+ @post = find_by_guid_or_id_with_current_user(params[:id])
if @post
# @commenting_disabled = can_not_comment_on_post?
@@ -59,6 +53,17 @@ class PostsController < ApplicationController
render :text => post_iframe_url(params[:id]), :layout => false
end
+ def oembed
+ post_id = OEmbedPresenter.id_from_url(params.delete(:url))
+ post = find_by_guid_or_id_with_current_user(post_id)
+ if post.present?
+ oembed = OEmbedPresenter.new(post, params.slice(:format, :maxheight, :minheight))
+ render :json => oembed
+ else
+ render :nothing => true, :status => 404
+ end
+ end
+
def destroy
@post = current_user.posts.where(:id => params[:id]).first
if @post
@@ -76,6 +81,16 @@ class PostsController < ApplicationController
private
+ def find_by_guid_or_id_with_current_user(id)
+ key = id.to_s.length <= 8 ? :id : :guid
+ if user_signed_in?
+ current_user.find_visible_shareable_by_id(Post, id, :key => key)
+ else
+ Post.where(key => id, :public => true).includes(:author, :comments => :author).first
+ end
+
+ end
+
def set_format_if_malformed_from_status_net
request.format = :html if request.format == 'application/html+xml'
end
diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb
index 35e981a1c..e12ed5901 100644
--- a/app/helpers/posts_helper.rb
+++ b/app/helpers/posts_helper.rb
@@ -20,6 +20,7 @@ module PostsHelper
def post_iframe_url(post_id, opts={})
opts[:width] ||= 516
opts[:height] ||= 315
- "".html_safe
+ host = AppConfig[:pod_uri].port ==80 ? AppConfig[:pod_uri].host : "#{AppConfig[:pod_uri].host}:#{AppConfig[:pod_uri].port}"
+ "".html_safe
end
end
diff --git a/app/presenters/o_embed_presenter.rb b/app/presenters/o_embed_presenter.rb
new file mode 100644
index 000000000..0723b7df9
--- /dev/null
+++ b/app/presenters/o_embed_presenter.rb
@@ -0,0 +1,49 @@
+require 'uri'
+class OEmbedPresenter
+ include PostsHelper
+ include ActionView::Helpers::TextHelper
+
+ def initialize(post, opts = {})
+ @post = post
+ @opts = opts
+ end
+
+ def to_json(opts={})
+ as_json(opts).to_json
+ end
+
+ def as_json(opts={})
+ {
+ :provider_name => "Diaspora",
+ :provider_url => AppConfig[:pod_url],
+ :type => 'rich',
+ :version => '1.0',
+ :title => post_title,
+ :author_name => post_author,
+ :author_url => post_author_url,
+ :width => @opts.fetch(:maxwidth, 516),
+ :height => @opts.fetch(:maxheight, 320),
+ :html => iframe_html
+ }
+ end
+
+ def self.id_from_url(url)
+ URI.parse(url).path.gsub(%r{\/posts\/|\/p\/}, '')
+ end
+
+ def post_title
+ post_page_title(@post)
+ end
+
+ def post_author
+ @post.author.name
+ end
+
+ def post_author_url
+ Rails.application.routes.url_helpers.person_url(@post.author, :host => AppConfig[:pod_uri].host)
+ end
+
+ def iframe_html
+ post_iframe_url(@post.id, :height => @opts[:maxheight], :width => @opts[:maxwidth])
+ end
+end
\ No newline at end of file
diff --git a/app/views/aspects/_aspect_stream.haml b/app/views/aspects/_aspect_stream.haml
index 79771a15b..91cbbbe81 100644
--- a/app/views/aspects/_aspect_stream.haml
+++ b/app/views/aspects/_aspect_stream.haml
@@ -13,6 +13,7 @@
#main_stream.stream
#paginate
+ = image_tag "static-loader.png", :height => 14, :width => 14, :class => "loader hidden"
- if current_user.contacts.size < 2
= render 'aspects/no_contacts_message'
\ No newline at end of file
diff --git a/app/views/layouts/post.html.haml b/app/views/layouts/post.html.haml
index 75ae0cb19..98f941219 100644
--- a/app/views/layouts/post.html.haml
+++ b/app/views/layouts/post.html.haml
@@ -44,6 +44,8 @@
= set_asset_host
= translation_missing_warnings
= current_user_atom_tag
+ - if @post.present?
+ %link{:rel => 'alternate', :type => "application/json+oembed", :href => "#{oembed_url(:url => post_url(@post))}"}
= yield(:head)
= csrf_meta_tag
diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml
index 54ae49a59..4448b1d22 100644
--- a/app/views/people/show.html.haml
+++ b/app/views/people/show.html.haml
@@ -34,3 +34,4 @@
= t('.ignoring', :name => @person.first_name)
#paginate
+ = image_tag "static-loader.png", :height => 14, :width => 14, :class => "loader hidden"
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 0a01b0369..1aaa3b27a 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -47,4 +47,6 @@ Diaspora::Application.configure do
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
+
+ config.default_url_options = { :host => AppConfig[:pod_uri].host}
end
diff --git a/config/initializers/silence_assets.rb b/config/initializers/silence_assets.rb
new file mode 100644
index 000000000..2bae826fd
--- /dev/null
+++ b/config/initializers/silence_assets.rb
@@ -0,0 +1,16 @@
+if Rails.env.development?
+
+ Rails.application.assets.logger = Logger.new('/dev/null')
+
+ Rails::Rack::Logger.class_eval do
+ def call_with_quiet_assets(env)
+ previous_level = Rails.logger.level
+ Rails.logger.level = Logger::ERROR if env['PATH_INFO'] =~ %r{^/assets/}
+ call_without_quiet_assets(env)
+ ensure
+ Rails.logger.level = previous_level
+ end
+ alias_method_chain :call, :quiet_assets
+ end
+
+end
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index d556997cb..daee08957 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,6 +5,7 @@
Diaspora::Application.routes.draw do
mount RailsAdmin::Engine => '/admin_panel', :as => 'rails_admin'
+ get 'oembed' => 'posts#oembed', :as => 'oembed'
# Posting and Reading
resources :reshares
diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb
index 2a53a661b..221beaca8 100644
--- a/spec/controllers/posts_controller_spec.rb
+++ b/spec/controllers/posts_controller_spec.rb
@@ -120,6 +120,19 @@ describe PostsController do
end
end
+ describe 'oembed' do
+ it 'works when you can see it' do
+ sign_in alice
+ get :oembed, :url => "/posts/#{@message.id}"
+ response.body.should match /iframe/
+ end
+
+ it 'returns a 404 response when the post is not found' do
+ get :oembed, :url => "/posts/#{@message.id}"
+ response.should_not be_success
+ end
+ end
+
describe '#destroy' do
before do
sign_in alice
diff --git a/spec/javascripts/app/views/photos_view_spec.js b/spec/javascripts/app/views/photos_view_spec.js
index 51cedb9c7..5f4a8c9d2 100644
--- a/spec/javascripts/app/views/photos_view_spec.js
+++ b/spec/javascripts/app/views/photos_view_spec.js
@@ -2,27 +2,31 @@ describe("app.views.Photos", function() {
beforeEach(function() {
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
- this._photos = $.parseJSON(spec.readFixture("photos_json"))["photos"];
+ this.photos = $.parseJSON(spec.readFixture("photos_json"))["photos"];
- this.photos = new app.models.Stream([], {collection: app.collections.Photos});
- this.photos.add(this._photos);
+ this.stream = new app.models.Stream([], {collection: app.collections.Photos});
+ this.stream.add(this.photos);
- this.view = new app.views.Photos({model : this.photos});
+ this.view = new app.views.Photos({model : this.stream});
// do this manually because we've moved loadMore into render??
this.view.render();
_.each(this.view.collection.models, function(photo) {
- this.view.addPhoto(photo);
+ this.view.addPost(photo);
}, this);
});
describe("initialize", function() {
- // nothing there yet
+ it("binds an infinite scroll listener", function() {
+ spyOn($.fn, "scroll");
+ new app.views.Stream({model : this.stream});
+ expect($.fn.scroll).toHaveBeenCalled();
+ });
});
describe("#render", function() {
beforeEach(function() {
- this.photo = this.photos.items.models[0];
+ this.photo = this.stream.items.models[0];
this.photoElement = $(this.view.$("#" + this.photo.get("guid")));
});
@@ -32,14 +36,4 @@ describe("app.views.Photos", function() {
});
});
});
-
- describe("removeLoader", function() {
- it("emptys the pagination div when the stream is fetched", function() {
- $("#jasmine_content").append($('