admin_assistant 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/admin_assistant.gemspec +404 -8
- data/rails_2_test/README +256 -0
- data/rails_2_test/Rakefile +13 -0
- data/rails_2_test/app/controllers/admin/appointments2_controller.rb +15 -0
- data/rails_2_test/app/controllers/admin/appointments_controller.rb +11 -0
- data/rails_2_test/app/controllers/admin/blog_posts2_controller.rb +138 -0
- data/rails_2_test/app/controllers/admin/blog_posts3_controller.rb +76 -0
- data/rails_2_test/app/controllers/admin/blog_posts4_controller.rb +21 -0
- data/rails_2_test/app/controllers/admin/blog_posts5_controller.rb +27 -0
- data/rails_2_test/app/controllers/admin/blog_posts6_controller.rb +10 -0
- data/rails_2_test/app/controllers/admin/blog_posts_controller.rb +8 -0
- data/rails_2_test/app/controllers/admin/blog_posts_custom_new_and_edit_controller.rb +15 -0
- data/rails_2_test/app/controllers/admin/blog_posts_read_only_controller.rb +19 -0
- data/rails_2_test/app/controllers/admin/bookmarks_controller.rb +11 -0
- data/rails_2_test/app/controllers/admin/comments2_controller.rb +14 -0
- data/rails_2_test/app/controllers/admin/comments_controller.rb +12 -0
- data/rails_2_test/app/controllers/admin/file_column_images2_controller.rb +10 -0
- data/rails_2_test/app/controllers/admin/file_column_images_controller.rb +6 -0
- data/rails_2_test/app/controllers/admin/images2_controller.rb +11 -0
- data/rails_2_test/app/controllers/admin/images_controller.rb +6 -0
- data/rails_2_test/app/controllers/admin/misconfigured1_controller.rb +7 -0
- data/rails_2_test/app/controllers/admin/not_migrated_yets_controller.rb +9 -0
- data/rails_2_test/app/controllers/admin/product_categories2_controller.rb +7 -0
- data/rails_2_test/app/controllers/admin/product_categories_controller.rb +5 -0
- data/rails_2_test/app/controllers/admin/products2_controller.rb +15 -0
- data/rails_2_test/app/controllers/admin/products_controller.rb +31 -0
- data/rails_2_test/app/controllers/admin/television_airings_controller.rb +5 -0
- data/rails_2_test/app/controllers/admin/television_time_slots_controller.rb +5 -0
- data/rails_2_test/app/controllers/admin/users2_controller.rb +10 -0
- data/rails_2_test/app/controllers/admin/users_controller.rb +63 -0
- data/rails_2_test/app/controllers/application.rb +16 -0
- data/rails_2_test/app/controllers/application_controller.rb +16 -0
- data/rails_2_test/app/controllers/blog_posts_controller.rb +5 -0
- data/rails_2_test/app/helpers/admin/appointments2_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/appointments_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/blog_posts2_helper.rb +27 -0
- data/rails_2_test/app/helpers/admin/blog_posts3_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/blog_posts4_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/blog_posts6_helper.rb +16 -0
- data/rails_2_test/app/helpers/admin/blog_posts_custom_new_and_edit_helper.rb +29 -0
- data/rails_2_test/app/helpers/admin/blog_posts_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/blog_posts_read_only_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/bookmarks_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/comments2_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/comments_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/file_column_images2_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/file_column_images_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/images2_helper.rb +8 -0
- data/rails_2_test/app/helpers/admin/images_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/misconfigured1_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/not_migrated_yets_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/product_categories2_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/product_categories_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/products2_helper.rb +3 -0
- data/rails_2_test/app/helpers/admin/products_helper.rb +6 -0
- data/rails_2_test/app/helpers/admin/television_airings_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/television_time_slots_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/users2_helper.rb +2 -0
- data/rails_2_test/app/helpers/admin/users_helper.rb +25 -0
- data/rails_2_test/app/helpers/application_helper.rb +3 -0
- data/rails_2_test/app/helpers/blog_posts_helper.rb +2 -0
- data/rails_2_test/app/models/appointment.rb +5 -0
- data/rails_2_test/app/models/blog_post.rb +14 -0
- data/rails_2_test/app/models/blog_post_tag.rb +6 -0
- data/rails_2_test/app/models/bookmark.rb +6 -0
- data/rails_2_test/app/models/comment.rb +4 -0
- data/rails_2_test/app/models/file_column_image.rb +3 -0
- data/rails_2_test/app/models/image.rb +3 -0
- data/rails_2_test/app/models/not_migrated_yet.rb +2 -0
- data/rails_2_test/app/models/product.rb +7 -0
- data/rails_2_test/app/models/product_category.rb +9 -0
- data/rails_2_test/app/models/tag.rb +4 -0
- data/rails_2_test/app/models/television_airing.rb +3 -0
- data/rails_2_test/app/models/television_time_slot.rb +11 -0
- data/rails_2_test/app/models/user.rb +14 -0
- data/rails_2_test/app/views/admin/appointments/_subject_input.html.erb +2 -0
- data/rails_2_test/app/views/admin/appointments2/_time_input.html.erb +31 -0
- data/rails_2_test/app/views/admin/blog_posts2/_after_form.html.erb +5 -0
- data/rails_2_test/app/views/admin/blog_posts2/_after_index.html.erb +1 -0
- data/rails_2_test/app/views/admin/blog_posts2/_after_tags_input.html.erb +16 -0
- data/rails_2_test/app/views/admin/blog_posts2/_before_index.html.erb +2 -0
- data/rails_2_test/app/views/admin/blog_posts5/_after_index_header.html.erb +8 -0
- data/rails_2_test/app/views/admin/blog_posts_read_only/_body_for_show.html.erb +1 -0
- data/rails_2_test/app/views/admin/products/_name_input.html.erb +2 -0
- data/rails_2_test/app/views/admin/products/_percent_off_input.html.erb +2 -0
- data/rails_2_test/app/views/admin/products/_price_input.html.erb +10 -0
- data/rails_2_test/app/views/blog_posts/show.html.erb +13 -0
- data/rails_2_test/app/views/layouts/admin.html.erb +31 -0
- data/rails_2_test/config/boot.rb +109 -0
- data/rails_2_test/config/database.yml +28 -0
- data/rails_2_test/config/environment.rb +78 -0
- data/rails_2_test/config/environments/development.rb +17 -0
- data/rails_2_test/config/environments/production.rb +24 -0
- data/rails_2_test/config/environments/test.rb +24 -0
- data/rails_2_test/config/initializers/inflections.rb +10 -0
- data/rails_2_test/config/initializers/mime_types.rb +5 -0
- data/rails_2_test/config/initializers/new_rails_defaults.rb +17 -0
- data/rails_2_test/config/locales/en.yml +5 -0
- data/rails_2_test/config/routes.rb +43 -0
- data/rails_2_test/db/migrate/20090213215514_create_blog_posts.rb +12 -0
- data/rails_2_test/db/migrate/20090217225542_add_body_to_blog_posts.rb +9 -0
- data/rails_2_test/db/migrate/20090221220917_create_tags.rb +13 -0
- data/rails_2_test/db/migrate/20090221220947_create_blog_post_tags.rb +14 -0
- data/rails_2_test/db/migrate/20090222162204_add_textile_to_blog_posts.rb +9 -0
- data/rails_2_test/db/migrate/20090222163231_add_published_at_to_blog_posts.rb +9 -0
- data/rails_2_test/db/migrate/20090301191722_create_images.rb +16 -0
- data/rails_2_test/db/migrate/20090305165345_create_accounts.rb +14 -0
- data/rails_2_test/db/migrate/20090307225027_rename_accounts_to_users.rb +9 -0
- data/rails_2_test/db/migrate/20090307225750_add_user_id_to_blog_posts.rb +9 -0
- data/rails_2_test/db/migrate/20090309185114_change_blog_posts_textile.rb +9 -0
- data/rails_2_test/db/migrate/20090309193635_create_products.rb +14 -0
- data/rails_2_test/db/migrate/20090309203056_create_comments.rb +15 -0
- data/rails_2_test/db/migrate/20090323005947_create_file_column_images.rb +12 -0
- data/rails_2_test/db/migrate/20090326160049_add_birthday_to_users.rb +9 -0
- data/rails_2_test/db/migrate/20090326223606_add_state_to_users.rb +9 -0
- data/rails_2_test/db/migrate/20090503134004_add_file_column_image_to_products.rb +9 -0
- data/rails_2_test/db/migrate/20090617173651_create_bookmarks.rb +15 -0
- data/rails_2_test/db/migrate/20090624165355_add_tags_string_to_blog_post.rb +9 -0
- data/rails_2_test/db/migrate/20090625144313_add_avatar_fields_to_user.rb +11 -0
- data/rails_2_test/db/migrate/20090629202956_add_merged_into_to_blog_posts.rb +9 -0
- data/rails_2_test/db/migrate/20090701171857_add_force_textile_to_users.rb +9 -0
- data/rails_2_test/db/migrate/20090704163647_add_deleted_at_to_products.rb +9 -0
- data/rails_2_test/db/migrate/20090704173800_add_sale_fields_to_products.rb +13 -0
- data/rails_2_test/db/migrate/20090714024501_create_product_categories.rb +15 -0
- data/rails_2_test/db/migrate/20090819162835_add_admin_level_to_users.rb +9 -0
- data/rails_2_test/db/migrate/20091115134559_add_first_and_last_names_to_users.rb +11 -0
- data/rails_2_test/db/migrate/20091221011256_add_position_to_product_categories.rb +9 -0
- data/rails_2_test/db/migrate/20091222160814_create_appointments.rb +14 -0
- data/rails_2_test/db/migrate/20091227224547_add_user_id_to_appointments.rb +9 -0
- data/rails_2_test/db/migrate/20100214213359_create_television_time_slots.rb +13 -0
- data/rails_2_test/db/migrate/20100214213451_create_television_airings.rb +15 -0
- data/rails_2_test/doc/README_FOR_APP +5 -0
- data/rails_2_test/lib/tasks/rspec.rake +163 -0
- data/rails_2_test/public/404.html +30 -0
- data/rails_2_test/public/422.html +30 -0
- data/rails_2_test/public/500.html +33 -0
- data/rails_2_test/public/dispatch.cgi +10 -0
- data/rails_2_test/public/dispatch.fcgi +24 -0
- data/rails_2_test/public/dispatch.rb +10 -0
- data/rails_2_test/public/favicon.ico +0 -0
- data/rails_2_test/public/images/rails.png +0 -0
- data/rails_2_test/public/index.html +274 -0
- data/rails_2_test/public/javascripts/application.js +2 -0
- data/rails_2_test/public/javascripts/jquery-1.4.4.min.js +167 -0
- data/rails_2_test/public/robots.txt +5 -0
- data/rails_2_test/public/stylesheets/admin.css +3 -0
- data/rails_2_test/public/stylesheets/scaffold.css +54 -0
- data/rails_2_test/script/about +4 -0
- data/rails_2_test/script/autospec +5 -0
- data/rails_2_test/script/console +3 -0
- data/rails_2_test/script/dbconsole +3 -0
- data/rails_2_test/script/destroy +3 -0
- data/rails_2_test/script/generate +3 -0
- data/rails_2_test/script/performance/benchmarker +3 -0
- data/rails_2_test/script/performance/profiler +3 -0
- data/rails_2_test/script/performance/request +3 -0
- data/rails_2_test/script/plugin +3 -0
- data/rails_2_test/script/populate_tables.rb +49 -0
- data/rails_2_test/script/process/inspector +3 -0
- data/rails_2_test/script/process/reaper +3 -0
- data/rails_2_test/script/process/spawner +3 -0
- data/rails_2_test/script/runner +3 -0
- data/rails_2_test/script/server +3 -0
- data/rails_2_test/script/spec +5 -0
- data/rails_2_test/script/spec_server +125 -0
- data/rails_2_test/test/data/ruby_throated.jpg +0 -0
- data/rails_2_test/test/data/tweenbot.jpg +0 -0
- data/rails_2_test/test/fixtures/file_column_images.yml +7 -0
- data/rails_2_test/test/integration/admin/appointments2_integration_test.rb +79 -0
- data/rails_2_test/test/integration/admin/appointments_integration_test.rb +367 -0
- data/rails_2_test/test/integration/admin/blog_posts2_integration_test.rb +806 -0
- data/rails_2_test/test/integration/admin/blog_posts3_integration_test.rb +418 -0
- data/rails_2_test/test/integration/admin/blog_posts4_integration_test.rb +189 -0
- data/rails_2_test/test/integration/admin/blog_posts5_integration_test.rb +75 -0
- data/rails_2_test/test/integration/admin/blog_posts6_integration_test.rb +39 -0
- data/rails_2_test/test/integration/admin/blog_posts_custom_new_and_edit_integration_test.rb +89 -0
- data/rails_2_test/test/integration/admin/blog_posts_integration_test.rb +788 -0
- data/rails_2_test/test/integration/admin/blog_posts_read_only_integration_test.rb +69 -0
- data/rails_2_test/test/integration/admin/bookmarks_integration_test.rb +445 -0
- data/rails_2_test/test/integration/admin/comments2_integration_test.rb +52 -0
- data/rails_2_test/test/integration/admin/comments_integration_test.rb +50 -0
- data/rails_2_test/test/integration/admin/file_column_images2_integration_test.rb +19 -0
- data/rails_2_test/test/integration/admin/file_column_images_integration_test.rb +69 -0
- data/rails_2_test/test/integration/admin/images2_integration_test.rb +33 -0
- data/rails_2_test/test/integration/admin/images_integration_test.rb +79 -0
- data/rails_2_test/test/integration/admin/misconfigured1_integration_test.rb +17 -0
- data/rails_2_test/test/integration/admin/product_categories2_integration_test.rb +179 -0
- data/rails_2_test/test/integration/admin/product_categories_integration_test.rb +12 -0
- data/rails_2_test/test/integration/admin/products2_integration_test.rb +155 -0
- data/rails_2_test/test/integration/admin/products_integration_test.rb +210 -0
- data/rails_2_test/test/integration/admin/television_airings_integration_test.rb +25 -0
- data/rails_2_test/test/integration/admin/users2_integration_test.rb +48 -0
- data/rails_2_test/test/integration/admin/users_integration_test.rb +266 -0
- data/rails_2_test/test/integration/blog_posts_integration_test.rb +9 -0
- data/rails_2_test/test/test_helper.rb +114 -0
- data/rails_2_test/vendor/plugins/file_column/CHANGELOG +69 -0
- data/rails_2_test/vendor/plugins/file_column/README +54 -0
- data/rails_2_test/vendor/plugins/file_column/Rakefile +36 -0
- data/rails_2_test/vendor/plugins/file_column/TODO +6 -0
- data/rails_2_test/vendor/plugins/file_column/init.rb +13 -0
- data/rails_2_test/vendor/plugins/file_column/lib/file_column.rb +723 -0
- data/rails_2_test/vendor/plugins/file_column/lib/file_column_helper.rb +150 -0
- data/rails_2_test/vendor/plugins/file_column/lib/file_compat.rb +28 -0
- data/rails_2_test/vendor/plugins/file_column/lib/magick_file_column.rb +260 -0
- data/rails_2_test/vendor/plugins/file_column/lib/rails_file_column.rb +19 -0
- data/rails_2_test/vendor/plugins/file_column/lib/test_case.rb +124 -0
- data/rails_2_test/vendor/plugins/file_column/lib/validations.rb +112 -0
- data/rails_2_test/vendor/plugins/file_column/test/abstract_unit.rb +63 -0
- data/rails_2_test/vendor/plugins/file_column/test/connection.rb +17 -0
- data/rails_2_test/vendor/plugins/file_column/test/file_column_helper_test.rb +97 -0
- data/rails_2_test/vendor/plugins/file_column/test/file_column_test.rb +650 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/entry.rb +32 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/invalid-image.jpg +1 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/kerb.jpg +0 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/mysql.sql +25 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/schema.rb +10 -0
- data/rails_2_test/vendor/plugins/file_column/test/fixtures/skanthak.png +0 -0
- data/rails_2_test/vendor/plugins/file_column/test/magick_test.rb +380 -0
- data/rails_2_test/vendor/plugins/file_column/test/magick_view_only_test.rb +21 -0
- data/rails_3_test/.gitignore +4 -0
- data/rails_3_test/Gemfile +12 -0
- data/rails_3_test/Gemfile.lock +100 -0
- data/rails_3_test/README +256 -0
- data/rails_3_test/Rakefile +7 -0
- data/rails_3_test/app/controllers/admin/appointments2_controller.rb +15 -0
- data/rails_3_test/app/controllers/admin/appointments_controller.rb +11 -0
- data/rails_3_test/app/controllers/admin/blog_posts2_controller.rb +138 -0
- data/rails_3_test/app/controllers/admin/blog_posts3_controller.rb +76 -0
- data/rails_3_test/app/controllers/admin/blog_posts4_controller.rb +21 -0
- data/rails_3_test/app/controllers/admin/blog_posts5_controller.rb +27 -0
- data/rails_3_test/app/controllers/admin/blog_posts6_controller.rb +10 -0
- data/rails_3_test/app/controllers/admin/blog_posts_controller.rb +9 -0
- data/rails_3_test/app/controllers/admin/blog_posts_custom_new_and_edit_controller.rb +15 -0
- data/rails_3_test/app/controllers/admin/blog_posts_read_only_controller.rb +19 -0
- data/rails_3_test/app/controllers/admin/bookmarks_controller.rb +11 -0
- data/rails_3_test/app/controllers/admin/comments2_controller.rb +14 -0
- data/rails_3_test/app/controllers/admin/comments_controller.rb +12 -0
- data/rails_3_test/app/controllers/admin/images2_controller.rb +11 -0
- data/rails_3_test/app/controllers/admin/images_controller.rb +6 -0
- data/rails_3_test/app/controllers/admin/misconfigured1_controller.rb +7 -0
- data/rails_3_test/app/controllers/admin/not_migrated_yets_controller.rb +9 -0
- data/rails_3_test/app/controllers/admin/product_categories2_controller.rb +7 -0
- data/rails_3_test/app/controllers/admin/product_categories_controller.rb +5 -0
- data/rails_3_test/app/controllers/admin/television_airings_controller.rb +5 -0
- data/rails_3_test/app/controllers/admin/television_time_slots_controller.rb +5 -0
- data/rails_3_test/app/controllers/admin/users2_controller.rb +10 -0
- data/rails_3_test/app/controllers/admin/users_controller.rb +48 -0
- data/rails_3_test/app/controllers/application_controller.rb +7 -0
- data/rails_3_test/app/controllers/blog_posts_controller.rb +5 -0
- data/rails_3_test/app/helpers/admin/appointments_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/blog_posts2_helper.rb +27 -0
- data/rails_3_test/app/helpers/admin/blog_posts3_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/blog_posts4_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/blog_posts6_helper.rb +13 -0
- data/rails_3_test/app/helpers/admin/blog_posts_custom_new_and_edit_helper.rb +29 -0
- data/rails_3_test/app/helpers/admin/blog_posts_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/blog_posts_read_only_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/bookmarks_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/comments2_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/comments_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/images2_helper.rb +8 -0
- data/rails_3_test/app/helpers/admin/images_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/misconfigured1_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/not_migrated_yets_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/product_categories2_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/product_categories_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/television_airings_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/television_time_slots_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/users2_helper.rb +2 -0
- data/rails_3_test/app/helpers/admin/users_helper.rb +13 -0
- data/rails_3_test/app/helpers/application_helper.rb +2 -0
- data/rails_3_test/app/models/appointment.rb +5 -0
- data/rails_3_test/app/models/blog_post.rb +14 -0
- data/rails_3_test/app/models/blog_post_tag.rb +6 -0
- data/rails_3_test/app/models/bookmark.rb +6 -0
- data/rails_3_test/app/models/comment.rb +4 -0
- data/rails_3_test/app/models/image.rb +3 -0
- data/rails_3_test/app/models/not_migrated_yet.rb +2 -0
- data/rails_3_test/app/models/product.rb +5 -0
- data/rails_3_test/app/models/product_category.rb +9 -0
- data/rails_3_test/app/models/tag.rb +4 -0
- data/rails_3_test/app/models/television_airing.rb +3 -0
- data/rails_3_test/app/models/television_time_slot.rb +11 -0
- data/rails_3_test/app/models/user.rb +11 -0
- data/rails_3_test/app/views/admin/appointments/_subject_input.html.erb +2 -0
- data/rails_3_test/app/views/admin/appointments2/_time_input.html.erb +31 -0
- data/rails_3_test/app/views/admin/blog_posts2/_after_form.html.erb +5 -0
- data/rails_3_test/app/views/admin/blog_posts2/_after_index.html.erb +1 -0
- data/rails_3_test/app/views/admin/blog_posts2/_after_tags_input.html.erb +16 -0
- data/rails_3_test/app/views/admin/blog_posts2/_before_index.html.erb +2 -0
- data/rails_3_test/app/views/admin/blog_posts5/_after_index_header.html.erb +8 -0
- data/rails_3_test/app/views/admin/blog_posts_read_only/_body_for_show.html.erb +1 -0
- data/rails_3_test/app/views/layouts/admin.html.erb +32 -0
- data/rails_3_test/app/views/layouts/application.html.erb +14 -0
- data/rails_3_test/config/application.rb +42 -0
- data/rails_3_test/config/boot.rb +6 -0
- data/rails_3_test/config/database.yml +22 -0
- data/rails_3_test/config/environment.rb +5 -0
- data/rails_3_test/config/environments/development.rb +26 -0
- data/rails_3_test/config/environments/production.rb +49 -0
- data/rails_3_test/config/environments/test.rb +35 -0
- data/rails_3_test/config/initializers/backtrace_silencers.rb +7 -0
- data/rails_3_test/config/initializers/inflections.rb +10 -0
- data/rails_3_test/config/initializers/mime_types.rb +5 -0
- data/rails_3_test/config/initializers/secret_token.rb +7 -0
- data/rails_3_test/config/initializers/session_store.rb +8 -0
- data/rails_3_test/config/locales/en.yml +5 -0
- data/rails_3_test/config/routes.rb +63 -0
- data/rails_3_test/config.ru +4 -0
- data/rails_3_test/db/migrate/20090213215514_create_blog_posts.rb +12 -0
- data/rails_3_test/db/migrate/20090217225542_add_body_to_blog_posts.rb +9 -0
- data/rails_3_test/db/migrate/20090221220917_create_tags.rb +13 -0
- data/rails_3_test/db/migrate/20090221220947_create_blog_post_tags.rb +14 -0
- data/rails_3_test/db/migrate/20090222162204_add_textile_to_blog_posts.rb +9 -0
- data/rails_3_test/db/migrate/20090222163231_add_published_at_to_blog_posts.rb +9 -0
- data/rails_3_test/db/migrate/20090301191722_create_images.rb +16 -0
- data/rails_3_test/db/migrate/20090305165345_create_accounts.rb +14 -0
- data/rails_3_test/db/migrate/20090307225027_rename_accounts_to_users.rb +9 -0
- data/rails_3_test/db/migrate/20090307225750_add_user_id_to_blog_posts.rb +9 -0
- data/rails_3_test/db/migrate/20090309185114_change_blog_posts_textile.rb +9 -0
- data/rails_3_test/db/migrate/20090309193635_create_products.rb +14 -0
- data/rails_3_test/db/migrate/20090309203056_create_comments.rb +15 -0
- data/rails_3_test/db/migrate/20090323005947_create_file_column_images.rb +12 -0
- data/rails_3_test/db/migrate/20090326160049_add_birthday_to_users.rb +9 -0
- data/rails_3_test/db/migrate/20090326223606_add_state_to_users.rb +9 -0
- data/rails_3_test/db/migrate/20090503134004_add_file_column_image_to_products.rb +9 -0
- data/rails_3_test/db/migrate/20090617173651_create_bookmarks.rb +15 -0
- data/rails_3_test/db/migrate/20090624165355_add_tags_string_to_blog_post.rb +9 -0
- data/rails_3_test/db/migrate/20090625144313_add_avatar_fields_to_user.rb +11 -0
- data/rails_3_test/db/migrate/20090629202956_add_merged_into_to_blog_posts.rb +9 -0
- data/rails_3_test/db/migrate/20090701171857_add_force_textile_to_users.rb +9 -0
- data/rails_3_test/db/migrate/20090704163647_add_deleted_at_to_products.rb +9 -0
- data/rails_3_test/db/migrate/20090704173800_add_sale_fields_to_products.rb +13 -0
- data/rails_3_test/db/migrate/20090714024501_create_product_categories.rb +15 -0
- data/rails_3_test/db/migrate/20090819162835_add_admin_level_to_users.rb +9 -0
- data/rails_3_test/db/migrate/20091115134559_add_first_and_last_names_to_users.rb +11 -0
- data/rails_3_test/db/migrate/20091221011256_add_position_to_product_categories.rb +9 -0
- data/rails_3_test/db/migrate/20091222160814_create_appointments.rb +14 -0
- data/rails_3_test/db/migrate/20091227224547_add_user_id_to_appointments.rb +9 -0
- data/rails_3_test/db/migrate/20100214213359_create_television_time_slots.rb +13 -0
- data/rails_3_test/db/migrate/20100214213451_create_television_airings.rb +15 -0
- data/rails_3_test/db/migrate/20110426215702_remove_avatar_fields_from_users.rb +11 -0
- data/rails_3_test/db/seeds.rb +7 -0
- data/rails_3_test/doc/README_FOR_APP +2 -0
- data/rails_3_test/lib/tasks/.gitkeep +0 -0
- data/rails_3_test/public/404.html +26 -0
- data/rails_3_test/public/422.html +26 -0
- data/rails_3_test/public/500.html +26 -0
- data/rails_3_test/public/favicon.ico +0 -0
- data/rails_3_test/public/images/rails.png +0 -0
- data/rails_3_test/public/index.html +239 -0
- data/rails_3_test/public/javascripts/application.js +2 -0
- data/rails_3_test/public/javascripts/jquery-1.4.4.min.js +167 -0
- data/rails_3_test/public/javascripts/rails.js +191 -0
- data/rails_3_test/public/robots.txt +5 -0
- data/rails_3_test/public/stylesheets/.gitkeep +0 -0
- data/rails_3_test/public/stylesheets/admin.css +3 -0
- data/rails_3_test/public/stylesheets/scaffold.css +54 -0
- data/rails_3_test/script/rails +6 -0
- data/rails_3_test/test/data/ruby_throated.jpg +0 -0
- data/rails_3_test/test/data/tweenbot.jpg +0 -0
- data/rails_3_test/test/fixtures/placeholder.txt +0 -0
- data/rails_3_test/test/integration/admin/appointments2_integration_test.rb +79 -0
- data/rails_3_test/test/integration/admin/appointments_integration_test.rb +367 -0
- data/rails_3_test/test/integration/admin/blog_posts2_integration_test.rb +789 -0
- data/rails_3_test/test/integration/admin/blog_posts3_integration_test.rb +418 -0
- data/rails_3_test/test/integration/admin/blog_posts4_integration_test.rb +189 -0
- data/rails_3_test/test/integration/admin/blog_posts5_integration_test.rb +75 -0
- data/rails_3_test/test/integration/admin/blog_posts6_integration_test.rb +39 -0
- data/rails_3_test/test/integration/admin/blog_posts_custom_new_and_edit_integration_test.rb +89 -0
- data/rails_3_test/test/integration/admin/blog_posts_integration_test.rb +784 -0
- data/rails_3_test/test/integration/admin/blog_posts_read_only_integration_test.rb +69 -0
- data/rails_3_test/test/integration/admin/bookmarks_integration_test.rb +445 -0
- data/rails_3_test/test/integration/admin/comments2_integration_test.rb +52 -0
- data/rails_3_test/test/integration/admin/comments_integration_test.rb +50 -0
- data/rails_3_test/test/integration/admin/images2_integration_test.rb +33 -0
- data/rails_3_test/test/integration/admin/images_integration_test.rb +79 -0
- data/rails_3_test/test/integration/admin/misconfigured1_integration_test.rb +21 -0
- data/rails_3_test/test/integration/admin/product_categories2_integration_test.rb +179 -0
- data/rails_3_test/test/integration/admin/product_categories_integration_test.rb +12 -0
- data/rails_3_test/test/integration/admin/television_airings_integration_test.rb +25 -0
- data/rails_3_test/test/integration/admin/users2_integration_test.rb +48 -0
- data/rails_3_test/test/integration/admin/users_integration_test.rb +182 -0
- data/rails_3_test/test/integration/blog_posts_integration_test.rb +10 -0
- data/rails_3_test/test/performance/browsing_test.rb +9 -0
- data/rails_3_test/test/test_helper.rb +98 -0
- data/rails_3_test/vendor/plugins/.gitkeep +0 -0
- data/website/_layouts/api.html +20 -17
- data/website/_layouts/api1.html +42 -0
- data/website/_layouts/default.html +42 -18
- data/website/api/core.markdown +6 -3
- data/website/api/destroy.markdown +4 -2
- data/website/api/form.markdown +6 -5
- data/website/api/idx.markdown +8 -7
- data/website/api/index.markdown +9 -7
- data/website/api/search.markdown +5 -3
- data/website/api/show.markdown +4 -2
- data/website/community.markdown +3 -2
- data/website/css/main.css +83 -17
- data/website/design_principles.markdown +3 -8
- data/website/index.markdown +13 -9
- data/website/js/lightbox.js +2 -2
- data/website/quick_start.markdown +10 -10
- data/website/screenshots.markdown +11 -11
- data/website/tutorial.markdown +23 -14
- data/website/v1/api/core.markdown +108 -0
- data/website/v1/api/destroy.markdown +27 -0
- data/website/v1/api/form.markdown +293 -0
- data/website/v1/api/idx.markdown +288 -0
- data/website/v1/api/index.markdown +149 -0
- data/website/v1/api/search.markdown +110 -0
- data/website/v1/api/show.markdown +24 -0
- data/website/v1/index.markdown +10 -0
- data/website/v1/quick_start.markdown +48 -0
- data/website/v1/tutorial.markdown +60 -0
- metadata +407 -9
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
require 'magick_file_column'
|
|
4
|
+
|
|
5
|
+
module FileColumn # :nodoc:
|
|
6
|
+
def self.append_features(base)
|
|
7
|
+
super
|
|
8
|
+
base.extend(ClassMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.create_state(instance,attr)
|
|
12
|
+
filename = instance[attr]
|
|
13
|
+
if filename.nil? or filename.empty?
|
|
14
|
+
NoUploadedFile.new(instance,attr)
|
|
15
|
+
else
|
|
16
|
+
PermanentUploadedFile.new(instance,attr)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.init_options(defaults, model, attr)
|
|
21
|
+
options = defaults.dup
|
|
22
|
+
options[:store_dir] ||= File.join(options[:root_path], model, attr)
|
|
23
|
+
unless options[:store_dir].is_a?(Symbol)
|
|
24
|
+
options[:tmp_base_dir] ||= File.join(options[:store_dir], "tmp")
|
|
25
|
+
end
|
|
26
|
+
options[:base_url] ||= options[:web_root] + File.join(model, attr)
|
|
27
|
+
|
|
28
|
+
[:store_dir, :tmp_base_dir].each do |dir_sym|
|
|
29
|
+
if options[dir_sym].is_a?(String) and !File.exists?(options[dir_sym])
|
|
30
|
+
FileUtils.mkpath(options[dir_sym])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
options
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class BaseUploadedFile # :nodoc:
|
|
38
|
+
|
|
39
|
+
def initialize(instance,attr)
|
|
40
|
+
@instance, @attr = instance, attr
|
|
41
|
+
@options_method = "#{attr}_options".to_sym
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def assign(file)
|
|
46
|
+
if file.is_a? File
|
|
47
|
+
# this did not come in via a CGI request. However,
|
|
48
|
+
# assigning files directly may be useful, so we
|
|
49
|
+
# make just this file object similar enough to an uploaded
|
|
50
|
+
# file that we can handle it.
|
|
51
|
+
file.extend FileColumn::FileCompat
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if file.nil?
|
|
55
|
+
delete
|
|
56
|
+
else
|
|
57
|
+
if file.size == 0
|
|
58
|
+
# user did not submit a file, so we
|
|
59
|
+
# can simply ignore this
|
|
60
|
+
self
|
|
61
|
+
else
|
|
62
|
+
if file.is_a?(String)
|
|
63
|
+
# if file is a non-empty string it is most probably
|
|
64
|
+
# the filename and the user forgot to set the encoding
|
|
65
|
+
# to multipart/form-data. Since we would raise an exception
|
|
66
|
+
# because of the missing "original_filename" method anyways,
|
|
67
|
+
# we raise a more meaningful exception rightaway.
|
|
68
|
+
raise TypeError.new("Do not know how to handle a string with value '#{file}' that was passed to a file_column. Check if the form's encoding has been set to 'multipart/form-data'.")
|
|
69
|
+
end
|
|
70
|
+
upload(file)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def just_uploaded?
|
|
76
|
+
@just_uploaded
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def on_save(&blk)
|
|
80
|
+
@on_save ||= []
|
|
81
|
+
@on_save << Proc.new
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# the following methods are overriden by sub-classes if needed
|
|
85
|
+
|
|
86
|
+
def temp_path
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def absolute_dir
|
|
91
|
+
if absolute_path then File.dirname(absolute_path) else nil end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def relative_dir
|
|
95
|
+
if relative_path then File.dirname(relative_path) else nil end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def after_save
|
|
99
|
+
@on_save.each { |blk| blk.call } if @on_save
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def after_destroy
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def options
|
|
107
|
+
@instance.send(@options_method)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def store_dir
|
|
113
|
+
if options[:store_dir].is_a? Symbol
|
|
114
|
+
raise ArgumentError.new("'#{options[:store_dir]}' is not an instance method of class #{@instance.class.name}") unless @instance.respond_to?(options[:store_dir])
|
|
115
|
+
|
|
116
|
+
dir = File.join(options[:root_path], @instance.send(options[:store_dir]))
|
|
117
|
+
FileUtils.mkpath(dir) unless File.exists?(dir)
|
|
118
|
+
dir
|
|
119
|
+
else
|
|
120
|
+
options[:store_dir]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def tmp_base_dir
|
|
125
|
+
if options[:tmp_base_dir]
|
|
126
|
+
options[:tmp_base_dir]
|
|
127
|
+
else
|
|
128
|
+
dir = File.join(store_dir, "tmp")
|
|
129
|
+
FileUtils.mkpath(dir) unless File.exists?(dir)
|
|
130
|
+
dir
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def clone_as(klass)
|
|
135
|
+
klass.new(@instance, @attr)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class NoUploadedFile < BaseUploadedFile # :nodoc:
|
|
142
|
+
def delete
|
|
143
|
+
# we do not have a file so deleting is easy
|
|
144
|
+
self
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def upload(file)
|
|
148
|
+
# replace ourselves with a TempUploadedFile
|
|
149
|
+
temp = clone_as TempUploadedFile
|
|
150
|
+
temp.store_upload(file)
|
|
151
|
+
temp
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def absolute_path(subdir=nil)
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def relative_path(subdir=nil)
|
|
160
|
+
nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def assign_temp(temp_path)
|
|
164
|
+
return self if temp_path.nil? or temp_path.empty?
|
|
165
|
+
temp = clone_as TempUploadedFile
|
|
166
|
+
temp.parse_temp_path temp_path
|
|
167
|
+
temp
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class RealUploadedFile < BaseUploadedFile # :nodoc:
|
|
172
|
+
def absolute_path(subdir=nil)
|
|
173
|
+
if subdir
|
|
174
|
+
File.join(@dir, subdir, @filename)
|
|
175
|
+
else
|
|
176
|
+
File.join(@dir, @filename)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def relative_path(subdir=nil)
|
|
181
|
+
if subdir
|
|
182
|
+
File.join(relative_path_prefix, subdir, @filename)
|
|
183
|
+
else
|
|
184
|
+
File.join(relative_path_prefix, @filename)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
# regular expressions to try for identifying extensions
|
|
191
|
+
EXT_REGEXPS = [
|
|
192
|
+
/^(.+)\.([^.]+\.[^.]+)$/, # matches "something.tar.gz"
|
|
193
|
+
/^(.+)\.([^.]+)$/ # matches "something.jpg"
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
def split_extension(filename,fallback=nil)
|
|
197
|
+
EXT_REGEXPS.each do |regexp|
|
|
198
|
+
if filename =~ regexp
|
|
199
|
+
base,ext = $1, $2
|
|
200
|
+
return [base, ext] if options[:extensions].include?(ext.downcase)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
if fallback and filename =~ EXT_REGEXPS.last
|
|
204
|
+
return [$1, $2]
|
|
205
|
+
end
|
|
206
|
+
[filename, ""]
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
class TempUploadedFile < RealUploadedFile # :nodoc:
|
|
212
|
+
|
|
213
|
+
def store_upload(file)
|
|
214
|
+
@tmp_dir = FileColumn.generate_temp_name
|
|
215
|
+
@dir = File.join(tmp_base_dir, @tmp_dir)
|
|
216
|
+
FileUtils.mkdir(@dir)
|
|
217
|
+
|
|
218
|
+
@filename = FileColumn::sanitize_filename(file.original_filename)
|
|
219
|
+
local_file_path = File.join(tmp_base_dir,@tmp_dir,@filename)
|
|
220
|
+
|
|
221
|
+
# stored uploaded file into local_file_path
|
|
222
|
+
# If it was a Tempfile object, the temporary file will be
|
|
223
|
+
# cleaned up automatically, so we do not have to care for this
|
|
224
|
+
if file.respond_to?(:local_path) and file.local_path and File.exists?(file.local_path)
|
|
225
|
+
FileUtils.copy_file(file.local_path, local_file_path)
|
|
226
|
+
elsif file.respond_to?(:read)
|
|
227
|
+
File.open(local_file_path, "wb") { |f| f.write(file.read) }
|
|
228
|
+
else
|
|
229
|
+
raise ArgumentError.new("Do not know how to handle #{file.inspect}")
|
|
230
|
+
end
|
|
231
|
+
File.chmod(options[:permissions], local_file_path)
|
|
232
|
+
|
|
233
|
+
if options[:fix_file_extensions]
|
|
234
|
+
# try to determine correct file extension and fix
|
|
235
|
+
# if necessary
|
|
236
|
+
content_type = get_content_type((file.content_type.chomp if file.content_type))
|
|
237
|
+
if content_type and options[:mime_extensions][content_type]
|
|
238
|
+
@filename = correct_extension(@filename,options[:mime_extensions][content_type])
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
new_local_file_path = File.join(tmp_base_dir,@tmp_dir,@filename)
|
|
242
|
+
File.rename(local_file_path, new_local_file_path) unless new_local_file_path == local_file_path
|
|
243
|
+
local_file_path = new_local_file_path
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
@instance[@attr] = @filename
|
|
247
|
+
@just_uploaded = true
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# tries to identify and strip the extension of filename
|
|
252
|
+
# if an regular expresion from EXT_REGEXPS matches and the
|
|
253
|
+
# downcased extension is a known extension (in options[:extensions])
|
|
254
|
+
# we'll strip this extension
|
|
255
|
+
def strip_extension(filename)
|
|
256
|
+
split_extension(filename).first
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def correct_extension(filename, ext)
|
|
260
|
+
strip_extension(filename) << ".#{ext}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def parse_temp_path(temp_path, instance_options=nil)
|
|
264
|
+
raise ArgumentError.new("invalid format of '#{temp_path}'") unless temp_path =~ %r{^((\d+\.)+\d+)/([^/].+)$}
|
|
265
|
+
@tmp_dir, @filename = $1, FileColumn.sanitize_filename($3)
|
|
266
|
+
@dir = File.join(tmp_base_dir, @tmp_dir)
|
|
267
|
+
|
|
268
|
+
@instance[@attr] = @filename unless instance_options == :ignore_instance
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def upload(file)
|
|
272
|
+
# store new file
|
|
273
|
+
temp = clone_as TempUploadedFile
|
|
274
|
+
temp.store_upload(file)
|
|
275
|
+
|
|
276
|
+
# delete old copy
|
|
277
|
+
delete_files
|
|
278
|
+
|
|
279
|
+
# and return new TempUploadedFile object
|
|
280
|
+
temp
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def delete
|
|
284
|
+
delete_files
|
|
285
|
+
@instance[@attr] = ""
|
|
286
|
+
clone_as NoUploadedFile
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def assign_temp(temp_path)
|
|
290
|
+
return self if temp_path.nil? or temp_path.empty?
|
|
291
|
+
# we can ignore this since we've already received a newly uploaded file
|
|
292
|
+
|
|
293
|
+
# however, we delete the old temporary files
|
|
294
|
+
temp = clone_as TempUploadedFile
|
|
295
|
+
temp.parse_temp_path(temp_path, :ignore_instance)
|
|
296
|
+
temp.delete_files
|
|
297
|
+
|
|
298
|
+
self
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def temp_path
|
|
302
|
+
File.join(@tmp_dir, @filename)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def after_save
|
|
306
|
+
super
|
|
307
|
+
|
|
308
|
+
# we have a newly uploaded image, move it to the correct location
|
|
309
|
+
file = clone_as PermanentUploadedFile
|
|
310
|
+
file.move_from(File.join(tmp_base_dir, @tmp_dir), @just_uploaded)
|
|
311
|
+
|
|
312
|
+
# delete temporary files
|
|
313
|
+
delete_files
|
|
314
|
+
|
|
315
|
+
# replace with the new PermanentUploadedFile object
|
|
316
|
+
file
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def delete_files
|
|
320
|
+
FileUtils.rm_rf(File.join(tmp_base_dir, @tmp_dir))
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def get_content_type(fallback=nil)
|
|
324
|
+
if options[:file_exec]
|
|
325
|
+
begin
|
|
326
|
+
content_type = `#{options[:file_exec]} -bi "#{File.join(@dir,@filename)}"`.chomp
|
|
327
|
+
content_type = fallback unless $?.success?
|
|
328
|
+
content_type.gsub!(/;.+$/,"") if content_type
|
|
329
|
+
content_type
|
|
330
|
+
rescue
|
|
331
|
+
fallback
|
|
332
|
+
end
|
|
333
|
+
else
|
|
334
|
+
fallback
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
private
|
|
339
|
+
|
|
340
|
+
def relative_path_prefix
|
|
341
|
+
File.join("tmp", @tmp_dir)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class PermanentUploadedFile < RealUploadedFile # :nodoc:
|
|
347
|
+
def initialize(*args)
|
|
348
|
+
super *args
|
|
349
|
+
@dir = File.join(store_dir, relative_path_prefix)
|
|
350
|
+
@filename = @instance[@attr]
|
|
351
|
+
@filename = nil if @filename.empty?
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def move_from(local_dir, just_uploaded)
|
|
355
|
+
# remove old permament dir first
|
|
356
|
+
# this creates a short moment, where neither the old nor
|
|
357
|
+
# the new files exist but we can't do much about this as
|
|
358
|
+
# filesystems aren't transactional.
|
|
359
|
+
FileUtils.rm_rf @dir
|
|
360
|
+
|
|
361
|
+
FileUtils.mv local_dir, @dir
|
|
362
|
+
|
|
363
|
+
@just_uploaded = just_uploaded
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def upload(file)
|
|
367
|
+
temp = clone_as TempUploadedFile
|
|
368
|
+
temp.store_upload(file)
|
|
369
|
+
temp
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def delete
|
|
373
|
+
file = clone_as NoUploadedFile
|
|
374
|
+
@instance[@attr] = ""
|
|
375
|
+
file.on_save { delete_files }
|
|
376
|
+
file
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def assign_temp(temp_path)
|
|
380
|
+
return nil if temp_path.nil? or temp_path.empty?
|
|
381
|
+
|
|
382
|
+
temp = clone_as TempUploadedFile
|
|
383
|
+
temp.parse_temp_path(temp_path)
|
|
384
|
+
temp
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def after_destroy
|
|
388
|
+
delete_files
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def delete_files
|
|
392
|
+
FileUtils.rm_rf @dir
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
private
|
|
396
|
+
|
|
397
|
+
def relative_path_prefix
|
|
398
|
+
raise RuntimeError.new("Trying to access file_column, but primary key got lost.") if @instance.id.to_s.empty?
|
|
399
|
+
@instance.id.to_s
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# The FileColumn module allows you to easily handle file uploads. You can designate
|
|
404
|
+
# one or more columns of your model's table as "file columns" like this:
|
|
405
|
+
#
|
|
406
|
+
# class Entry < ActiveRecord::Base
|
|
407
|
+
#
|
|
408
|
+
# file_column :image
|
|
409
|
+
# end
|
|
410
|
+
#
|
|
411
|
+
# Now, by default, an uploaded file "test.png" for an entry object with primary key 42 will
|
|
412
|
+
# be stored in in "public/entry/image/42/test.png". The filename "test.png" will be stored
|
|
413
|
+
# in the record's "image" column. The "entries" table should have a +VARCHAR+ column
|
|
414
|
+
# named "image".
|
|
415
|
+
#
|
|
416
|
+
# The methods of this module are automatically included into <tt>ActiveRecord::Base</tt>
|
|
417
|
+
# as class methods, so that you can use them in your models.
|
|
418
|
+
#
|
|
419
|
+
# == Generated Methods
|
|
420
|
+
#
|
|
421
|
+
# After calling "<tt>file_column :image</tt>" as in the example above, a number of instance methods
|
|
422
|
+
# will automatically be generated, all prefixed by "image":
|
|
423
|
+
#
|
|
424
|
+
# * <tt>Entry#image=(uploaded_file)</tt>: this will handle a newly uploaded file
|
|
425
|
+
# (see below). Note that
|
|
426
|
+
# you can simply call your upload field "entry[image]" in your view (or use the
|
|
427
|
+
# helper).
|
|
428
|
+
# * <tt>Entry#image(subdir=nil)</tt>: This will return an absolute path (as a
|
|
429
|
+
# string) to the currently uploaded file
|
|
430
|
+
# or nil if no file has been uploaded
|
|
431
|
+
# * <tt>Entry#image_relative_path(subdir=nil)</tt>: This will return a path relative to
|
|
432
|
+
# this file column's base directory
|
|
433
|
+
# as a string or nil if no file has been uploaded. This would be "42/test.png" in the example.
|
|
434
|
+
# * <tt>Entry#image_just_uploaded?</tt>: Returns true if a new file has been uploaded to this instance.
|
|
435
|
+
# You can use this in your code to perform certain actions (e. g., validation,
|
|
436
|
+
# custom post-processing) only on newly uploaded files.
|
|
437
|
+
#
|
|
438
|
+
# You can access the raw value of the "image" column (which will contain the filename) via the
|
|
439
|
+
# <tt>ActiveRecord::Base#attributes</tt> or <tt>ActiveRecord::Base#[]</tt> methods like this:
|
|
440
|
+
#
|
|
441
|
+
# entry['image'] # e.g."test.png"
|
|
442
|
+
#
|
|
443
|
+
# == Storage of uploaded files
|
|
444
|
+
#
|
|
445
|
+
# For a model class +Entry+ and a column +image+, all files will be stored under
|
|
446
|
+
# "public/entry/image". A sub-directory named after the primary key of the object will
|
|
447
|
+
# be created, so that files can be stored using their real filename. For example, a file
|
|
448
|
+
# "test.png" stored in an Entry object with id 42 will be stored in
|
|
449
|
+
#
|
|
450
|
+
# public/entry/image/42/test.png
|
|
451
|
+
#
|
|
452
|
+
# Files will be moved to this location in an +after_save+ callback. They will be stored in
|
|
453
|
+
# a temporary location previously as explained in the next section.
|
|
454
|
+
#
|
|
455
|
+
# By default, files will be created with unix permissions of <tt>0644</tt> (i. e., owner has
|
|
456
|
+
# read/write access, group and others only have read access). You can customize
|
|
457
|
+
# this by passing the desired mode as a <tt>:permissions</tt> options. The value
|
|
458
|
+
# you give here is passed directly to <tt>File::chmod</tt>, so on Unix you should
|
|
459
|
+
# give some octal value like 0644, for example.
|
|
460
|
+
#
|
|
461
|
+
# == Handling of form redisplay
|
|
462
|
+
#
|
|
463
|
+
# Suppose you have a form for creating a new object where the user can upload an image. The form may
|
|
464
|
+
# have to be re-displayed because of validation errors. The uploaded file has to be stored somewhere so
|
|
465
|
+
# that the user does not have to upload it again. FileColumn will store these in a temporary directory
|
|
466
|
+
# (called "tmp" and located under the column's base directory by default) so that it can be moved to
|
|
467
|
+
# the final location if the object is successfully created. If the form is never completed, though, you
|
|
468
|
+
# can easily remove all the images in this "tmp" directory once per day or so.
|
|
469
|
+
#
|
|
470
|
+
# So in the example above, the image "test.png" would first be stored in
|
|
471
|
+
# "public/entry/image/tmp/<some_random_key>/test.png" and be moved to
|
|
472
|
+
# "public/entry/image/<primary_key>/test.png".
|
|
473
|
+
#
|
|
474
|
+
# This temporary location of newly uploaded files has another advantage when updating objects. If the
|
|
475
|
+
# update fails for some reasons (e.g. due to validations), the existing image will not be overwritten, so
|
|
476
|
+
# it has a kind of "transactional behaviour".
|
|
477
|
+
#
|
|
478
|
+
# == Additional Files and Directories
|
|
479
|
+
#
|
|
480
|
+
# FileColumn allows you to keep more than one file in a directory and will move/delete
|
|
481
|
+
# all the files and directories it finds in a model object's directory when necessary.
|
|
482
|
+
#
|
|
483
|
+
# As a convenience you can access files stored in sub-directories via the +subdir+
|
|
484
|
+
# parameter if they have the same filename.
|
|
485
|
+
#
|
|
486
|
+
# Suppose your uploaded file is named "vancouver.jpg" and you want to create a
|
|
487
|
+
# thumb-nail and store it in the "thumb" directory. If you call
|
|
488
|
+
# <tt>image("thumb")</tt>, you
|
|
489
|
+
# will receive an absolute path for the file "thumb/vancouver.jpg" in the same
|
|
490
|
+
# directory "vancouver.jpg" is stored. Look at the documentation of FileColumn::Magick
|
|
491
|
+
# for more examples and how to create these thumb-nails automatically.
|
|
492
|
+
#
|
|
493
|
+
# == File Extensions
|
|
494
|
+
#
|
|
495
|
+
# FileColumn will try to fix the file extension of uploaded files, so that
|
|
496
|
+
# the files are served with the correct mime-type by your web-server. Most
|
|
497
|
+
# web-servers are setting the mime-type based on the file's extension. You
|
|
498
|
+
# can disable this behaviour by passing the <tt>:fix_file_extensions</tt> option
|
|
499
|
+
# with a value of +nil+ to +file_column+.
|
|
500
|
+
#
|
|
501
|
+
# In order to set the correct extension, FileColumn tries to determine
|
|
502
|
+
# the files mime-type first. It then uses the +MIME_EXTENSIONS+ hash to
|
|
503
|
+
# choose the corresponding file extension. You can override this hash
|
|
504
|
+
# by passing in a <tt>:mime_extensions</tt> option to +file_column+.
|
|
505
|
+
#
|
|
506
|
+
# The mime-type of the uploaded file is determined with the following steps:
|
|
507
|
+
#
|
|
508
|
+
# 1. Run the external "file" utility. You can specify the full path to
|
|
509
|
+
# the executable in the <tt>:file_exec</tt> option or set this option
|
|
510
|
+
# to +nil+ to disable this step
|
|
511
|
+
#
|
|
512
|
+
# 2. If the file utility couldn't determine the mime-type or the utility was not
|
|
513
|
+
# present, the content-type provided by the user's browser is used
|
|
514
|
+
# as a fallback.
|
|
515
|
+
#
|
|
516
|
+
# == Custom Storage Directories
|
|
517
|
+
#
|
|
518
|
+
# FileColumn's storage location is determined in the following way. All
|
|
519
|
+
# files are saved below the so-called "root_path" directory, which defaults to
|
|
520
|
+
# "RAILS_ROOT/public". For every file_column, you can set a separte "store_dir"
|
|
521
|
+
# option. It defaults to "model_name/attribute_name".
|
|
522
|
+
#
|
|
523
|
+
# Files will always be stored in sub-directories of the store_dir path. The
|
|
524
|
+
# subdirectory is named after the instance's +id+ attribute for a saved model,
|
|
525
|
+
# or "tmp/<randomkey>" for unsaved models.
|
|
526
|
+
#
|
|
527
|
+
# You can specify a custom root_path by setting the <tt>:root_path</tt> option.
|
|
528
|
+
#
|
|
529
|
+
# You can specify a custom storage_dir by setting the <tt>:storage_dir</tt> option.
|
|
530
|
+
#
|
|
531
|
+
# For setting a static storage_dir that doesn't change with respect to a particular
|
|
532
|
+
# instance, you assign <tt>:storage_dir</tt> a String representing a directory
|
|
533
|
+
# as an absolute path.
|
|
534
|
+
#
|
|
535
|
+
# If you need more fine-grained control over the storage directory, you
|
|
536
|
+
# can use the name of a callback-method as a symbol for the
|
|
537
|
+
# <tt>:store_dir</tt> option. This method has to be defined as an
|
|
538
|
+
# instance method in your model. It will be called without any arguments
|
|
539
|
+
# whenever the storage directory for an uploaded file is needed. It should return
|
|
540
|
+
# a String representing a directory relativeo to root_path.
|
|
541
|
+
#
|
|
542
|
+
# Uploaded files for unsaved models objects will be stored in a temporary
|
|
543
|
+
# directory. By default this directory will be a "tmp" directory in
|
|
544
|
+
# your <tt>:store_dir</tt>. You can override this via the
|
|
545
|
+
# <tt>:tmp_base_dir</tt> option.
|
|
546
|
+
module ClassMethods
|
|
547
|
+
|
|
548
|
+
# default mapping of mime-types to file extensions. FileColumn will try to
|
|
549
|
+
# rename a file to the correct extension if it detects a known mime-type
|
|
550
|
+
MIME_EXTENSIONS = {
|
|
551
|
+
"image/gif" => "gif",
|
|
552
|
+
"image/jpeg" => "jpg",
|
|
553
|
+
"image/pjpeg" => "jpg",
|
|
554
|
+
"image/x-png" => "png",
|
|
555
|
+
"image/jpg" => "jpg",
|
|
556
|
+
"image/png" => "png",
|
|
557
|
+
"application/x-shockwave-flash" => "swf",
|
|
558
|
+
"application/pdf" => "pdf",
|
|
559
|
+
"application/pgp-signature" => "sig",
|
|
560
|
+
"application/futuresplash" => "spl",
|
|
561
|
+
"application/msword" => "doc",
|
|
562
|
+
"application/postscript" => "ps",
|
|
563
|
+
"application/x-bittorrent" => "torrent",
|
|
564
|
+
"application/x-dvi" => "dvi",
|
|
565
|
+
"application/x-gzip" => "gz",
|
|
566
|
+
"application/x-ns-proxy-autoconfig" => "pac",
|
|
567
|
+
"application/x-shockwave-flash" => "swf",
|
|
568
|
+
"application/x-tgz" => "tar.gz",
|
|
569
|
+
"application/x-tar" => "tar",
|
|
570
|
+
"application/zip" => "zip",
|
|
571
|
+
"audio/mpeg" => "mp3",
|
|
572
|
+
"audio/x-mpegurl" => "m3u",
|
|
573
|
+
"audio/x-ms-wma" => "wma",
|
|
574
|
+
"audio/x-ms-wax" => "wax",
|
|
575
|
+
"audio/x-wav" => "wav",
|
|
576
|
+
"image/x-xbitmap" => "xbm",
|
|
577
|
+
"image/x-xpixmap" => "xpm",
|
|
578
|
+
"image/x-xwindowdump" => "xwd",
|
|
579
|
+
"text/css" => "css",
|
|
580
|
+
"text/html" => "html",
|
|
581
|
+
"text/javascript" => "js",
|
|
582
|
+
"text/plain" => "txt",
|
|
583
|
+
"text/xml" => "xml",
|
|
584
|
+
"video/mpeg" => "mpeg",
|
|
585
|
+
"video/quicktime" => "mov",
|
|
586
|
+
"video/x-msvideo" => "avi",
|
|
587
|
+
"video/x-ms-asf" => "asf",
|
|
588
|
+
"video/x-ms-wmv" => "wmv"
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
EXTENSIONS = Set.new MIME_EXTENSIONS.values
|
|
592
|
+
EXTENSIONS.merge %w(jpeg)
|
|
593
|
+
|
|
594
|
+
# default options. You can override these with +file_column+'s +options+ parameter
|
|
595
|
+
DEFAULT_OPTIONS = {
|
|
596
|
+
:root_path => File.join(RAILS_ROOT, "public"),
|
|
597
|
+
:web_root => "",
|
|
598
|
+
:mime_extensions => MIME_EXTENSIONS,
|
|
599
|
+
:extensions => EXTENSIONS,
|
|
600
|
+
:fix_file_extensions => true,
|
|
601
|
+
:permissions => 0644,
|
|
602
|
+
|
|
603
|
+
# path to the unix "file" executbale for
|
|
604
|
+
# guessing the content-type of files
|
|
605
|
+
:file_exec => "file"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
# handle the +attr+ attribute as a "file-upload" column, generating additional methods as explained
|
|
609
|
+
# above. You should pass the attribute's name as a symbol, like this:
|
|
610
|
+
#
|
|
611
|
+
# file_column :image
|
|
612
|
+
#
|
|
613
|
+
# You can pass in an options hash that overrides the options
|
|
614
|
+
# in +DEFAULT_OPTIONS+.
|
|
615
|
+
def file_column(attr, options={})
|
|
616
|
+
options = DEFAULT_OPTIONS.merge(options) if options
|
|
617
|
+
|
|
618
|
+
my_options = FileColumn::init_options(
|
|
619
|
+
options,
|
|
620
|
+
(RAILS_GEM_VERSION=='2.1.0' ? Inflector : ActiveSupport::Inflector).
|
|
621
|
+
underscore(self.name).to_s,
|
|
622
|
+
attr.to_s
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
state_attr = "@#{attr}_state".to_sym
|
|
626
|
+
state_method = "#{attr}_state".to_sym
|
|
627
|
+
|
|
628
|
+
define_method state_method do
|
|
629
|
+
result = instance_variable_get state_attr
|
|
630
|
+
if result.nil?
|
|
631
|
+
result = FileColumn::create_state(self, attr.to_s)
|
|
632
|
+
instance_variable_set state_attr, result
|
|
633
|
+
end
|
|
634
|
+
result
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
private state_method
|
|
638
|
+
|
|
639
|
+
define_method attr do |*args|
|
|
640
|
+
send(state_method).absolute_path *args
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
define_method "#{attr}_relative_path" do |*args|
|
|
644
|
+
send(state_method).relative_path *args
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
define_method "#{attr}_dir" do
|
|
648
|
+
send(state_method).absolute_dir
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
define_method "#{attr}_relative_dir" do
|
|
652
|
+
send(state_method).relative_dir
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
define_method "#{attr}=" do |file|
|
|
656
|
+
state = send(state_method).assign(file)
|
|
657
|
+
instance_variable_set state_attr, state
|
|
658
|
+
if state.options[:after_upload] and state.just_uploaded?
|
|
659
|
+
state.options[:after_upload].each do |sym|
|
|
660
|
+
self.send sym
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
define_method "#{attr}_temp" do
|
|
666
|
+
send(state_method).temp_path
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
define_method "#{attr}_temp=" do |temp_path|
|
|
670
|
+
instance_variable_set state_attr, send(state_method).assign_temp(temp_path)
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
after_save_method = "#{attr}_after_save".to_sym
|
|
674
|
+
|
|
675
|
+
define_method after_save_method do
|
|
676
|
+
instance_variable_set state_attr, send(state_method).after_save
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
after_save after_save_method
|
|
680
|
+
|
|
681
|
+
after_destroy_method = "#{attr}_after_destroy".to_sym
|
|
682
|
+
|
|
683
|
+
define_method after_destroy_method do
|
|
684
|
+
send(state_method).after_destroy
|
|
685
|
+
end
|
|
686
|
+
after_destroy after_destroy_method
|
|
687
|
+
|
|
688
|
+
define_method "#{attr}_just_uploaded?" do
|
|
689
|
+
send(state_method).just_uploaded?
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
# this creates a closure keeping a reference to my_options
|
|
693
|
+
# right now that's the only way we store the options. We
|
|
694
|
+
# might use a class attribute as well
|
|
695
|
+
define_method "#{attr}_options" do
|
|
696
|
+
my_options
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
private after_save_method, after_destroy_method
|
|
700
|
+
|
|
701
|
+
FileColumn::MagickExtension::file_column(self, attr, my_options) if options[:magick]
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
private
|
|
707
|
+
|
|
708
|
+
def self.generate_temp_name
|
|
709
|
+
now = Time.now
|
|
710
|
+
"#{now.to_i}.#{now.usec}.#{Process.pid}"
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def self.sanitize_filename(filename)
|
|
714
|
+
filename = File.basename(filename.gsub("\\", "/")) # work-around for IE
|
|
715
|
+
filename.gsub!(/[^a-zA-Z0-9\.\-\+_]/,"_")
|
|
716
|
+
filename = "_#{filename}" if filename =~ /^\.+$/
|
|
717
|
+
filename = "unnamed" if filename.size == 0
|
|
718
|
+
filename
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
|