mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 07:10:12 +01:00
Create attachment model and hook it up to todo
An attachment has write permissions on group so that managing attachments work from different users in same group, i.e. user apache and user mail.
This commit is contained in:
parent
5499ac2a03
commit
2bd68fecb7
10 changed files with 121 additions and 39 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -15,6 +15,7 @@
|
|||
/db/*.db
|
||||
/db/*.sqlite3
|
||||
/db/*.sqlite3-journal
|
||||
/db/assets/*
|
||||
/log/*.log
|
||||
/public/assets/
|
||||
/tmp
|
||||
|
|
|
|||
20
app/models/attachment.rb
Normal file
20
app/models/attachment.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -101,11 +101,12 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
# match /todos/tag and put everything in :name, including extensions like .m and .txt.
|
||||
# match /todos/tag and put everything in :name, including extensions like .m and .txt.
|
||||
# This means the controller action needs to parse the extension and set format/content type
|
||||
# Needed for /todos/tag/first.last.m to work
|
||||
get 'todos/tag/:name' => 'todos#tag', :as => :tag, :format => false, :name => /.*/
|
||||
|
||||
get 'attachments/:id/:filename' => "todos#attachment"
|
||||
get 'tags.autocomplete' => "todos#tags", :format => 'autocomplete'
|
||||
get 'todos/done/tag/:name' => "todos#done_tag", :as => :done_tag
|
||||
get 'todos/all_done/tag/:name' => "todos#all_done_tag", :as => :all_done_tag
|
||||
|
|
|
|||
9
db/migrate/20150805144100_create_attachments.rb
Normal file
9
db/migrate/20150805144100_create_attachments.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class CreateAttachments < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :attachments do |t|
|
||||
t.references :todo, index: true
|
||||
t.attachment :file
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
56
db/schema.rb
56
db/schema.rb
|
|
@ -11,11 +11,23 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150209233951) do
|
||||
ActiveRecord::Schema.define(version: 20150805144100) do
|
||||
|
||||
create_table "attachments", force: true do |t|
|
||||
t.integer "todo_id"
|
||||
t.string "file_file_name"
|
||||
t.string "file_content_type"
|
||||
t.integer "file_file_size"
|
||||
t.datetime "file_updated_at"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "attachments", ["todo_id"], name: "index_attachments_on_todo_id", using: :btree
|
||||
|
||||
create_table "contexts", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.integer "position"
|
||||
t.integer "position", default: 0
|
||||
t.integer "user_id", default: 1
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
|
|
@ -85,11 +97,11 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
add_index "preferences", ["user_id"], name: "index_preferences_on_user_id", using: :btree
|
||||
|
||||
create_table "projects", force: true do |t|
|
||||
t.string "name", null: false
|
||||
t.integer "position"
|
||||
t.integer "user_id", default: 1
|
||||
t.text "description"
|
||||
t.string "state", limit: 20, null: false
|
||||
t.string "name", null: false
|
||||
t.integer "position", default: 0
|
||||
t.integer "user_id", default: 1
|
||||
t.text "description", limit: 16777215
|
||||
t.string "state", limit: 20, null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "default_context_id"
|
||||
|
|
@ -104,17 +116,17 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
add_index "projects", ["user_id"], name: "index_projects_on_user_id", using: :btree
|
||||
|
||||
create_table "recurring_todos", force: true do |t|
|
||||
t.integer "user_id", default: 1
|
||||
t.integer "context_id", null: false
|
||||
t.integer "user_id", default: 1
|
||||
t.integer "context_id", null: false
|
||||
t.integer "project_id"
|
||||
t.string "description", null: false
|
||||
t.text "notes"
|
||||
t.string "state", limit: 20, null: false
|
||||
t.string "description", null: false
|
||||
t.text "notes", limit: 16777215
|
||||
t.string "state", limit: 20, null: false
|
||||
t.datetime "start_from"
|
||||
t.string "ends_on"
|
||||
t.datetime "end_date"
|
||||
t.integer "number_of_occurrences"
|
||||
t.integer "occurrences_count", default: 0
|
||||
t.integer "occurrences_count", default: 0
|
||||
t.string "target"
|
||||
t.integer "show_from_delta"
|
||||
t.string "recurring_period"
|
||||
|
|
@ -123,7 +135,7 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
t.integer "every_other2"
|
||||
t.integer "every_other3"
|
||||
t.string "every_day"
|
||||
t.boolean "only_work_days", default: false
|
||||
t.boolean "only_work_days", default: false
|
||||
t.integer "every_count"
|
||||
t.integer "weekday"
|
||||
t.datetime "completed_at"
|
||||
|
|
@ -141,7 +153,7 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree
|
||||
add_index "sessions", ["session_id"], name: "sessions_session_id_index", using: :btree
|
||||
|
||||
create_table "taggings", force: true do |t|
|
||||
t.integer "taggable_id"
|
||||
|
|
@ -162,19 +174,19 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
add_index "tags", ["name"], name: "index_tags_on_name", using: :btree
|
||||
|
||||
create_table "todos", force: true do |t|
|
||||
t.integer "context_id", null: false
|
||||
t.integer "context_id", null: false
|
||||
t.integer "project_id"
|
||||
t.string "description", null: false
|
||||
t.text "notes"
|
||||
t.string "description", null: false
|
||||
t.text "notes", limit: 16777215
|
||||
t.datetime "created_at"
|
||||
t.datetime "due"
|
||||
t.datetime "completed_at"
|
||||
t.integer "user_id", default: 1
|
||||
t.integer "user_id", default: 1
|
||||
t.datetime "show_from"
|
||||
t.string "state", limit: 20, null: false
|
||||
t.string "state", limit: 20, null: false
|
||||
t.integer "recurring_todo_id"
|
||||
t.datetime "updated_at"
|
||||
t.text "rendered_notes"
|
||||
t.text "rendered_notes", limit: 16777215
|
||||
end
|
||||
|
||||
add_index "todos", ["context_id"], name: "index_todos_on_context_id", using: :btree
|
||||
|
|
@ -212,7 +224,7 @@ ActiveRecord::Schema.define(version: 20150209233951) do
|
|||
|
||||
create_table "users", force: true do |t|
|
||||
t.string "login", limit: 80, null: false
|
||||
t.string "crypted_password", limit: 60, null: false
|
||||
t.string "crypted_password", limit: 60
|
||||
t.string "token"
|
||||
t.boolean "is_admin", default: false, null: false
|
||||
t.string "first_name"
|
||||
|
|
|
|||
7
test/fixtures/attachments.yml
vendored
Normal file
7
test/fixtures/attachments.yml
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
todo_id:
|
||||
|
||||
two:
|
||||
todo_id:
|
||||
7
test/models/attachment_test.rb
Normal file
7
test/models/attachment_test.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require 'test_helper'
|
||||
|
||||
class AttachmentTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
|
|
@ -72,26 +72,26 @@ class TodoTest < ActiveSupport::TestCase
|
|||
def test_validate_show_from_must_be_a_date_in_the_future
|
||||
t = @not_completed2
|
||||
t.show_from = 1.week.ago
|
||||
|
||||
|
||||
assert !t.save, "todo should not be saved without validation errors"
|
||||
assert_equal 1, t.errors.count
|
||||
assert_equal "must be a date in the future", t.errors[:show_from][0]
|
||||
end
|
||||
|
||||
|
||||
def test_validate_circular_dependencies
|
||||
@completed.activate!
|
||||
@not_completed3=@completed
|
||||
|
||||
|
||||
# 2 -> 1
|
||||
@not_completed1.add_predecessor(@not_completed2)
|
||||
assert @not_completed1.save!
|
||||
assert_equal 1, @not_completed2.successors.count
|
||||
|
||||
|
||||
# 3 -> 2 -> 1
|
||||
@not_completed2.add_predecessor(@not_completed3)
|
||||
assert @not_completed2.save!
|
||||
assert_equal 1, @not_completed3.successors.count
|
||||
|
||||
|
||||
# 1 -> 3 -> 2 -> 1 == circle
|
||||
assert_raises ActiveRecord::RecordInvalid do
|
||||
@not_completed3.add_predecessor(@not_completed1)
|
||||
|
|
@ -131,7 +131,7 @@ class TodoTest < ActiveSupport::TestCase
|
|||
t.toggle_completion!
|
||||
assert_equal :active, t.aasm.current_state
|
||||
end
|
||||
|
||||
|
||||
def test_toggle_completion_with_show_from_in_future
|
||||
t = @not_completed1
|
||||
t.show_from= 1.week.from_now
|
||||
|
|
@ -140,12 +140,12 @@ class TodoTest < ActiveSupport::TestCase
|
|||
t.toggle_completion!
|
||||
assert_equal :completed, t.aasm.current_state
|
||||
end
|
||||
|
||||
|
||||
def test_toggle_completion_with_show_from_in_past
|
||||
t = @not_completed1
|
||||
t.update_attribute(:show_from, 1.week.ago)
|
||||
assert_equal :active, t.aasm.current_state
|
||||
|
||||
|
||||
assert t.toggle_completion!, "shoud be able to mark active todo complete even if show_from is set in the past"
|
||||
assert_equal :completed, t.aasm.current_state
|
||||
end
|
||||
|
|
@ -219,7 +219,7 @@ class TodoTest < ActiveSupport::TestCase
|
|||
# And I update the state of the todo from its project
|
||||
new_todo.update_state_from_project
|
||||
# Then the todo should be hidden
|
||||
assert new_todo.hidden?
|
||||
assert new_todo.hidden?
|
||||
end
|
||||
|
||||
def test_initial_state_defaults_to_active
|
||||
|
|
@ -280,7 +280,7 @@ class TodoTest < ActiveSupport::TestCase
|
|||
assert todo.pending?, "todo with predecessor should be blocked"
|
||||
|
||||
# cannot activate if part of hidden project
|
||||
assert_raise(AASM::InvalidTransition) { todo.activate! }
|
||||
assert_raise(AASM::InvalidTransition) { todo.activate! }
|
||||
|
||||
todo.remove_predecessor(todo2)
|
||||
assert todo.reload.hidden?, "todo should be put back in hidden state"
|
||||
|
|
@ -337,7 +337,7 @@ class TodoTest < ActiveSupport::TestCase
|
|||
@not_completed1.add_predecessor(@not_completed2)
|
||||
@not_completed1.save_predecessors
|
||||
# blocking is not done automagically
|
||||
@not_completed1.block!
|
||||
@not_completed1.block!
|
||||
|
||||
assert @not_completed1.uncompleted_predecessors?
|
||||
assert @not_completed1.pending?, "a todo with predecessors should be pending"
|
||||
|
|
@ -358,7 +358,7 @@ class TodoTest < ActiveSupport::TestCase
|
|||
@not_completed1.add_predecessor_list("#{@not_completed2.id}, #{@not_completed3.id}")
|
||||
@not_completed1.save_predecessors
|
||||
# blocking is not done automagically
|
||||
@not_completed1.block!
|
||||
@not_completed1.block!
|
||||
|
||||
# Then @completed1 should have predecessors and should be blocked
|
||||
assert @not_completed1.uncompleted_predecessors?
|
||||
|
|
@ -526,20 +526,43 @@ class TodoTest < ActiveSupport::TestCase
|
|||
assert !older_created_todos.include?(todo_now)
|
||||
assert !recent_created_todos.include?(todo_old)
|
||||
end
|
||||
|
||||
|
||||
def test_notes_are_rendered_on_save
|
||||
user = @completed.user
|
||||
todo = user.todos.create(:description => "test", :context => @completed.context)
|
||||
|
||||
|
||||
assert_nil todo.notes
|
||||
assert_nil todo.rendered_notes
|
||||
|
||||
|
||||
todo.notes = "*test*"
|
||||
todo.save!
|
||||
todo.reload
|
||||
|
||||
|
||||
assert_equal "*test*", todo.notes
|
||||
assert_equal "<p><strong>test</strong></p>", todo.rendered_notes
|
||||
end
|
||||
|
||||
def test_attachments_are_removed_after_delete
|
||||
# Given a user and a todo withou any attachments
|
||||
todo = @not_completed1
|
||||
assert_equal 0, todo.attachments.count, "we start without attachments"
|
||||
assert_equal 0, todo.user.attachments.count, "the user has no attachments"
|
||||
|
||||
# When I add a file as attachment to a todo of this user
|
||||
attachment = todo.attachments.build
|
||||
attachment.file = File.open(File.join(Rails.root, 'test', 'fixtures', 'email_with_multipart.txt'))
|
||||
attachment.save!
|
||||
new_path = attachment.file.path
|
||||
|
||||
# then the attachment should be there
|
||||
assert File.exists?(new_path), "attachment should be on file system"
|
||||
assert_equal 1, todo.attachments.reload.count, "should have one attachment"
|
||||
|
||||
# When I destroy the todo
|
||||
todo.destroy!
|
||||
|
||||
# Then the attachement and file should nogt be there anymore
|
||||
assert_equal 0, todo.user.attachments.reload.count
|
||||
assert !File.exists?(new_path), "attachment should not be on file system"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue