diff --git a/.gitignore b/.gitignore index e200dc5c..c7069277 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ /db/*.db /db/*.sqlite3 /db/*.sqlite3-journal +/db/assets/* /log/*.log /public/assets/ /tmp diff --git a/Gemfile b/Gemfile index 5b07e9c7..98913b0a 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,7 @@ gem "htmlentities" gem "swf_fu" gem "rails_autolink" gem 'thin' +gem 'paperclip' # To use ActiveModel has_secure_password gem 'bcrypt', '~> 3.1.7' diff --git a/Gemfile.lock b/Gemfile.lock index 909d1115..aafd4049 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -52,6 +52,10 @@ GEM xpath (~> 2.0) childprocess (0.5.5) ffi (~> 1.0, >= 1.0.11) + climate_control (0.0.3) + activesupport (>= 3.0) + cocaine (0.5.7) + climate_control (>= 0.0.3, < 1.0) codeclimate-test-reporter (0.4.1) simplecov (>= 0.7.1, < 1.0.0) coffee-rails (4.1.0) @@ -101,6 +105,7 @@ GEM mime-types (>= 1.16, < 3) metaclass (0.0.4) mime-types (2.6.1) + mimemagic (0.3.0) mini_portile (0.6.1) minitest (5.7.0) mocha (1.1.0) @@ -112,6 +117,12 @@ GEM mini_portile (~> 0.6.0) nokogumbo (1.1.12) nokogiri + paperclip (4.3.0) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + cocaine (~> 0.5.5) + mime-types + mimemagic (= 0.3.0) rack (1.5.5) rack-dev-mark (0.7.3) rack (>= 1.1) @@ -228,6 +239,7 @@ DEPENDENCIES jquery-rails mocha mysql2 + paperclip rack-dev-mark rack-mini-profiler rails (~> 4.1.11) diff --git a/app/assets/stylesheets/tracks.css.scss b/app/assets/stylesheets/tracks.css.scss index 3bb46e3c..6cbd2653 100644 --- a/app/assets/stylesheets/tracks.css.scss +++ b/app/assets/stylesheets/tracks.css.scss @@ -124,6 +124,11 @@ img.delete_item { } } +a.todo_attachment { + background: image-url('bottom_off.png') no-repeat top; + border: none; +} + a.undecorated_link {background-color:transparent;color:transparent;} img.todo_star {background-image: image-url('staricons.png'); background-repeat: no-repeat; border:none; background-position: -32px 0px;} img.todo_star.starred{ background-position: 0px 0px; } diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 515450f6..0b6fbd1c 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -815,6 +815,20 @@ class TodosController < ApplicationController end end + def attachment + id = params[:id] + filename = params[:filename] + attachment = current_user.attachments.find(id) + + if attachment + send_file(attachment.file.path, + disposition: 'attachment', + type: 'message/rfc822') + else + head :not_found + end + end + private def set_group_view_by diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 75a651ad..7bec8b48 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -204,6 +204,14 @@ module TodosHelper link_to(t('todos.convert_to_project'), url, {:class => "icon_item_to_project", :id => dom_id(todo, "to_project")}) end + def attachment_image(todo) + link_to( + image_tag('blank.png', width: 16, height: 16, border:0), + todo.attachments.first.file.url, + {:class => 'todo_attachment', title: 'Get attachments of this todo'} + ) + end + def collapsed_notes_image(todo) link = link_to( image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), diff --git a/app/models/attachment.rb b/app/models/attachment.rb new file mode 100644 index 00000000..564a0ed6 --- /dev/null +++ b/app/models/attachment.rb @@ -0,0 +1,20 @@ +class Attachment < ActiveRecord::Base + belongs_to :todo, touch: true + + has_attached_file :file, + url: '/:class/:id/:basename.:extension', + path: ":rails_root/db/assets/#{Rails.env}/:class/:id/:basename.:extension", + override_file_permissions: 0660 + + do_not_validate_attachment_file_type :file + # validates_attachment_content_type :file, :content_type => ["text/plain"] + + before_destroy :delete_attached_file + + private + + def delete_attached_file + file = nil + save! + end +end diff --git a/app/models/message_gateway.rb b/app/models/message_gateway.rb index 11ea3167..de60e8df 100644 --- a/app/models/message_gateway.rb +++ b/app/models/message_gateway.rb @@ -10,13 +10,41 @@ class MessageGateway < ActionMailer::Base todo_builder = TodoFromRichMessage.new(user, context.id, todo_params[:description], todo_params[:notes]) todo = todo_builder.construct - todo.save! - Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}" + + if todo.save! + Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}" + + if attach_email_to_todo(todo, email) + Rails.logger.info "Saved email as attachment to todo for user #{user.login} in context #{context.name}" + end + end todo end private + def attach_email_to_todo(todo, email) + attachment = todo.attachments.build + + # create temp file + tmp = Tempfile.new(['attachment', '.eml'], {universal_newline: true}) + tmp.write email.raw_source.gsub(/\r/, "") + + # add temp file to attachment. paperclip will copy the file to the right location + Rails.logger.info "Saved received email to #{tmp.path}" + attachment.file = tmp + tmp.close + saved = attachment.save! + + # enable write permissions on group, since MessageGateway could be run under different + # user than Tracks (i.e. apache versus mail) + dir = File.open(File.dirname(attachment.file.path)) + dir.chmod(0770) + + # delete temp file + tmp.unlink + end + def get_todo_params(email) params = {} @@ -111,5 +139,4 @@ class MessageGateway < ActionMailer::Base end end end - end diff --git a/app/models/todo.rb b/app/models/todo.rb index 3f82e217..7842a7a6 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -24,6 +24,7 @@ class Todo < ActiveRecord::Base :source => :predecessor has_many :pending_successors, -> {where('todos.state = ?', 'pending')}, :through => :predecessor_dependencies, :source => :successor + has_many :attachments, dependent: :destroy # scopes for states of this todo scope :active, -> { where state: 'active' } diff --git a/app/models/user.rb b/app/models/user.rb index 49bc4c2e..d693c0f1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -96,6 +96,7 @@ class User < ActiveRecord::Base has_many :notes, -> { order "created_at DESC" }, dependent: :delete_all has_one :preference, dependent: :destroy + has_many :attachments, through: :todos validates_presence_of :login validates_presence_of :password, if: :password_required? diff --git a/app/views/todos/_todo.html.erb b/app/views/todos/_todo.html.erb index 07e8da70..13335d37 100644 --- a/app/views/todos/_todo.html.erb +++ b/app/views/todos/_todo.html.erb @@ -41,6 +41,7 @@ cache [todo, current_user.date.strftime("%Y%m%d"), @source_view, current_user.pr <%= project_and_context_links( todo, parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %> <%= collapsed_notes_image(todo) if todo.notes.present? %> <%= collapsed_successors_image(todo) if todo.has_pending_successors %> + <%= attachment_image(todo) if todo.attachments.present? %>