diff --git a/Gemfile b/Gemfile index 34ac839f8..b0c56c115 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,6 @@ gem "nokogiri", "1.4.3.1" #Security gem 'devise', '1.1.3' -gem 'devise-mongo_mapper', :git => 'git://github.com/collectiveidea/devise-mongo_mapper' gem 'devise_invitable','0.3.5' #Authentication diff --git a/Gemfile.lock b/Gemfile.lock index c00287981..46265611b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,13 +6,6 @@ GIT activesupport (>= 2.3.0) nokogiri (>= 1.3.3) -GIT - remote: git://github.com/collectiveidea/devise-mongo_mapper - revision: fa2f20310e0988295adc192255d3b1cedee1b412 - specs: - devise-mongo_mapper (0.0.1) - devise (~> 1.1.0) - GIT remote: git://github.com/iain/http_accept_language.git revision: 0b78aa7849fc90cf9e12586af162fa4c408a795d @@ -390,7 +383,6 @@ DEPENDENCIES cucumber-rails (= 0.3.2) database_cleaner (= 0.5.2) devise (= 1.1.3) - devise-mongo_mapper! devise_invitable (= 0.3.5) em-http-request! em-websocket! diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 02fad11e9..c41dca80f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -20,14 +20,14 @@ module ApplicationHelper end def aspects_with_post aspects, post - aspects.select do |a| - post.aspect_ids.include?(a.id) + aspects.select do |aspect| + aspect.posts.include?(post) end end def aspects_without_post aspects, post - aspects.reject do |a| - post.aspect_ids.include?(a.id) + aspects.reject do |aspect| + aspect.posts.include?(post) end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 5d58709f9..55308962d 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -11,10 +11,10 @@ class Comment < ActiveRecord::Base include Encryptable include Diaspora::Socketable - xml_reader :text - xml_reader :diaspora_handle - xml_reader :post_guid - xml_reader :guid + xml_accessor :text + xml_accessor :diaspora_handle + xml_accessor :post_guid + xml_accessor :guid belongs_to :post belongs_to :person diff --git a/app/models/photo.rb b/app/models/photo.rb index 6b5232fb4..e7784dd24 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -3,23 +3,13 @@ # the COPYRIGHT file. class Photo < Post - require 'carrierwave/orm/mongomapper' - include MongoMapper::Document + require 'carrierwave/orm/activerecord' mount_uploader :image, ImageUploader xml_accessor :remote_photo xml_accessor :caption xml_reader :status_message_id - key :caption, String - key :remote_photo_path - key :remote_photo_name - key :random_string - - key :status_message_id, ObjectId - - timestamps! - belongs_to :status_message attr_accessible :caption, :pending diff --git a/app/models/post.rb b/app/models/post.rb index dc3cc201a..cb5dd8d5b 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -2,33 +2,22 @@ # licensed under the Affero General Public License version 3 or later. See # the COPYRIGHT file. -class Post +class Post < ActiveRecord::Base require File.join(Rails.root, 'lib/encryptable') require File.join(Rails.root, 'lib/diaspora/web_socket') - include MongoMapper::Document include ApplicationHelper include ROXML include Diaspora::Webhooks - xml_reader :_id + xml_reader :guid xml_reader :diaspora_handle xml_reader :public xml_reader :created_at - - key :public, Boolean, :default => false - - key :diaspora_handle, String - key :user_refs, Integer, :default => 0 - key :pending, Boolean, :default => false - key :aspect_ids, Array, :typecast => 'ObjectId' - - many :comments, :class_name => 'Comment', :foreign_key => :post_id, :order => 'created_at ASC' - many :aspects, :in => :aspect_ids, :class_name => 'Aspect' + has_many :comments, :order => 'created_at ASC' + has_and_belongs_to_many :aspects belongs_to :person, :class_name => 'Person' - timestamps! - cattr_reader :per_page @@per_page = 10 @@ -36,11 +25,13 @@ class Post after_destroy :destroy_comments attr_accessible :user_refs - + def self.instantiate params new_post = self.new params.to_hash new_post.person = params[:person] - new_post.aspect_ids = params[:aspect_ids] + params[:aspect_ids].each do |aspect_id| + new_post.aspects << Aspect.find_by_id(aspect_id) + end if params[:aspect_ids] new_post.public = params[:public] new_post.pending = params[:pending] new_post.diaspora_handle = new_post.person.diaspora_handle @@ -49,10 +40,10 @@ class Post def as_json(opts={}) { - :post => { - :id => self.id, - :person => self.person.as_json, - } + :post => { + :id => self.id, + :person => self.person.as_json, + } } end @@ -62,7 +53,7 @@ class Post protected def destroy_comments - comments.each{|c| c.destroy} + comments.each { |c| c.destroy } end def propogate_retraction diff --git a/app/models/status_message.rb b/app/models/status_message.rb index a9eb800f7..9e6de3c52 100644 --- a/app/models/status_message.rb +++ b/app/models/status_message.rb @@ -10,10 +10,9 @@ class StatusMessage < Post validates_length_of :message, :maximum => 1000, :message => "please make your status messages less than 1000 characters" xml_name :status_message - xml_reader :message + xml_accessor :message - key :message, String - many :photos, :class => Photo, :foreign_key => :status_message_id, :dependent => :destroy + has_many :photos, :dependent => :destroy validate :message_or_photos_present? attr_accessible :message @@ -21,6 +20,7 @@ class StatusMessage < Post before_save do get_youtube_title message end + def to_activity <<-XML @@ -35,7 +35,6 @@ class StatusMessage < Post XML end - def public_message(length, url = "") space_for_url = url.blank? ? 0 : (url.length + 1) truncated = truncate(self.message, :length => (length - space_for_url)) @@ -50,6 +49,5 @@ class StatusMessage < Post errors[:base] << 'Status message requires a message or at least one photo' end end - end diff --git a/app/models/user.rb b/app/models/user.rb index e7e1583cb..6a7de3587 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,391 +7,367 @@ require File.join(Rails.root, 'lib/salmon/salmon') require 'rest-client' class User < ActiveRecord::Base -# include MongoMapper::Document -# include Diaspora::UserModules -# include Encryptor::Private -# -# plugin MongoMapper::Devise -# -# devise :invitable, :database_authenticatable, :registerable, -# :recoverable, :rememberable, :trackable, :validatable, -# :timeoutable -# -# key :username -# key :serialized_private_key, String -# key :invites, Integer, :default => 5 -# key :invitation_token, String -# key :invitation_sent_at, DateTime + include Diaspora::UserModules + include Encryptor::Private + + devise :invitable, :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable, + :timeoutable + # key :visible_post_ids, Array, :typecast => 'ObjectId' # key :visible_person_ids, Array, :typecast => 'ObjectId' # -# key :getting_started, Boolean, :default => true -# key :disable_mail, Boolean, :default => false -# -# key :language, String -# -# before_validation :strip_and_downcase_username, :on => :create -# before_validation :set_current_language, :on => :create -# -# validates_presence_of :username -# validates_uniqueness_of :username, :case_sensitive => false -# validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/ -# validates_length_of :username, :maximum => 32 -# validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES -# -# validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?} -# validates_associated :person -# -# one :person, :class => Person, :foreign_key => :owner_id -# -# many :invitations_from_me, :class => Invitation, :foreign_key => :from_id -# many :invitations_to_me, :class => Invitation, :foreign_key => :to_id -# has_many :aspects, :dependent => :destroy -# has_many :aspect_memberships, :through => :aspects + before_validation :strip_and_downcase_username, :on => :create + before_validation :set_current_language, :on => :create + + validates_presence_of :username + validates_uniqueness_of :username, :case_sensitive => false + validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/ + validates_length_of :username, :maximum => 32 + validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES + + validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?} + validates_associated :person + + has_one :person, :foreign_key => :owner_id + + has_many :invitations_from_me, :class_name => 'Invitation', :foreign_key => :sender_id + has_many :invitations_to_me, :class_name => 'Invitation', :foreign_key => :recipient_id + has_many :aspects, :dependent => :destroy + has_many :aspect_memberships, :through => :aspects # many :visible_people, :in => :visible_person_ids, :class => Person # One of these needs to go # many :raw_visible_posts, :in => :visible_post_ids, :class => Post -# + # many :services, :class => Service -# timestamps! -# #after_create :seed_aspects -# -# before_destroy :disconnect_everyone, :remove_person -# before_save do -# person.save if person -# end -# -# attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail -# -# def strip_and_downcase_username -# if username.present? -# username.strip! -# username.downcase! -# end -# end -# -# def set_current_language -# self.language = I18n.locale.to_s if self.language.blank? -# end -# -# def self.find_for_authentication(conditions={}) -# conditions[:username] = conditions[:username].downcase -# if conditions[:username] =~ /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i # email regex -# conditions[:email] = conditions.delete(:username) -# end -# super -# end -# -# ######## Making things work ######## -# key :email, String -# -# def method_missing(method, *args) -# self.person.send(method, *args) if self.person -# end -# -# ######### Aspects ###################### -# def drop_aspect(aspect) -# if aspect.contacts.count == 0 -# aspect.destroy -# else -# raise "Aspect not empty" -# end -# end -# -# def move_contact(person, to_aspect, from_aspect) -# contact = contact_for(person) -# if to_aspect == from_aspect -# true -# elsif add_contact_to_aspect(contact, to_aspect) -# delete_person_from_aspect(person.id, from_aspect.id) -# end -# end -# -# def add_contact_to_aspect(contact, aspect) -# return true if contact.aspect_ids.include?(aspect.id) -# contact.aspects << aspect -# contact.save! -# end -# -# def delete_person_from_aspect(person_id, aspect_id, opts = {}) -# aspect = Aspect.find(aspect_id) -# raise "Can not delete a person from an aspect you do not own" unless aspect.user == self -# contact = contact_for Person.find(person_id) -# -# if opts[:force] || contact.aspect_ids.count > 1 -# contact.aspect_ids.delete aspect.id -# contact.save! -# aspect.save! -# else -# raise "Can not delete a person from last aspect" -# end -# end -# -# ######## Posting ######## -# def build_post(class_name, opts = {}) -# opts[:person] = self.person -# opts[:diaspora_handle] = opts[:person].diaspora_handle -# -# model_class = class_name.to_s.camelize.constantize -# model_class.instantiate(opts) -# end -# -# def dispatch_post(post, opts = {}) -# aspect_ids = opts.delete(:to) -# -# Rails.logger.info("event=dispatch user=#{diaspora_handle} post=#{post.id.to_s}") -# push_to_aspects(post, aspects_from_ids(aspect_ids)) -# Resque.enqueue(Jobs::PostToServices, self.id, post.id, opts[:url]) if post.public -# end -# -# def post_to_services(post, url) -# if post.respond_to?(:message) -# self.services.each do |service| -# service.post(post, url) -# end -# end -# end -# -# def post_to_hub(post) -# Rails.logger.debug("event=post_to_service type=pubsub sender_handle=#{self.diaspora_handle}") -# EventMachine::PubSubHubbub.new(APP_CONFIG[:pubsub_server]).publish self.public_url -# end -# -# def update_post(post, post_hash = {}) -# if self.owns? post -# post.update_attributes(post_hash) -# aspects = aspects_with_post(post.id) -# self.push_to_aspects(post, aspects) -# end -# end -# -# def add_to_streams(post, aspect_ids) -# self.raw_visible_posts << post -# self.save -# -# post.socket_to_uid(id, :aspect_ids => aspect_ids) if post.respond_to? :socket_to_uid -# target_aspects = aspects_from_ids(aspect_ids) -# target_aspects.each do |aspect| -# aspect.posts << post -# aspect.save -# end -# end -# -# def aspects_from_ids(aspect_ids) -# if aspect_ids == "all" || aspect_ids == :all -# self.aspects -# else -# if aspect_ids.respond_to? :to_id -# aspect_ids = [aspect_ids] -# end -# aspect_ids.map!{ |x| x.to_id } -# aspects.all(:id.in => aspect_ids) -# end -# end -# -# def push_to_aspects(post, aspects) -# #send to the aspects -# target_aspect_ids = aspects.map {|a| a.id} -# -# target_contacts = Contact.all(:aspect_ids.in => target_aspect_ids, :pending => false) -# -# post_to_hub(post) if post.respond_to?(:public) && post.public -# push_to_people(post, self.person_objects(target_contacts)) -# end -# -# def push_to_people(post, people) -# salmon = salmon(post) -# people.each do |person| -# push_to_person(salmon, post, person) -# end -# end -# -# def push_to_person(salmon, post, person) -# person.reload # Sadly, we need this for Ruby 1.9. -# # person.owner will always return a ProxyObject. -# # calling nil? performs a necessary evaluation. -# if person.owner_id -# Rails.logger.info("event=push_to_person route=local sender=#{self.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{post.class}") -# -# if post.is_a?(Post) || post.is_a?(Comment) -# Resque.enqueue(Jobs::ReceiveLocal, person.owner_id, self.person.id, post.class.to_s, post.id) -# else -# Resque.enqueue(Jobs::Receive, person.owner_id, post.to_diaspora_xml, self.person.id) -# end -# else -# xml = salmon.xml_for person -# Rails.logger.info("event=push_to_person route=remote sender=#{self.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{post.class}") -# MessageHandler.add_post_request(person.receive_url, xml) -# end -# end -# -# def salmon(post) -# created_salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) -# created_salmon -# end -# -# ######## Commenting ######## -# def build_comment(text, options = {}) -# comment = Comment.new(:person_id => self.person.id, -# :diaspora_handle => self.person.diaspora_handle, -# :text => text, -# :post => options[:on]) -# -# #sign comment as commenter -# comment.creator_signature = comment.sign_with_key(self.encryption_key) -# -# if !comment.post_id.blank? && owns?(comment.post) -# #sign comment as post owner -# comment.post_creator_signature = comment.sign_with_key(self.encryption_key) -# end -# -# comment -# end -# -# def dispatch_comment(comment) -# if owns? comment.post -# #push DOWNSTREAM (to original audience) -# Rails.logger.info "event=dispatch_comment direction=downstream user=#{self.diaspora_handle} comment=#{comment.id}" -# aspects = aspects_with_post(comment.post_id) -# -# #just socket to local users, as the comment has already -# #been associated and saved by post owner -# # (we'll push to all of their aspects for now, the comment won't -# # show up via js where corresponding posts are not present) -# -# people_in_aspects(aspects, :type => 'local').each do |person| -# comment.socket_to_uid(person.owner_id, :aspect_ids => 'all') -# end -# -# #push to remote people -# push_to_people(comment, people_in_aspects(aspects, :type => 'remote')) -# -# elsif owns? comment -# #push UPSTREAM (to poster) -# Rails.logger.info "event=dispatch_comment direction=upstream user=#{self.diaspora_handle} comment=#{comment.id}" -# push_to_people comment, [comment.post.person] -# end -# end -# -# ######### Mailer ####################### -# def mail(job, *args) -# unless self.disable_mail -# Resque.enqueue(job, *args) -# end -# end -# -# ######### Posts and Such ############### -# def retract(post) -# aspect_ids = aspects_with_post(post.id) -# aspect_ids.map! { |aspect| aspect.id.to_s } -# -# post.unsocket_from_uid(self.id, :aspect_ids => aspect_ids) if post.respond_to? :unsocket_from_uid -# retraction = Retraction.for(post) -# push_to_people retraction, people_in_aspects(aspects_with_post(post.id)) -# retraction -# end -# -# ########### Profile ###################### -# def update_profile(params) -# if params[:photo] -# params[:photo].update_attributes(:pending => false) if params[:photo].pending -# params[:image_url] = params[:photo].url(:thumb_large) -# params[:image_url_medium] = params[:photo].url(:thumb_medium) -# params[:image_url_small] = params[:photo].url(:thumb_small) -# end -# if self.person.profile.update_attributes(params) -# push_to_people profile, self.person_objects(contacts(:pending => false)) -# true -# else -# false -# end -# end -# -# ###Invitations############ -# def invite_user(email, aspect_id, invite_message = "") -# aspect_object = Aspect.first(:user_id => self.id, :id => aspect_id) -# if aspect_object -# Invitation.invite(:email => email, -# :from => self, -# :into => aspect_object, -# :message => invite_message) -# else -# false -# end -# end -# -# def accept_invitation!(opts = {}) -# if self.invited? -# log_string = "event=invitation_accepted username=#{opts[:username]} " -# log_string << "inviter=#{invitations_to_me.first.from.diaspora_handle}" if invitations_to_me.first -# Rails.logger.info log_string -# self.setup(opts) -# self.invitation_token = nil -# self.password = opts[:password] -# self.password_confirmation = opts[:password_confirmation] -# self.save! -# invitations_to_me.each{|invitation| invitation.to_request!} -# -# self.reload # Because to_request adds a request and saves elsewhere -# self -# end -# end -# -# ###Helpers############ -# def self.build(opts = {}) -# u = User.new(opts) -# u.email = opts[:email] -# u.setup(opts) -# u -# end -# -# def setup(opts) -# self.username = opts[:username] -# self.valid? -# errors = self.errors -# errors.delete :person -# return if errors.size > 0 -# -# opts[:person] ||= {} -# opts[:person][:profile] ||= Profile.new -# -# self.person = Person.new(opts[:person]) -# self.person.diaspora_handle = "#{opts[:username]}@#{APP_CONFIG[:pod_uri].host}" -# self.person.url = APP_CONFIG[:pod_url] -# -# -# self.serialized_private_key ||= User.generate_key -# self.person.serialized_public_key = OpenSSL::PKey::RSA.new(self.serialized_private_key).public_key -# -# self -# end -# -# def seed_aspects -# self.aspects.create(:name => I18n.t('aspects.seed.family')) -# self.aspects.create(:name => I18n.t('aspects.seed.work')) -# end -# -# def self.generate_key -# key_size = (Rails.env == 'test' ? 512 : 4096) -# OpenSSL::PKey::RSA::generate key_size -# end -# -# def encryption_key -# OpenSSL::PKey::RSA.new(serialized_private_key) -# end -# -# protected -# -# def remove_person -# self.person.destroy -# end -# -# def disconnect_everyone -# contacts.each { |contact| -# if contact.person.owner? -# contact.person.owner.disconnected_by self.person -# else -# self.disconnect contact -# end -# } -# end + + before_destroy :disconnect_everyone, :remove_person + before_save do + person.save if person + end + + attr_accessible :getting_started, :password, :password_confirmation, :language, :disable_mail + + def strip_and_downcase_username + if username.present? + username.strip! + username.downcase! + end + end + + def set_current_language + self.language = I18n.locale.to_s if self.language.blank? + end + + def self.find_for_authentication(conditions={}) + conditions[:username] = conditions[:username].downcase + if conditions[:username] =~ /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i # email regex + conditions[:email] = conditions.delete(:username) + end + super + end + + ######### Aspects ###################### + def drop_aspect(aspect) + if aspect.contacts.count == 0 + aspect.destroy + else + raise "Aspect not empty" + end + end + + def move_contact(person, to_aspect, from_aspect) + contact = contact_for(person) + if to_aspect == from_aspect + true + elsif add_contact_to_aspect(contact, to_aspect) + delete_person_from_aspect(person.id, from_aspect.id) + end + end + + def add_contact_to_aspect(contact, aspect) + return true if contact.aspect_ids.include?(aspect.id) + contact.aspects << aspect + contact.save! + end + + def delete_person_from_aspect(person_id, aspect_id, opts = {}) + aspect = Aspect.find(aspect_id) + raise "Can not delete a person from an aspect you do not own" unless aspect.user == self + contact = contact_for Person.find(person_id) + + if opts[:force] || contact.aspect_ids.count > 1 + contact.aspect_ids.delete aspect.id + contact.save! + aspect.save! + else + raise "Can not delete a person from last aspect" + end + end + + ######## Posting ######## + def build_post(class_name, opts = {}) + opts[:person] = self.person + opts[:diaspora_handle] = opts[:person].diaspora_handle + + model_class = class_name.to_s.camelize.constantize + model_class.instantiate(opts) + end + + def dispatch_post(post, opts = {}) + aspect_ids = opts.delete(:to) + + Rails.logger.info("event=dispatch user=#{diaspora_handle} post=#{post.id.to_s}") + push_to_aspects(post, aspects_from_ids(aspect_ids)) + Resque.enqueue(Jobs::PostToServices, self.id, post.id, opts[:url]) if post.public + end + + def post_to_services(post, url) + if post.respond_to?(:message) + self.services.each do |service| + service.post(post, url) + end + end + end + + def post_to_hub(post) + Rails.logger.debug("event=post_to_service type=pubsub sender_handle=#{self.diaspora_handle}") + EventMachine::PubSubHubbub.new(APP_CONFIG[:pubsub_server]).publish self.public_url + end + + def update_post(post, post_hash = {}) + if self.owns? post + post.update_attributes(post_hash) + aspects = aspects_with_post(post.id) + self.push_to_aspects(post, aspects) + end + end + + def add_to_streams(post, aspect_ids) + self.raw_visible_posts << post + self.save + + post.socket_to_uid(id, :aspect_ids => aspect_ids) if post.respond_to? :socket_to_uid + target_aspects = aspects_from_ids(aspect_ids) + target_aspects.each do |aspect| + aspect.posts << post + aspect.save + end + end + + def aspects_from_ids(aspect_ids) + if aspect_ids == "all" || aspect_ids == :all + self.aspects + else + if aspect_ids.respond_to? :to_id + aspect_ids = [aspect_ids] + end + aspect_ids.map!{ |x| x.to_id } + aspects.all(:id.in => aspect_ids) + end + end + + def push_to_aspects(post, aspects) + #send to the aspects + target_aspect_ids = aspects.map {|a| a.id} + + target_contacts = Contact.all(:aspect_ids.in => target_aspect_ids, :pending => false) + + post_to_hub(post) if post.respond_to?(:public) && post.public + push_to_people(post, self.person_objects(target_contacts)) + end + + def push_to_people(post, people) + salmon = salmon(post) + people.each do |person| + push_to_person(salmon, post, person) + end + end + + def push_to_person(salmon, post, person) + person.reload # Sadly, we need this for Ruby 1.9. + # person.owner will always return a ProxyObject. + # calling nil? performs a necessary evaluation. + if person.owner_id + Rails.logger.info("event=push_to_person route=local sender=#{self.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{post.class}") + + if post.is_a?(Post) || post.is_a?(Comment) + Resque.enqueue(Jobs::ReceiveLocal, person.owner_id, self.person.id, post.class.to_s, post.id) + else + Resque.enqueue(Jobs::Receive, person.owner_id, post.to_diaspora_xml, self.person.id) + end + else + xml = salmon.xml_for person + Rails.logger.info("event=push_to_person route=remote sender=#{self.diaspora_handle} recipient=#{person.diaspora_handle} payload_type=#{post.class}") + MessageHandler.add_post_request(person.receive_url, xml) + end + end + + def salmon(post) + created_salmon = Salmon::SalmonSlap.create(self, post.to_diaspora_xml) + created_salmon + end + + ######## Commenting ######## + def build_comment(text, options = {}) + comment = Comment.new(:person_id => self.person.id, + :text => text, + :post => options[:on]) + + #sign comment as commenter + comment.creator_signature = comment.sign_with_key(self.encryption_key) + + if !comment.post_id.blank? && person.owns?(comment.post) + #sign comment as post owner + comment.post_creator_signature = comment.sign_with_key(self.encryption_key) + end + + comment + end + + def dispatch_comment(comment) + if person.owns? comment.post + #push DOWNSTREAM (to original audience) + Rails.logger.info "event=dispatch_comment direction=downstream user=#{self.person.diaspora_handle} comment=#{comment.id}" + aspects = comment.post.aspects + + #just socket to local users, as the comment has already + #been associated and saved by post owner + # (we'll push to all of their aspects for now, the comment won't + # show up via js where corresponding posts are not present) + + people_in_aspects(aspects, :type => 'local').each do |person| + comment.socket_to_uid(person.owner_id, :aspect_ids => 'all') + end + + #push to remote people + push_to_people(comment, people_in_aspects(aspects, :type => 'remote')) + + elsif owns? comment + #push UPSTREAM (to poster) + Rails.logger.info "event=dispatch_comment direction=upstream user=#{self.diaspora_handle} comment=#{comment.id}" + push_to_people comment, [comment.post.person] + end + end + + ######### Mailer ####################### + def mail(job, *args) + unless self.disable_mail + Resque.enqueue(job, *args) + end + end + + ######### Posts and Such ############### + def retract(post) + aspects = post.aspects + + post.unsocket_from_uid(self.id, :aspect_ids => aspects.map { |a| a.id.to_s }) if post.respond_to? :unsocket_from_uid + retraction = Retraction.for(post) + push_to_people retraction, people_in_aspects(aspects) + retraction + end + + ########### Profile ###################### + def update_profile(params) + if params[:photo] + params[:photo].update_attributes(:pending => false) if params[:photo].pending + params[:image_url] = params[:photo].url(:thumb_large) + params[:image_url_medium] = params[:photo].url(:thumb_medium) + params[:image_url_small] = params[:photo].url(:thumb_small) + end + if self.person.profile.update_attributes(params) + push_to_people profile, self.person_objects(contacts(:pending => false)) + true + else + false + end + end + + ###Invitations############ + def invite_user(email, aspect_id, invite_message = "") + aspect_object = Aspect.first(:user_id => self.id, :id => aspect_id) + if aspect_object + Invitation.invite(:email => email, + :from => self, + :into => aspect_object, + :message => invite_message) + else + false + end + end + + def accept_invitation!(opts = {}) + if self.invited? + log_string = "event=invitation_accepted username=#{opts[:username]} " + log_string << "inviter=#{invitations_to_me.first.from.diaspora_handle}" if invitations_to_me.first + Rails.logger.info log_string + self.setup(opts) + self.invitation_token = nil + self.password = opts[:password] + self.password_confirmation = opts[:password_confirmation] + self.save! + invitations_to_me.each{|invitation| invitation.to_request!} + + self.reload # Because to_request adds a request and saves elsewhere + self + end + end + + ###Helpers############ + def self.build(opts = {}) + u = User.new(opts) + u.email = opts[:email] + u.setup(opts) + u + end + + def setup(opts) + self.username = opts[:username] + self.valid? + errors = self.errors + errors.delete :person + return if errors.size > 0 + + opts[:person] ||= {} + opts[:person][:profile] ||= Profile.new + + self.person = Person.new(opts[:person]) + self.person.diaspora_handle = "#{opts[:username]}@#{APP_CONFIG[:pod_uri].host}" + self.person.url = APP_CONFIG[:pod_url] + + + self.serialized_private_key ||= User.generate_key + self.person.serialized_public_key = OpenSSL::PKey::RSA.new(self.serialized_private_key).public_key + + self + end + + def seed_aspects + self.aspects.create(:name => I18n.t('aspects.seed.family')) + self.aspects.create(:name => I18n.t('aspects.seed.work')) + end + + def self.generate_key + key_size = (Rails.env == 'test' ? 512 : 4096) + OpenSSL::PKey::RSA::generate key_size + end + + def encryption_key + OpenSSL::PKey::RSA.new(serialized_private_key) + end + + protected + + def remove_person + self.person.destroy + end + + def disconnect_everyone + contacts.each { |contact| + if contact.person.owner? + contact.person.owner.disconnected_by self.person + else + self.disconnect contact + end + } + end end diff --git a/config/application.rb b/config/application.rb index e9fa4ad29..cf3df99af 100644 --- a/config/application.rb +++ b/config/application.rb @@ -39,7 +39,6 @@ module Diaspora # Configure generators values. Many other options are available, be sure to check the documentation. config.generators do |g| - g.orm :mongo_mapper g.template_engine :haml g.test_framework :rspec end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 1e3ba8fa0..ba06fe877 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -19,7 +19,7 @@ Devise.setup do |config| # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default), :mongoid # (bson_ext recommended) and :data_mapper (experimental). - require 'devise/orm/mongo_mapper' + require 'devise/orm/active_record' # ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating an user. By default is diff --git a/db/migrate/0000_create_schema.rb b/db/migrate/0000_create_schema.rb index ac17fc236..eb84f7013 100644 --- a/db/migrate/0000_create_schema.rb +++ b/db/migrate/0000_create_schema.rb @@ -86,6 +86,44 @@ class CreateSchema < ActiveRecord::Migration add_index :profiles, [:last_name, :searchable] add_index :profiles, [:first_name, :last_name, :searchable] add_index :profiles, :person_id + + create_table :posts do |t| + t.boolean :public, :default => false + t.string :diaspora_handle + t.boolean :pending + t.integer :user_refs + t.string :type + t.text :message + t.integer :status_message_id + t.text :caption + t.text :remote_photo_path + t.string :remote_photo_name + t.string :random_string + t.timestamps + end + add_index :posts, :type + + create_table :users do |t| + t.string :username + t.text :serialized_private_key + t.integer :invites + t.boolean :getting_started, :default => true + t.boolean :disable_mail, :default => false + t.string :language + t.string :email + + t.database_authenticatable + t.invitable + t.recoverable + t.rememberable + t.trackable + + t.timestamps + end + add_index :users, :username, :unique => true + add_index :users, :email, :unique => true + add_index :users, :invitation_token + end def self.down diff --git a/db/schema.rb b/db/schema.rb index 6bf3a6d3b..9b5852566 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -94,6 +94,24 @@ ActiveRecord::Schema.define(:version => 0) do add_index "people", ["guid"], :name => "index_people_on_guid", :unique => true add_index "people", ["owner_id"], :name => "index_people_on_owner_id", :unique => true + create_table "posts", :force => true do |t| + t.boolean "public", :default => false + t.string "diaspora_handle" + t.boolean "pending" + t.integer "user_refs" + t.string "type" + t.text "message" + t.integer "status_message_id" + t.text "caption" + t.text "remote_photo_path" + t.string "remote_photo_name" + t.string "random_string" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "posts", ["type"], :name => "index_posts_on_type" + create_table "profiles", :force => true do |t| t.string "diaspora_handle" t.string "first_name" @@ -115,4 +133,32 @@ ActiveRecord::Schema.define(:version => 0) do add_index "profiles", ["last_name", "searchable"], :name => "index_profiles_on_last_name_and_searchable" add_index "profiles", ["person_id"], :name => "index_profiles_on_person_id" + create_table "users", :force => true do |t| + t.string "username" + t.text "serialized_private_key" + t.integer "invites" + t.boolean "getting_started", :default => true + t.boolean "disable_mail", :default => false + t.string "language" + t.string "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "password_salt", :default => "", :null => false + t.string "invitation_token", :limit => 20 + t.datetime "invitation_sent_at" + t.string "reset_password_token" + t.string "remember_token" + t.datetime "remember_created_at" + t.integer "sign_in_count", :default => 0 + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "users", ["email"], :name => "index_users_on_email", :unique => true + add_index "users", ["invitation_token"], :name => "index_users_on_invitation_token" + add_index "users", ["username"], :name => "index_users_on_username", :unique => true + end diff --git a/lib/diaspora/user/querying.rb b/lib/diaspora/user/querying.rb index dd8ebbde7..72f40a7ec 100644 --- a/lib/diaspora/user/querying.rb +++ b/lib/diaspora/user/querying.rb @@ -60,7 +60,7 @@ module Diaspora def people_in_aspects(aspects, opts={}) person_ids = contacts_in_aspects(aspects).collect{|contact| contact.person_id} - people = Person.all(:id.in => person_ids) + people = Person.where(:id => person_ids) if opts[:type] == 'remote' people.delete_if{ |p| !p.owner.blank? } @@ -71,14 +71,9 @@ module Diaspora end def aspect_by_id( id ) - id = id.to_id aspects.detect{|x| x.id == id } end - def aspects_with_post( id ) - self.aspects.find_all_by_post_ids( id.to_id ) - end - def aspects_with_person person contact_for(person).aspects end diff --git a/spec/factories.rb b/spec/factories.rb index c9a9d279b..03ceae97b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -32,7 +32,7 @@ Factory.define :user do |u| u.serialized_private_key OpenSSL::PKey::RSA.generate(1024).export u.after_build do |user| user.person = Factory.build(:person, :profile => Factory.create(:profile), - :owner_id => user._id, + :owner_id => user.id, :serialized_public_key => user.encryption_key.public_key.export, :diaspora_handle => "#{user.username}@#{APP_CONFIG[:pod_url].gsub(/(https?:|www\.)\/\//, '').chop!}") end @@ -48,7 +48,7 @@ end Factory.define :status_message do |m| m.sequence(:message) { |n| "jimmy's #{n} whales" } - m.person + m.association :person end Factory.define :photo do |p| diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb index 0752444a0..9a1f9ac98 100644 --- a/spec/models/person_spec.rb +++ b/spec/models/person_spec.rb @@ -5,12 +5,9 @@ require 'spec_helper' describe Person do + before do - @user = Factory(:user) - @user2 = Factory(:user) - @person = Factory.create(:person) - @aspect = @user.aspects.create(:name => "Dudes") - @aspect2 = @user2.aspects.create(:name => "Abscence of Babes") + @person = Factory.create(:person) end describe "delegating" do @@ -121,6 +118,12 @@ describe Person do end describe "disconnecting" do + before do + @user = Factory(:user) + @user2 = Factory(:user) + @aspect = @user.aspects.create(:name => "Dudes") + @aspect2 = @user2.aspects.create(:name => "Abscence of Babes") + end it 'should not delete an orphaned contact' do @user.activate_contact(@person, @aspect) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 936979835..80cf5fabc 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -6,7 +6,7 @@ require 'spec_helper' describe Post do before do - @user = make_user + @user = Factory(:user) @aspect = @user.aspects.create(:name => "winners") end @@ -15,8 +15,8 @@ describe Post do post = Factory.create(:status_message, :person => @user.person) @user.comment "hey", :on => post post.destroy - Post.all(:id => post.id).empty?.should == true - Comment.all(:text => "hey").empty?.should == true + Post.where(:id => post.id).empty?.should == true + Comment.where(:text => "hey").empty?.should == true end end diff --git a/spec/models/status_message_spec.rb b/spec/models/status_message_spec.rb index 53cd3e8c5..1b52e5e79 100644 --- a/spec/models/status_message_spec.rb +++ b/spec/models/status_message_spec.rb @@ -7,7 +7,7 @@ require 'spec_helper' describe StatusMessage do before do - @user = make_user + @user = Factory(:user) @aspect = @user.aspects.create(:name => "losers") end