diff --git a/app/models/dmail.rb b/app/models/dmail.rb new file mode 100644 index 000000000..376c9a3fd --- /dev/null +++ b/app/models/dmail.rb @@ -0,0 +1,94 @@ +class Dmail < ActiveRecord::Base + validates_presence_of :to_id + validates_presence_of :from_id + validates_format_of :title, :with => /\S/ + validates_format_of :body, :with => /\S/ + belongs_to :owner, :class_name => "User" + belongs_to :to, :class_name => "User" + belongs_to :from, :class_name => "User" + after_create :update_recipient + after_create :send_dmail + attr_accessible :title, :body, :is_deleted + scope :for, lambda {|user| where(["owner_id = ?", user])} + scope :inbox, where("to_id = owner_id") + scope :sent, where("from_id = owner_id") + scope :active, where(["is_deleted = ?", false]) + scope :deleted, where(["is_deleted = ?", true]) + scope :search_message, lambda {|query| where(["message_index @@ plainto_tsquery(?)", query])} + + module AddressMethods + def to_name + User.find_pretty_name(to_id) + end + + def from_name + User.find_pretty_name(from_id) + end + + def to_name=(name) + user = User.find_by_name(name) + return if user.nil? + self.to_id = user.id + end + + def from_name=(name) + user = User.find_by_name(name) + return if user.nil? + self.from_id = user.id + end + end + + module FactoryMethods + module ClassMethods + def create_new(dmail) + copy = dmail.clone + copy.owner_id = dmail.to_id + copy.save + + copy = dmail.clone + copy.owner_id = dmail.from_id + copy.save + end + end + + def self.included(m) + m.extend(ClassMethods) + end + + def build_response + Dmail.new do |dmail| + dmail.title = "Re: #{title}" + dmail.owner_id = from_id + dmail.body = quoted_body + dmail.to_id = from_id + dmail.from_id = to_id + dmail.parent_id = id + end + end + end + + include AddressMethods + include FactoryMethods + + def quoted_body + "[quote]#{body}[/quote]" + end + + def send_dmail + if to.receive_email_notifications? && to.email.include?("@") + UserMailer.dmail_notice(self).deliver + end + end + + def mark_as_read! + update_attribute(:is_read, true) + + unless Dmail.exists?(["to_id = ? AND is_read = false", to_id]) + to.update_attribute(:has_mail, false) + end + end + + def update_recipient + to.update_attribute(:has_mail, true) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 73bbe238d..c8e3c6d54 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,6 +23,10 @@ class User < ActiveRecord::Base select_value_sql("SELECT name FROM users WHERE id = ?", user_id) || Danbooru.config.default_guest_name end end + + def find_pretty_name(user_id) + find_name.tr("_", " ") + end end def self.included(m) diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb new file mode 100644 index 000000000..1f464f845 --- /dev/null +++ b/app/models/user_mailer.rb @@ -0,0 +1,24 @@ +class UserMailer < ActionMailer::Base + default :host => Danbooru.config.server_host, :from => Danbooru.config.contact_email, :content_type => "text/html" + + def password_reset(user, new_password) + @user = user + @new_password = new_password + mail(:to => @user.email, :subject => "#{Danbooru.config.app_name} - Password Reset") + end + + def name_reminder(user) + @user = user + mail(:to => user.email, :subject => "#{Danbooru.config.app_name} - Name Reminder") + end + + def deletion(user) + @user = user + mail(:to => user.email, :subject => "#{}") + end + + def dmail_notice(dmail) + @dmail = dmail + mail(:to => dmail.to.email, :subject => "#{Danbooru.config.app_name} - Message received from #{dmail.from.name}") + end +end diff --git a/app/views/user_mailer/dmail.html.erb b/app/views/user_mailer/dmail.html.erb new file mode 100644 index 000000000..da309e93d --- /dev/null +++ b/app/views/user_mailer/dmail.html.erb @@ -0,0 +1,5 @@ +

<%= h @dmail.fromr.name %> said:

+ +
+ <%= h @dmail.body %> +
diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb index 171ec5b15..62e032a65 100644 --- a/config/danbooru_default_config.rb +++ b/config/danbooru_default_config.rb @@ -10,6 +10,11 @@ module Danbooru "Danbooru" end + # Contact email address of the admin. + def contact_email + "webmaster#{server_host}" + end + # Stripped of any special characters. def safe_app_name app_name.gsub(/[^a-zA-Z0-9_-]/, "_") diff --git a/db/development_structure.sql b/db/development_structure.sql index 1b57cc3dd..fe2faece1 100644 --- a/db/development_structure.sql +++ b/db/development_structure.sql @@ -353,6 +353,44 @@ CREATE SEQUENCE comments_id_seq ALTER SEQUENCE comments_id_seq OWNED BY comments.id; +-- +-- Name: dmails; Type: TABLE; Schema: public; Owner: -; Tablespace: +-- + +CREATE TABLE dmails ( + id integer NOT NULL, + owner_id integer NOT NULL, + from_id integer NOT NULL, + to_id integer NOT NULL, + parent_id integer, + title character varying(255) NOT NULL, + body text NOT NULL, + message_index tsvector NOT NULL, + is_read boolean DEFAULT false NOT NULL, + created_at timestamp without time zone, + updated_at timestamp without time zone +); + + +-- +-- Name: dmails_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE dmails_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + + +-- +-- Name: dmails_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE dmails_id_seq OWNED BY dmails.id; + + -- -- Name: favorites_0; Type: TABLE; Schema: public; Owner: -; Tablespace: -- @@ -1245,6 +1283,13 @@ ALTER TABLE comment_votes ALTER COLUMN id SET DEFAULT nextval('comment_votes_id_ ALTER TABLE comments ALTER COLUMN id SET DEFAULT nextval('comments_id_seq'::regclass); +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE dmails ALTER COLUMN id SET DEFAULT nextval('dmails_id_seq'::regclass); + + -- -- Name: id; Type: DEFAULT; Schema: public; Owner: - -- @@ -1477,6 +1522,14 @@ ALTER TABLE ONLY comments ADD CONSTRAINT comments_pkey PRIMARY KEY (id); +-- +-- Name: dmails_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: +-- + +ALTER TABLE ONLY dmails + ADD CONSTRAINT dmails_pkey PRIMARY KEY (id); + + -- -- Name: favorites_0_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- @@ -1788,6 +1841,27 @@ CREATE INDEX index_comments_on_body_index ON comments USING gin (body_index); CREATE INDEX index_comments_on_post_id ON comments USING btree (post_id); +-- +-- Name: index_dmails_on_message_index; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_dmails_on_message_index ON dmails USING gin (message_index); + + +-- +-- Name: index_dmails_on_owner_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_dmails_on_owner_id ON dmails USING btree (owner_id); + + +-- +-- Name: index_dmails_on_parent_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_dmails_on_parent_id ON dmails USING btree (parent_id); + + -- -- Name: index_favorites_0_on_post_id; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -2151,6 +2225,16 @@ CREATE TRIGGER trigger_comments_on_update EXECUTE PROCEDURE tsvector_update_trigger('body_index', 'pg_catalog.english', 'body'); +-- +-- Name: trigger_dmails_on_update; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trigger_dmails_on_update + BEFORE INSERT OR UPDATE ON dmails + FOR EACH ROW + EXECUTE PROCEDURE tsvector_update_trigger('message_index', 'pg_catalog.english', 'title', 'body'); + + -- -- Name: trigger_posts_on_tag_index_update; Type: TRIGGER; Schema: public; Owner: - -- @@ -2217,4 +2301,6 @@ INSERT INTO schema_migrations (version) VALUES ('20100215224635'); INSERT INTO schema_migrations (version) VALUES ('20100215225710'); -INSERT INTO schema_migrations (version) VALUES ('20100215230642'); \ No newline at end of file +INSERT INTO schema_migrations (version) VALUES ('20100215230642'); + +INSERT INTO schema_migrations (version) VALUES ('20100219230537'); \ No newline at end of file diff --git a/db/migrate/20100219230537_create_dmails.rb b/db/migrate/20100219230537_create_dmails.rb new file mode 100644 index 000000000..f0fc4c8da --- /dev/null +++ b/db/migrate/20100219230537_create_dmails.rb @@ -0,0 +1,26 @@ +class CreateDmails < ActiveRecord::Migration + def self.up + create_table :dmails do |t| + t.column :owner_id, :integer, :null => false + t.column :from_id, :integer, :null => false + t.column :to_id, :integer, :null => false + t.column :parent_id, :integer + t.column :title, :string, :null => false + t.column :body, :text, :null => false + t.column :message_index, "tsvector", :null => false + t.column :is_read, :boolean, :null => false, :default => false + t.column :is_deleted, :boolean, :null => false, :default => false + t.timestamps + end + + add_index :dmails, :owner_id + add_index :dmails, :parent_id + + execute "CREATE INDEX index_dmails_on_message_index ON dmails USING GIN (message_index)" + execute "CREATE TRIGGER trigger_dmails_on_update BEFORE INSERT OR UPDATE ON dmails FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('message_index', 'pg_catalog.english', 'title', 'body')" + end + + def self.down + drop_table :dmails + end +end diff --git a/lib/dtext.rb b/lib/dtext.rb new file mode 100644 index 000000000..98bb0c9cb --- /dev/null +++ b/lib/dtext.rb @@ -0,0 +1,144 @@ +#!/usr/bin/env ruby + +require 'cgi' + +module DText + def parse_inline(str, options = {}) + str = str.gsub(/&/, "&") + str.gsub!(//, ">") + str.gsub!(/\[\[.+?\]\]/m) do |tag| + tag = tag[2..-3] + if tag =~ /^(.+?)\|(.+)$/ + tag = $1 + name = $2 + '' + name + '' + else + '' + tag + '' + end + end + str.gsub!(/\{\{.+?\}\}/m) do |tag| + tag = tag[2..-3] + '' + tag + '' + end + str.gsub!(/[Pp]ost #(\d+)/, 'post #\1') + str.gsub!(/[Ff]orum #(\d+)/, 'forum #\1') + str.gsub!(/[Cc]omment #(\d+)/, 'comment #\1') + str.gsub!(/[Pp]ool #(\d+)/, 'pool #\1') + str.gsub!(/\n/m, "
") + str.gsub!(/\[b\](.+?)\[\/b\]/, '\1') + str.gsub!(/\[i\](.+?)\[\/i\]/, '\1') + str.gsub!(/\[spoilers?\](.+?)\[\/spoilers?\]/m, '\1') + str.gsub!(/("[^"]+":(http:\/\/|\/)\S+|http:\/\/\S+)/m) do |link| + if link =~ /^"([^"]+)":(.+)$/ + text = $1 + link = $2 + else + text = link + end + + if link =~ /([;,.!?\)\]<>])$/ + link.chop! + ch = $1 + else + ch = "" + end + + link.gsub!(/"/, '"') + '' + text + '' + ch + end + str + end + + def parse_list(str, options = {}) + html = "" + layout = [] + nest = 0 + + str.split(/\n/).each do |line| + if line =~ /^\s*(\*+) (.+)/ + nest = $1.size + content = parse_inline($2) + else + content = parse_inline(line) + end + + if nest > layout.size + html += "