diff --git a/Gemfile.lock b/Gemfile.lock
old mode 100644
new mode 100755
index a3cbd0a..3e95002
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,32 +1,32 @@
GEM
remote: http://rubygems.org/
specs:
- actionmailer (3.0.9)
- actionpack (= 3.0.9)
- mail (~> 2.2.19)
- actionpack (3.0.9)
- activemodel (= 3.0.9)
- activesupport (= 3.0.9)
+ actionmailer (3.0.7)
+ actionpack (= 3.0.7)
+ mail (~> 2.2.15)
+ actionpack (3.0.7)
+ activemodel (= 3.0.7)
+ activesupport (= 3.0.7)
builder (~> 2.1.2)
erubis (>= 2.6.6)
i18n (~> 0.5.0)
rack (~> 1.2.1)
- rack-mount (~> 0.6.14)
+ rack-mount (>= 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
- activemodel (3.0.9)
- activesupport (= 3.0.9)
+ activemodel (3.0.7)
+ activesupport (= 3.0.7)
builder (~> 2.1.2)
i18n (~> 0.5.0)
- activerecord (3.0.9)
- activemodel (= 3.0.9)
- activesupport (= 3.0.9)
- arel (~> 2.0.10)
+ activerecord (3.0.7)
+ activemodel (= 3.0.7)
+ activesupport (= 3.0.7)
+ arel (~> 2.0.2)
tzinfo (~> 0.3.23)
- activeresource (3.0.9)
- activemodel (= 3.0.9)
- activesupport (= 3.0.9)
- activesupport (3.0.9)
+ activeresource (3.0.7)
+ activemodel (= 3.0.7)
+ activesupport (= 3.0.7)
+ activesupport (3.0.7)
arel (2.0.10)
builder (2.1.2)
erubis (2.7.0)
@@ -41,26 +41,24 @@ GEM
mysql2 (0.2.7)
polyglot (0.3.1)
rack (1.2.3)
- rack-mount (0.6.14)
+ rack-mount (0.8.1)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
- rails (3.0.9)
- actionmailer (= 3.0.9)
- actionpack (= 3.0.9)
- activerecord (= 3.0.9)
- activeresource (= 3.0.9)
- activesupport (= 3.0.9)
+ rails (3.0.7)
+ actionmailer (= 3.0.7)
+ actionpack (= 3.0.7)
+ activerecord (= 3.0.7)
+ activeresource (= 3.0.7)
+ activesupport (= 3.0.7)
bundler (~> 1.0)
- railties (= 3.0.9)
- railties (3.0.9)
- actionpack (= 3.0.9)
- activesupport (= 3.0.9)
+ railties (= 3.0.7)
+ railties (3.0.7)
+ actionpack (= 3.0.7)
+ activesupport (= 3.0.7)
rake (>= 0.8.7)
- rdoc (~> 3.4)
thor (~> 0.14.4)
rake (0.9.2)
- rdoc (3.6.1)
themes_for_rails (0.4.2)
rails (~> 3.0.0)
themes_for_rails
diff --git a/README.markdown b/README.markdown
index 49cab78..d810ce2 100755
--- a/README.markdown
+++ b/README.markdown
@@ -36,3 +36,13 @@ rake db:clear_data
```
* Use it.
+
+### Specific configuration
+
+For themes: if server sends files with no content in production mode comment out
+
+```ruby
+config.action_dispatch.x_sendfile_header = "X-Sendfile"
+```
+
+from _config/environments/production.rb_ file.
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 4bd6e18..d6e2daf 100755
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,15 +2,16 @@ require 'yaml'
class ApplicationController < ActionController::Base
- #unless config.consider_all_requests_local
- # #rescue_from ActionController::RoutingError, :with => :route_not_found
- # rescue_from ActiveRecord::RecordNotFound, :with => :route_not_found
- #end
-
protect_from_forgery
before_filter :load_defaults,:current_user,:set_locale
before_filter :plugins_configuration
+ rescue_from ActiveRecord::RecordNotFound do
+ logger.custom('record_not_found','exc')
+ reset_session
+ redirect_to :controller=>'user', :action => 'login'
+ end
+
################################# protected section ###########################################
protected
@@ -42,7 +43,7 @@ class ApplicationController < ActionController::Base
def check_current_user
if @current_user.nil?
session["return_to"] = request.fullpath
- redirect_to :controller=>"user", :action => "login"
+ redirect_to :controller => 'user', :action => 'login'
return false
end
end
@@ -65,9 +66,5 @@ class ApplicationController < ActionController::Base
WillPaginate::ViewHelpers.pagination_options[:next_label] = t(:next_page)
end
- #def route_not_found
- # render :text => 'What the fuck are you looking for ?', :status => :not_found
- #end
-
end
diff --git a/app/controllers/folders_controller.rb b/app/controllers/folders_controller.rb
index a81a913..6d753f9 100755
--- a/app/controllers/folders_controller.rb
+++ b/app/controllers/folders_controller.rb
@@ -8,8 +8,8 @@ class FoldersController < ApplicationController
before_filter :check_current_user ,:selected_folder
- before_filter :open_imap_session, :except => [:index,:refresh_status,:show_hide]
- after_filter :close_imap_session, :except => [:index,:refresh_status,:show_hide]
+ before_filter :open_imap_session, :except => [:index,:show_hide]
+ after_filter :close_imap_session, :except => [:index,:show_hide]
before_filter :get_folders
@@ -25,6 +25,7 @@ class FoldersController < ApplicationController
render "index"
else
begin
+ #TODO recreate local copy of folders
if params["parent_folder"].empty?
@mailbox.create_folder(params[:folder])
else
@@ -39,10 +40,11 @@ class FoldersController < ApplicationController
render 'index'
return
end
- redirect_to :action => 'refresh', :flash => t(:was_created,:scope=>:folder), :type => :notice
+ flash[:notice] = t(:was_created,:scope=>:folder)
+ redirect_to :action => 'index'
end
end
- # FIXME if you delete folder you should change current folder because if you go to messages/index you got nil
+
def delete
if params["folder"].empty?
flash[:warning] = t(:to_delete_empty,:scope=>:folder)
@@ -59,25 +61,33 @@ class FoldersController < ApplicationController
raise Exception, t(:system_folder)
end
@mailbox.delete_folder(folder.full_name)
+ logger.custom('c',@current_folder.inspect)
+ logger.custom('f',folder.inspect)
+ if @current_folder.eql? folder
+ session[:selected_folder] = $defaults['mailbox_inbox']
+ end
+ folder.destroy
rescue Exception => e
flash[:error] = t(:can_not_delete,:scope=>:folder) + ' (' + e.to_s + ')'
render 'index'
return
end
- redirect_to :action => 'refresh', :flash => t(:was_deleted,:scope=>:folder), :type => :notice
+ flash[:notice] = t(:was_deleted,:scope=>:folder)
+ redirect_to :action => 'index'
end
end
def show_hide
- @folders.each do |f|
- logger.info f.inspect,"\n"
- if params["folders_to_show"].include?(f.id.to_s)
- f.shown = true
- f.save
- else
- f.shown = false
- f.save
- end
+ if !params["folders_to_show"].nil?
+ @folders.each do |f|
+ if params["folders_to_show"].include?(f.id.to_s)
+ f.shown = true
+ f.save
+ else
+ f.shown = false
+ f.save
+ end
+ end
end
redirect_to :action => 'index'
end
@@ -88,6 +98,39 @@ class FoldersController < ApplicationController
redirect_to :action => 'index'
end
+ def select
+ session[:selected_folder] = params[:id]
+ redirect_to :controller => 'messages', :action => 'index'
+ end
+
+ def refresh_status
+ @folders_shown.each do |f|
+ @mailbox.set_folder(f.full_name)
+ folder_status = @mailbox.status
+ f.update_attributes(:total => folder_status['MESSAGES'], :unseen => folder_status['UNSEEN'])
+ end
+ redirect_to :controller=> 'messages', :action => 'index'
+ end
+
+ def emptybin
+ begin
+ trash_folder = @current_user.folders.find_by_full_name($defaults["mailbox_trash"])
+ @mailbox.set_folder(trash_folder.full_name)
+ trash_folder.messages.each do |m|
+ @mailbox.delete_message(m.uid)
+ end
+ @mailbox.expunge
+ trash_folder.messages.destroy_all
+ trash_folder.update_attributes(:unseen => 0, :total => 0)
+ rescue Exception => e
+ flash[:error] = "#{t(:imap_error)} (#{e.to_s})"
+ end
+ redirect_to :controller => 'messages', :action => 'index'
+ end
+
+
+ ############################################# protected section #######################################
+
protected
def get_folders
diff --git a/app/controllers/internal_controller.rb b/app/controllers/internal_controller.rb
index 3794ea1..076b05d 100755
--- a/app/controllers/internal_controller.rb
+++ b/app/controllers/internal_controller.rb
@@ -28,4 +28,11 @@ class InternalController < ApplicationController
redirect_to :controller=>'user', :action => 'login'
end
+ def onlycanlogins
+ reset_session
+ flash[:error] = t(:only_can_logins,:scope=>:user)
+ @current_user = nil
+ redirect_to :controller=>'user', :action => 'login'
+ end
+
end
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
index 5105a29..c3bb144 100755
--- a/app/controllers/messages_controller.rb
+++ b/app/controllers/messages_controller.rb
@@ -1,6 +1,7 @@
require 'imap_session'
require 'imap_mailbox'
require 'imap_message'
+require 'mail'
class MessagesController < ApplicationController
@@ -9,18 +10,20 @@ class MessagesController < ApplicationController
include ImapMessageModule
include MessagesHelper
- before_filter :check_current_user ,:selected_folder
-
- before_filter :get_current_folders
+ before_filter :check_current_user ,:selected_folder,:get_current_folders
before_filter :open_imap_session, :select_imap_folder
after_filter :close_imap_session
theme :theme_resolver
-
def index
+ if @current_folder.nil?
+ redirect_to :controller => 'folders', :action => 'index'
+ return
+ end
+
@messages = []
folder_status = @mailbox.status
@@ -45,53 +48,38 @@ class MessagesController < ApplicationController
end
- def folder
- session[:selected_folder] = params[:id]
- redirect_to :action => 'index'
- end
-
def compose
- flash[:notice] = 'Not impelented yet'
+ @message = Message.new
end
-# TODO error when no folders are shown
+ def reply
+ @message = Message.new
+ render 'compose'
+ end
- def refresh
- @folders_shown.each do |f|
- @mailbox.set_folder(f.full_name)
- folder_status = @mailbox.status
- f.update_attributes(:total => folder_status['MESSAGES'], :unseen => folder_status['UNSEEN'])
- end
+ def sendout
+ flash[:notice] = t(:was_sent,:scope => :sendout)
redirect_to :action => 'index'
- end
+ end
- def emptybin
+ def msgops
begin
- trash_folder = @current_user.folders.find_by_full_name($defaults["mailbox_trash"])
- @mailbox.set_folder(trash_folder.full_name)
- trash_folder.messages.each do |m|
- logger.custom('id',m.inspect)
- @mailbox.delete_message(m.uid)
+ if !params["uids"]
+ flash[:warning] = t(:no_selected,:scope=>:message)
+ elsif params["reply"]
+ redirect_to :action => 'reply', :id => params[:id]
+ return
end
- @mailbox.expunge
- trash_folder.messages.destroy_all
- trash_folder.update_attributes(:unseen => 0, :total => 0)
rescue Exception => e
flash[:error] = "#{t(:imap_error)} (#{e.to_s})"
end
- redirect_to :action => 'index'
- end
+ redirect_to :action => 'show', :id => params[:id]
+ end
def ops
begin
if !params["uids"]
flash[:warning] = t(:no_selected,:scope=>:message)
- elsif params["delete"]
- params["uids"].each do |uid|
- @mailbox.delete_message(uid)
- @current_user.messages.find_by_uid(uid).destroy
- end
- @current_folder.update_stats
elsif params["set_unread"]
params["uids"].each do |uid|
@mailbox.set_unread(uid)
@@ -116,7 +104,7 @@ class MessagesController < ApplicationController
if params["dest_folder"].empty?
flash[:warning] = t(:no_selected,:scope=>:folder)
else
- dest_folder = find(params["dest_folder"])
+ dest_folder = @current_user.folders.find(params["dest_folder"])
params["uids"].each do |uid|
@mailbox.copy_message(uid,dest_folder.full_name)
message = @current_folder.messages.find_by_uid(uid)
@@ -149,10 +137,86 @@ class MessagesController < ApplicationController
end
def show
+
+ @attachments = []
+ @render_as_text = []
+
@message = @current_user.messages.find(params[:id])
@message.update_attributes(:unseen => false)
- flash[:notice] = 'Not implemented yet'
- @body = @mailbox.fetch_body(@message.uid)
+ imap_message = @mailbox.fetch_body(@message.uid)
+ parts = imap_message.split(/\r\n\r\n/)
+ @message_header = parts[0]
+ mail = Mail.new(imap_message)
+ @title = mail.subject
+ if mail.multipart?
+ idx = 0
+ mail.parts.each do |part|
+ case part.content_type
+ when /^text\/plain/ then
+ @render_as_text << part.body.raw_source
+ else
+ a = Attachment.new( :message_id => @message.id,
+ :description => part.content_description,
+ :type => part.content_type,
+ :content => part.body.raw_source,
+ :idx => idx
+ )
+ @attachments << a
+ end
+ idx += 1
+ end
+ else
+ if mail.content_type.nil?
+ @render_as_text << mail.body.raw_source
+ else
+ a = Attachment.new( :message_id => @message.id,
+ :type => mail.content_type,
+ :encoding => mail.body.encoding,
+ :charset => mail.body.charset,
+ :content => mail.body.raw_source,
+ :idx => 0
+ )
+ @attachments << a
+ end
+ end
+ end
+
+ def body
+ message = @mailbox.fetch_body(params[:id].to_i)
+ mail = Mail.new(message)
+ @title = ''
+ @body = ''
+ #
+ #header = parts[0]
+ #body = parts[1]
+ #@body = "
ala
#{header}
#{mail.inspect}"
+ render 'mail_view',:layout => 'mail_view'
+ end
+
+ def attachment
+ @message = @current_user.messages.find(params[:id])
+ mail = Mail.new(@mailbox.fetch_body(@message.uid))
+
+ if mail.multipart?
+ part = mail.parts[params[:idx].to_i]
+ a = Attachment.new( :message_id => @message.id,
+ :description => part.content_description,
+ :type => part.content_type,
+ :content => part.body.raw_source,
+ :idx => params[:idx]
+ )
+ else
+ a = Attachment.new( :message_id => @message.id,
+ :type => mail.content_type,
+ :encoding => mail.body.encoding,
+ :charset => mail.body.charset,
+ :content => mail.body.raw_source,
+ :idx => 0
+ )
+ end
+ headers['Content-type'] = a.type
+ headers['Content-Disposition'] = %(attachment; filename="#{a.name}")
+ render :text => a.content_decoded
end
end
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 7b9d597..5304635 100755
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -12,9 +12,11 @@ class UserController < ApplicationController
redirect_to :action => "login"
end
-# TODO make login possible to use only one username
-
def authenticate
+ if !$defaults["only_can_logins"].include?(params[:user][:email])
+ redirect_to :controller => 'internal', :action => 'onlycanlogins'
+ return false
+ end
user = User.find_by_email(params[:user][:email])
if user.nil?
redirect_to :action => 'unknown' ,:email=> params[:user][:email]
@@ -26,7 +28,7 @@ class UserController < ApplicationController
redirect_to(session["return_to"])
session["return_to"] = nil
else
- redirect_to :controller=> 'messages', :action=> 'refresh'
+ redirect_to :controller=> 'messages', :action=> 'index'
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 4bafdea..c498d6a 100755
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -4,18 +4,16 @@ def form_field(object,field,flabel,example,val)
model_name = eval(object.class.model_name)
html = ""
html << "
"
- if object.errors[field.to_sym]
+ if not object.errors[field.to_sym].empty?
html << "
"
end
+
html << ""
- if object.errors[field.to_sym]
+
+ if not object.errors[field.to_sym].empty?
html << " "
html << object.errors[field.to_sym].to_s
html << ""
@@ -25,8 +23,8 @@ def form_field(object,field,flabel,example,val)
html << object.class.name.downcase+"_"+field
html << "\""
html << " name=\"#{object.class.name.downcase}[#{field}]\""
- html << " size=50 type=\"text\" class=\"text_field\" value=\""
- value = object.instance_eval(field) || val || ""
+ html << " type=\"text\" class=\"text_field\" value=\""
+ value = object.instance_eval(field) || val || ""
html << value
html << "\"/>"
html << ""
@@ -38,6 +36,47 @@ def form_field(object,field,flabel,example,val)
end
+def mail_param_view(object,field,value)
+ model_name = eval(object.class.model_name)
+ html = ""
+ html << "
"
+ html << ""
+ html << value
+ html << "
"
+ html
+end
+
+def area_field(object,field,flabel,example,val,cols,rows)
+ model_name = eval(object.class.model_name)
+ html = ""
+ html << "
"
+
+ if not object.errors[field.to_sym].empty?
+ html << "
"
+ end
+
+ html << ""
+
+ if not object.errors[field.to_sym].empty?
+ html << ""
+ html << object.errors[field.to_sym].to_s
+ html << ""
+ html << "
"
+ end
+
+ name = object.class.name.downcase + '[' + field + ']'
+ id = object.class.name.downcase+"_"+field
+ value = object.instance_eval(field) || val || ""
+ html << ""
+
+ desc = t(:example) + ": " + example
+ html << "#{desc}"
+
+ html << "
"
+end
+
def form_button(text,image)
html = ""
html << "
"
diff --git a/app/helpers/folder_helper.rb b/app/helpers/folder_helper.rb
index 4a4d477..68dc87f 100755
--- a/app/helpers/folder_helper.rb
+++ b/app/helpers/folder_helper.rb
@@ -3,12 +3,12 @@ module FolderHelper
def folder_link(folder)
folder.parent.empty? ? name = folder.name : name = folder.parent.gsub(/\./,'#') + "#" + folder.name
- s = link_to folder.name.capitalize, messages_folder_path(:id => name)
+ s = link_to folder.name.capitalize, folders_select_path(:id => name)
if folder.full_name.downcase == $defaults["mailbox_trash"].downcase
if not folder.total.zero?
s <<' ('
- s << link_to(t(:emptybin,:scope=>:folder),messages_emptybin_path)
+ s << link_to(t(:emptybin,:scope=>:folder),folders_emptybin_path)
s << ')'
end
else
diff --git a/app/helpers/messages_helper.rb b/app/helpers/messages_helper.rb
index 135c25a..bfcdbc3 100755
--- a/app/helpers/messages_helper.rb
+++ b/app/helpers/messages_helper.rb
@@ -11,13 +11,20 @@ module MessagesHelper
end
def date_formatter(date)
- date.strftime("%Y-%m-%d %H:%M")
+ date.nil? ? t(:no_data) : date.strftime("%Y-%m-%d %H:%M")
end
def address_formatter(addr)
ImapMessageModule::IMAPAddress.parse(addr).friendly
end
+ def show_address_formatter(addr)
+ html = ""
+ fs = addr.split(/#/)
+ fs[0].size.zero? ? html << h("<")+fs[1]+h("@")+fs[2]+h(">") : html << fs[0]+h(" <")+fs[1]+h("@")+fs[2]+h(">")
+ html
+ end
+
def subject_formatter(message)
if message.subject.size.zero?
s = t(:no_subject,:scope=>:message)
@@ -25,7 +32,7 @@ module MessagesHelper
length = $defaults["msg_subject_length"].to_i
message.subject.length >= length ? s = message.subject[0,length]+"..." : s = message.subject
end
- link_to s,:controller => 'messages', :action => 'show', :id => message.id
+ link_to s,{:controller => 'messages', :action => 'show', :id => message.id} , :title => message.subject
end
def attachment_formatter(message)
@@ -53,5 +60,11 @@ module MessagesHelper
html
end
+ def content_text_plain_for_render(text)
+ html = h(text)
+ html.gsub!(/\r\n/," ")
+ html
+ end
+
end
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
new file mode 100755
index 0000000..60cb816
--- /dev/null
+++ b/app/models/attachment.rb
@@ -0,0 +1,89 @@
+class Attachment
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+ extend ActiveModel::Naming
+
+ attr_accessor :type, :charset, :encoding, :name, :description, :content, :message_id, :idx
+ attr_reader :link
+
+ def initialize(attributes = {})
+ attributes.each do |name, value|
+ send("#{name}=", value)
+ end
+
+ if @type =~ /name=(\S+)/
+ @name = $1
+ @type =~ /^(\S+);/
+ @type = $1
+ else
+ @name = "no_name.dat"
+ end
+ end
+
+ def persisted?
+ false
+ end
+
+ def to_s
+ s = "Attachment:\n"
+ instance_variables.sort.each do |name|
+ if name == "@content"
+ s += "\t#{name}: size #{instance_variable_get(name).size}\n"
+ else
+ s += "\t#{name}: #{instance_variable_get(name)}\n"
+ end
+ end
+ s
+ end
+
+ def title
+ @description.nil? ? @name : @description
+ end
+
+ def type
+ @type.nil? ? '' : @type
+ end
+
+ def charset
+ @charset.nil? ? '' : @charset
+ end
+
+ def encoding
+ @encoding.nil? ? '' : @encoding
+ end
+
+# def to_html
+# html = ""
+# html << ""
+# html << "#{@description.nil? ? @name : @description}"
+# html << " #{@type}"
+# @charset.nil? ? html : html << " #{@charset}"
+# @encoding.nil? ? html : html << " #{@encoding}"
+# case @type
+# when /^message\/delivery-status/
+# html << "
"
+# html << @content
+# html << "
"
+# #when /^message\/rfc822/
+# # html << "
"
+# # html << @content
+# # html << "
"
+# end
+# html << ""
+# end
+
+# def to_table
+# html = ""
+## @type.nil? ? html << "
" : html << "
#{@type}
"
+# @charset.nil? ? html << "
" : html << "
#{@charset}
"
+# @encoding.nil? ? html << "
" : html << "
#{@encoding}
"
+
+# html
+# end
+
+ def content_decoded
+ # TODO attachments decoding
+ @content
+ end
+
+end
diff --git a/app/models/folder.rb b/app/models/folder.rb
index d967081..c3dfc97 100755
--- a/app/models/folder.rb
+++ b/app/models/folder.rb
@@ -40,6 +40,9 @@ class Folder < ActiveRecord::Base
def check_fill_params
self.total.nil? ? self.total = 0 : self.total
self.unseen.nil? ? self.unseen = 0 : self.unseen
+ self.parent.nil? ? self.parent = "" : self.parent
+ self.haschildren.nil? ? self.haschildren = false : self.haschildren
+ self.delim.nil? ? self.delim = "." : self.delim
end
def self.createBulk(user,imapFolders)
diff --git a/app/models/message.rb b/app/models/message.rb
index 3a51d40..4001327 100755
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -2,6 +2,8 @@ class Message < ActiveRecord::Base
belongs_to :user
belongs_to :folder
+ attr_accessor :body
+
def self.getPageForUser(user,folder,page,sort_field,sort_dir)
order = 'date desc'
@@ -22,7 +24,7 @@ class Message < ActiveRecord::Base
:msg_id => mess.message_id,
:uid => mess.uid,
:from_addr => mess.from_to_db,
- :to_addr => mess.to,
+ :to_addr => mess.to_to_db,
:subject => mess.subject,
:content_type => mess.content_type,
:date => mess.date,
diff --git a/config/application.rb b/config/application.rb
index 4b75793..2619d4b 100755
--- a/config/application.rb
+++ b/config/application.rb
@@ -13,12 +13,14 @@ module Mailr
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
- # config.autoload_paths += %W(#{config.root}/extras)
+ config.autoload_paths += %W(#{config.root}/lib)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+ config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
+
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
diff --git a/config/defaults.yml b/config/defaults.yml
index afbae6e..fab7635 100755
--- a/config/defaults.yml
+++ b/config/defaults.yml
@@ -19,7 +19,7 @@ msgs_sent_view_fields: [to_addr, subject, date, size]
msg_subject_length: 45
msg_search_fields: [subject, from, to]
-imap_debug: true
+imap_debug: false
imap_use_ssl: 'false'
imap_port: 143
imap_ssl_port: 993
@@ -35,3 +35,4 @@ mailbox_trash: INBOX.Trash
mailbox_sent: INBOX.sent
mailbox_drafts: INBOX.drafts
+only_can_logins: [wtodryk]
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 41aeea9..1925999 100755
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -10,7 +10,7 @@ Mailr::Application.configure do
config.action_controller.perform_caching = true
# Specifies the header that your server uses for sending files
- config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ #config.action_dispatch.x_sendfile_header = "X-Sendfile"
# For nginx:
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 70e313f..cdbf423 100755
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -28,6 +28,7 @@ pl:
subject: Temat
size: Rozmiar
date: Data
+ body: Treść
user:
email: E-mail
password: Hasło
@@ -40,7 +41,7 @@ pl:
compose_to_selected: Napisz do wybranych
delete_selected: Usuń wybrane
modifying: Modyfikacja kontaktu
- creating_new: Tworzenie nowego kontaktu
+ creating_new: Nowy kontakt
create_new: Utwórz nowy kontakt
contacts: Kontakty
contact: Kontakt
@@ -55,7 +56,7 @@ pl:
folder:
folders: Foldery
- no_shown: Żadnych pokazanych folderów
+ no_shown: Nie prezentowany jest żaden folder. Skonfiguruj widok folderów w zakładce
parent: Folder nadrzędny
to_create: Folder do utworzenia
to_delete: Folder do usunięcia
@@ -81,9 +82,24 @@ pl:
set_unread: Ustaw jako nieprzeczytane
no_in: Nie ma żadnych wiadomości w bieżącym folderze
no_subject: Brak tematu
+ no_content: Wiadomość nie zawiera treści
+ attachments: Załączniki
+ content: Treść wiadomości
+ header_source: Nagłówek wiadomości
+ show_header: Pokaż nagłówek
compose:
compose: Nowa wiadomość
+ new_message: Nowa wiadomość
+ not_contain_at: ",jeżeli nie zawiera znaku @, adres będzie szukany w kontaktach"
+ subject_of_the_message: Temat wiadomości
+ write_your_message_here: Tu wpisz swoją wiadomość
+
+ sendout:
+ was_sent: Wiadomość została wysłana
+
+ show:
+ replay_to: Odpowiedz
user:
login_failure: Nieudane logowanie. Podano błędny e-mail lub hasło.
@@ -96,6 +112,7 @@ pl:
unknown_setup: Idź do strony konfiguracyjnej i skonfiguruj swój dostęp
setup: Konfiguracja
login: Logowanie
+ only_can_logins: Podany identyfikator użytkownika nie uprawnia do korzystania z aplikacji
must_be_unique: musi być unikalny
some_add_info: jakieś dodatkowe informacje
@@ -119,4 +136,7 @@ pl:
mbytes: MB
site_link: https://github.com/lmanolov/mailr
send: Wyślij
+ no_data: Brak danych
+ logout: Wyloguj
+ download: Pobierz
diff --git a/config/routes.rb b/config/routes.rb
index a0c179a..028fbb9 100755
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -8,26 +8,35 @@ Mailr::Application.routes.draw do
#resources :folders
- get "folders/index"
+ match "folders/index" => 'folders#index', :as => :folders
post "folders/create"
post "folders/delete"
post "folders/show_hide"
get "folders/refresh"
+ get "folders/refresh_status"
post "folders/refresh"
+ match "folders/select/:id" => 'folders#select', :as => :folders_select
+ get "folders/emptybin"
get "internal/error"
get "internal/imaperror"
get "internal/loginfailure"
+ get "internal/onlycanlogins"
root :to => "messages#index"
+ #get "messages/refresh_status"
+ #get "messages/emptybin"
+ #match "messages/select/:id" => 'messages#select', :as => :messages_select
get "messages/index"
- get "messages/refresh"
- match 'messages/folder/:id' => 'messages#folder', :as => :messages_folder
+ #match 'messages/folder/:id' => 'messages#folder', :as => :messages_folder
post "messages/ops"
- get "messages/compose"
- get "messages/refresh"
+ post "messages/msgops"
+ match "messages/compose" => 'messages#compose'
+ match "messages/reply/:id" => 'messages#reply'
+ post "messages/sendout"
match "messages/show/:id" => 'messages#show'
- get "messages/emptybin"
+ match "messages/body/:id" => 'messages#body' , :as => :messages_body
+ match "messages/attachment/:id/:idx" => 'messages#attachment', :as => :messages_attachment_download
get "user/logout"
post "user/authenticate"
diff --git a/lib/imap_mailbox.rb b/lib/imap_mailbox.rb
index aa4b319..6f0fd1e 100755
--- a/lib/imap_mailbox.rb
+++ b/lib/imap_mailbox.rb
@@ -12,11 +12,12 @@ class IMAPMailbox
attr_accessor :sfolder
attr_accessor :logger
- def initialize(logger)
+ def initialize(logger,debug)
@sfolder = ''
@folders = {}
@connected = false
@logger = logger
+ Net::IMAP.debug = debug
end
def connect(server,username,password)
diff --git a/lib/imap_message.rb b/lib/imap_message.rb
index d72f1f2..9774b91 100755
--- a/lib/imap_message.rb
+++ b/lib/imap_message.rb
@@ -57,7 +57,6 @@ class IMAPMessage
envelope = message.attr['ENVELOPE']
m.envelope = envelope
m.message_id = envelope.message_id
- m.to = envelope.to
m.date = envelope.date
m.subject = envelope.subject
m.uid = message.attr['UID']
@@ -66,6 +65,7 @@ class IMAPMessage
m.size = message.attr['RFC822.SIZE']
m.unseen = !(message.attr['FLAGS'].member? :Seen)
m.from = IMAPAddress.from_address(envelope.from[0])
+ m.to = IMAPAddress.from_address(envelope.to[0])
m
end
@@ -77,8 +77,14 @@ class IMAPMessage
from.to_db
end
+ def to_to_db
+ to.to_db
+ end
+
end
+
+
end
diff --git a/lib/imap_session.rb b/lib/imap_session.rb
index 4e5042a..8b915f3 100755
--- a/lib/imap_session.rb
+++ b/lib/imap_session.rb
@@ -5,7 +5,7 @@ module ImapSessionModule
def open_imap_session
begin
- @mailbox ||= ImapMailboxModule::IMAPMailbox.new(logger)
+ @mailbox ||= ImapMailboxModule::IMAPMailbox.new(logger,$defaults["imap_debug"])
@mailbox.connect(@current_user.servers.primary,@current_user.email, @current_user.get_cached_password(session))
rescue Exception => ex
redirect_to :controller => 'internal', :action => 'loginfailure'
diff --git a/lib/tasks/clear_db.rake b/lib/tasks/clear_db.rake
index 35407fc..b12913c 100755
--- a/lib/tasks/clear_db.rake
+++ b/lib/tasks/clear_db.rake
@@ -1,10 +1,23 @@
namespace :db do
- desc "Clears all data in db"
- task :clear_data => :environment do
- users = User.all
- puts "Number of users in db: #{users.size}"
- puts "Deleting data....."
- User.destroy_all
- puts "Done"
+
+ desc "Removes all users data from db"
+ task :remove_all_data => :environment do
+ users = User.all
+ puts "Number of users in db: #{users.size}"
+ puts "Deleting data....."
+ User.destroy_all
+ puts "Done"
end
+
+ desc "Deletes users data (messages,folders,contacts)"
+ task :remove_users_data => :environment do
+ users = User.all
+ users.each do |u|
+ puts "Removing folders & messages for user #{u.email}"
+ u.folders.destroy_all
+ puts "Removing contacts for user #{u.email}"
+ u.contacts.destroy_all
+ end
+ end
+
end
diff --git a/public/images/ui-icons_222222_256x240.png b/public/images/ui-icons_222222_256x240.png
new file mode 100755
index 0000000..ee039dc
Binary files /dev/null and b/public/images/ui-icons_222222_256x240.png differ
diff --git a/public/images/ui-icons_454545_256x240.png b/public/images/ui-icons_454545_256x240.png
new file mode 100755
index 0000000..7ec70d1
Binary files /dev/null and b/public/images/ui-icons_454545_256x240.png differ
diff --git a/public/javascripts/jquery.js b/public/javascripts/jquery.js
new file mode 100755
index 0000000..48590ec
--- /dev/null
+++ b/public/javascripts/jquery.js
@@ -0,0 +1,18 @@
+/*!
+ * jQuery JavaScript Library v1.6.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Jun 30 14:16:56 2011 -0400
+ */
+(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="