imagine_cms 3.0.0.beta6 → 3.0.0.beta7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/app/assets/images/codepress/line-numbers.png +0 -0
- data/app/assets/images/cropper/marqueeHoriz.gif +0 -0
- data/app/assets/images/cropper/marqueeVert.gif +0 -0
- data/app/assets/images/management/btn_add.gif +0 -0
- data/app/assets/images/management/btn_archive.gif +0 -0
- data/app/assets/images/management/btn_delete.gif +0 -0
- data/app/assets/images/management/btn_duplicate.gif +0 -0
- data/app/assets/images/management/btn_edit.gif +0 -0
- data/app/assets/images/management/btn_help.gif +0 -0
- data/app/assets/images/management/btn_new_page.gif +0 -0
- data/app/assets/images/management/btn_preview.gif +0 -0
- data/app/assets/images/management/btn_properties.gif +0 -0
- data/app/assets/images/management/btn_restore.gif +0 -0
- data/app/assets/images/management/btn_top_delete.gif +0 -0
- data/app/assets/images/management/btn_top_duplicate.gif +0 -0
- data/app/assets/images/management/btn_top_edit.gif +0 -0
- data/app/assets/images/management/btn_top_new.gif +0 -0
- data/app/assets/images/management/btn_top_preview.gif +0 -0
- data/app/assets/images/management/btn_top_properties.gif +0 -0
- data/app/assets/{manage → images/management}/bullet.gif +0 -0
- data/app/assets/images/management/cvv2_graphic.gif +0 -0
- data/app/assets/images/management/error.gif +0 -0
- data/app/assets/images/management/gallery_index.gif +0 -0
- data/app/assets/images/management/gallery_preview_overlay.png +0 -0
- data/app/assets/images/management/gallery_small_drag_overlay.png +0 -0
- data/app/assets/images/management/gallery_small_overlay.png +0 -0
- data/app/assets/images/management/gallery_sort.gif +0 -0
- data/app/assets/images/management/icon_download.gif +0 -0
- data/app/assets/images/management/icon_locked.png +0 -0
- data/app/assets/images/management/icon_page.gif +0 -0
- data/app/assets/images/management/icon_time.gif +0 -0
- data/app/assets/images/management/icon_unlocked.png +0 -0
- data/app/assets/images/management/page_loading.gif +0 -0
- data/app/assets/{manage → images/management}/start.gif +0 -0
- data/app/assets/images/management/vcard.gif +0 -0
- data/app/assets/javascripts/builder.js +101 -0
- data/app/assets/javascripts/codepress/codepress.html +36 -0
- data/app/assets/javascripts/codepress/codepress.js +130 -0
- data/app/assets/javascripts/codepress/engines/gecko.js +240 -0
- data/{vendor/gems/acts_as_tree/test/abstract_unit.rb → app/assets/javascripts/codepress/engines/khtml.js} +0 -0
- data/app/assets/javascripts/codepress/engines/msie.js +263 -0
- data/{vendor/gems/acts_as_tree/test/database.yml → app/assets/javascripts/codepress/engines/older.js} +0 -0
- data/app/assets/javascripts/codepress/engines/opera.js +259 -0
- data/app/assets/javascripts/codepress/languages/css.js +23 -0
- data/app/assets/javascripts/codepress/languages/generic.js +25 -0
- data/app/assets/javascripts/codepress/languages/html.js +63 -0
- data/app/assets/javascripts/codepress/languages/java.js +24 -0
- data/app/assets/javascripts/codepress/languages/javascript.js +30 -0
- data/app/assets/javascripts/codepress/languages/perl.js +27 -0
- data/app/assets/javascripts/codepress/languages/php.js +60 -0
- data/app/assets/javascripts/codepress/languages/ruby.js +26 -0
- data/app/assets/javascripts/codepress/languages/sql.js +30 -0
- data/app/assets/javascripts/codepress/languages/text.js +9 -0
- data/app/assets/javascripts/cropper.js +568 -0
- data/app/assets/javascripts/dojo/dojo.js +14155 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowB.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowBL.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowBR.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowL.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowR.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowT.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowTL.png +0 -0
- data/app/assets/javascripts/dojo/src/html/images/shadowTR.png +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/Editor2/showtableborder_gecko.css +19 -0
- data/app/assets/javascripts/dojo/src/widget/templates/HslColorPicker.svg +30 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/aggregate.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/aggregate.psd +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/backcolor.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/bg-fade.png +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/bold.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/cancel.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/copy.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/createlink.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/cut.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/delete.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/forecolor.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/hilitecolor.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/indent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/inserthorizontalrule.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertimage.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertorderedlist.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/inserttable.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertunorderedlist.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/italic.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifycenter.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyfull.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyleft.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyright.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/left_to_right.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_bullet_indent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_bullet_outdent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_num_indent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_num_outdent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/outdent.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/paste.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/redo.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/removeformat.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/right_to_left.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/save.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/sep.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/space.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/strikethrough.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/subscript.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/superscript.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/underline.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/undo.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/buttons/wikiword.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/check.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/decrementMonth.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/decrementWeek.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/grabCorner.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/floatingPaneClose.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaAccordionOff.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaAccordionSelected.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-c.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-l.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-r.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaBarBg.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-c.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-l.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-r.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-c.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-l.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-r.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaMenuBg.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-c.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-l.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-r.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/tab_close.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/images/toolbar-bg.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/incrementMonth.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/incrementWeek.gif +0 -0
- data/app/assets/javascripts/dojo/src/widget/templates/richtextframe.html +24 -0
- data/app/assets/javascripts/imagine.js +1385 -0
- data/app/assets/javascripts/jquery_no_conflict.js +9405 -0
- data/app/assets/stylesheets/codepress/codepress.css +7 -0
- data/app/assets/stylesheets/codepress/languages/css.css +10 -0
- data/app/assets/stylesheets/codepress/languages/generic.css +9 -0
- data/app/assets/stylesheets/codepress/languages/html.css +18 -0
- data/app/assets/stylesheets/codepress/languages/java.css +7 -0
- data/app/assets/stylesheets/codepress/languages/javascript.css +8 -0
- data/app/assets/stylesheets/codepress/languages/perl.css +11 -0
- data/app/assets/stylesheets/codepress/languages/php.css +12 -0
- data/app/assets/stylesheets/codepress/languages/ruby.css +10 -0
- data/app/assets/stylesheets/codepress/languages/sql.css +10 -0
- data/app/assets/stylesheets/codepress/languages/text.css +5 -0
- data/app/assets/stylesheets/cropper.css +182 -0
- data/app/assets/stylesheets/management.css +96 -0
- data/app/assets/stylesheets/reset.css +58 -0
- data/app/controllers/cms/content_controller.rb +318 -2
- data/app/controllers/management/cms_controller.rb +1669 -0
- data/app/controllers/management/user_controller.rb +4 -4
- data/app/controllers/management/users_controller.rb +18 -4
- data/app/controllers/util_controller.rb +45 -0
- data/app/helpers/cms_application_helper.rb +662 -15
- data/app/models/cms_content_sweeper.rb +21 -0
- data/app/models/cms_page.rb +126 -0
- data/app/models/cms_page_object.rb +23 -0
- data/app/models/cms_page_tag.rb +5 -0
- data/app/models/cms_page_version.rb +3 -0
- data/app/models/cms_snippet.rb +16 -0
- data/app/models/cms_template.rb +29 -0
- data/app/models/user.rb +6 -3
- data/app/views/management/cms/_complete_gallery.html.erb +5 -0
- data/app/views/management/cms/_create_file_link.html.erb +21 -0
- data/app/views/management/cms/_crop_feature_image.html.erb +188 -0
- data/app/views/management/cms/_crop_image.html.erb +188 -0
- data/app/views/management/cms/_crop_results.html.erb +1 -0
- data/app/views/management/cms/_crop_results_feature_image.html.erb +1 -0
- data/app/views/management/cms/_crop_results_thumb.html.erb +1 -0
- data/app/views/management/cms/_crop_thumb.html.erb +188 -0
- data/app/views/management/cms/_dialogs.html.erb +39 -0
- data/app/views/management/cms/_edit_page.html.erb +176 -0
- data/app/views/management/cms/_gallery_index.html.erb +10 -0
- data/app/views/management/cms/_gallery_setup.html.erb +22 -0
- data/app/views/management/cms/_image.html.erb +3 -0
- data/app/views/management/cms/_image_details.html.erb +26 -0
- data/app/views/management/cms/_image_draggable.html.erb +4 -0
- data/app/views/management/cms/_list_page.html.erb +8 -0
- data/app/views/management/cms/_list_page_select.html.erb +8 -0
- data/app/views/management/cms/_list_pages.html.erb +1 -0
- data/app/views/management/cms/_list_pages_select.html.erb +1 -0
- data/app/views/management/cms/_page_attribute.html.erb +6 -0
- data/app/views/management/cms/_page_list.html.erb +171 -0
- data/app/views/management/cms/_page_list_source_folder.html.erb +20 -0
- data/app/views/management/cms/_page_list_source_tag.html.erb +18 -0
- data/app/views/management/cms/_select_gallery.html.erb +117 -0
- data/app/views/management/cms/_snippet.html.erb +3 -0
- data/app/views/management/cms/_sort_images.html.erb +15 -0
- data/app/views/management/cms/_temp.html.erb +3 -0
- data/app/views/management/cms/_template_options.html.erb +21 -0
- data/app/views/management/cms/_template_reference.html.erb +42 -0
- data/app/views/management/cms/_upload_feature_image.html.erb +35 -0
- data/app/views/management/cms/_upload_file.html.erb +31 -0
- data/app/views/management/cms/_upload_image.html.erb +74 -0
- data/app/views/management/cms/_upload_thumb.html.erb +35 -0
- data/app/views/management/cms/edit_master.html.erb +48 -0
- data/app/views/management/cms/edit_page_content.html.erb +4 -0
- data/app/views/management/cms/edit_snippet.html.erb +47 -0
- data/app/views/management/cms/edit_template.html.erb +48 -0
- data/app/views/management/cms/gallery_management.html.erb +108 -0
- data/app/views/management/cms/index.html.erb +20 -0
- data/app/views/management/cms/page_tags_for_lookup.html.erb +5 -0
- data/app/views/management/cms/pages.html.erb +99 -0
- data/app/views/management/cms/permission_denied.html.erb +1 -0
- data/app/views/management/cms/select_page.html.erb +57 -0
- data/app/views/management/cms/snippets.html.erb +14 -0
- data/app/views/management/cms/templates.html.erb +14 -0
- data/app/views/management/cms/toolbar_edit.html.erb +269 -0
- data/app/views/management/cms/toolbar_preview.html.erb +109 -0
- data/app/views/util/_calendar_days.html.erb +72 -0
- data/app/views/util/_calendar_month_year.html.erb +1 -0
- data/app/views/util/_date_picker.html.erb +56 -0
- data/app/views/util/_message.html.erb +1 -0
- data/app/views/util/_show_message.js.erb +6 -0
- data/app/views/util/_tab.html.erb +4 -0
- data/config/routes.rb +4 -1
- data/imagine_cms.gemspec +4 -0
- data/{vendor/gems → lib}/acts_as_versioned/.document +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/.gitignore +2 -0
- data/{vendor/gems → lib}/acts_as_versioned/CHANGELOG +0 -0
- data/lib/acts_as_versioned/Gemfile +7 -0
- data/{vendor/gems → lib}/acts_as_versioned/MIT-LICENSE +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/README +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/RUNNING_UNIT_TESTS +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/Rakefile +1 -1
- data/{vendor/gems → lib}/acts_as_versioned/acts_as_versioned.gemspec +4 -4
- data/{vendor/gems → lib}/acts_as_versioned/init.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/lib/acts_as_versioned.rb +109 -107
- data/{vendor/gems → lib}/acts_as_versioned/test/abstract_unit.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/database.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/authors.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmark.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmark_versions.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmarks.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/locked_pages.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/locked_pages_revisions.yml +0 -0
- data/{vendor/gems/acts_as_versioned/test/fixtures/migrations/1_add_versioned_tables.rb → lib/acts_as_versioned/test/fixtures/migrations/2_add_versioned_tables.rb} +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/page.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/page_versions.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/pages.yml +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/widget.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/migration_test.rb +0 -1
- data/{vendor/gems → lib}/acts_as_versioned/test/schema.rb +0 -0
- data/{vendor/gems → lib}/acts_as_versioned/test/versioned_test.rb +0 -0
- data/lib/extensions/action_controller.rb +154 -143
- data/lib/imagine_cms/engine.rb +33 -12
- data/lib/imagine_cms/version.rb +1 -1
- data/lib/imagine_cms.rb +30 -6
- data/lib/prototype_legacy_helper/README.markdown +13 -0
- data/lib/prototype_legacy_helper/init.rb +1 -0
- data/lib/prototype_legacy_helper/lib/prototype_legacy_helper.rb +430 -0
- data/lib/prototype_legacy_helper/test/test_prototype_helper.rb +297 -0
- data/lib/upload_progress/CHANGELOG +5 -0
- data/lib/upload_progress/MIT-LICENSE +20 -0
- data/lib/upload_progress/README +45 -0
- data/{vendor/gems/acts_as_tree → lib/upload_progress}/Rakefile +6 -5
- data/lib/upload_progress/init.rb +7 -0
- data/lib/upload_progress/lib/multipart_progress.rb +176 -0
- data/lib/upload_progress/lib/progress.rb +145 -0
- data/lib/upload_progress/lib/upload_progress.rb +303 -0
- data/lib/upload_progress/lib/upload_progress_helper.rb +425 -0
- data/lib/upload_progress/public/stylesheets/upload_progress.css +21 -0
- data/lib/upload_progress/test/multipart_progress_testx.rb +364 -0
- data/lib/upload_progress/test/upload_progress_helper_testx.rb +134 -0
- data/lib/upload_progress/test/upload_progress_testx.rb +88 -0
- metadata +305 -43
- data/app/assets/manage/btn_delete.gif +0 -0
- data/vendor/gems/.DS_Store +0 -0
- data/vendor/gems/acts_as_tree/README +0 -26
- data/vendor/gems/acts_as_tree/init.rb +0 -1
- data/vendor/gems/acts_as_tree/lib/active_record/acts/tree.rb +0 -96
- data/vendor/gems/acts_as_tree/test/acts_as_tree_test.rb +0 -219
- data/vendor/gems/acts_as_tree/test/fixtures/mixin.rb +0 -0
- data/vendor/gems/acts_as_tree/test/fixtures/mixins.yml +0 -0
- data/vendor/gems/acts_as_tree/test/schema.rb +0 -0
- data/vendor/gems/acts_as_versioned/Gemfile +0 -7
@@ -0,0 +1,297 @@
|
|
1
|
+
if ENV['RAILS_ROOT']
|
2
|
+
environment = File.expand_path('vendor/gems/environment', ENV['RAILS_ROOT'])
|
3
|
+
require environment if File.exist?("#{environment}.rb")
|
4
|
+
end
|
5
|
+
|
6
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'action_view'
|
10
|
+
require 'action_controller'
|
11
|
+
require 'active_model'
|
12
|
+
require 'prototype_helper'
|
13
|
+
|
14
|
+
class Bunny < Struct.new(:Bunny, :id)
|
15
|
+
end
|
16
|
+
|
17
|
+
class Author
|
18
|
+
extend ActiveModel::Naming
|
19
|
+
|
20
|
+
attr_reader :id
|
21
|
+
def save; @id = 1 end
|
22
|
+
def new_record?; @id.nil? end
|
23
|
+
def name
|
24
|
+
@id.nil? ? 'new author' : "author ##{@id}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Article
|
29
|
+
extend ActiveModel::Naming
|
30
|
+
|
31
|
+
attr_reader :id
|
32
|
+
attr_reader :author_id
|
33
|
+
def save; @id = 1; @author_id = 1 end
|
34
|
+
def new_record?; @id.nil? end
|
35
|
+
def name
|
36
|
+
@id.nil? ? 'new article' : "article ##{@id}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Author::Nested < Author; end
|
41
|
+
|
42
|
+
class PrototypeHelperTest < ActionView::TestCase
|
43
|
+
attr_accessor :formats, :output_buffer, :template_format
|
44
|
+
|
45
|
+
def _evaluate_assigns_and_ivars() end
|
46
|
+
|
47
|
+
def reset_formats(format)
|
48
|
+
@format = format
|
49
|
+
end
|
50
|
+
|
51
|
+
def setup
|
52
|
+
@record = @author = Author.new
|
53
|
+
@article = Article.new
|
54
|
+
super
|
55
|
+
@template = self
|
56
|
+
@controller = Class.new do
|
57
|
+
def url_for(options)
|
58
|
+
if options.is_a?(String)
|
59
|
+
options
|
60
|
+
else
|
61
|
+
url = "http://www.example.com/"
|
62
|
+
url << options[:action].to_s if options and options[:action]
|
63
|
+
url << "?a=#{options[:a]}" if options && options[:a]
|
64
|
+
url << "&b=#{options[:b]}" if options && options[:a] && options[:b]
|
65
|
+
url
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end.new
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def test_observe_form
|
73
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {new Ajax.Request('http://www.example.com/cart_changed', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
|
74
|
+
observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" })
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_observe_form_using_function_for_callback
|
78
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Observer('cart', 2, function(element, value) {alert('Form changed')})\n//]]>\n</script>),
|
79
|
+
observe_form("cart", :frequency => 2, :function => "alert('Form changed')")
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_observe_field
|
83
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/reorder_if_empty', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
|
84
|
+
observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" })
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_observe_field_using_with_option
|
88
|
+
expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>)
|
89
|
+
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id')
|
90
|
+
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)")
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_observe_field_using_json_in_with_option
|
94
|
+
expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:{'id':value}})})\n//]]>\n</script>)
|
95
|
+
assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_observe_field_using_function_for_callback
|
99
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {alert('Element changed')})\n//]]>\n</script>),
|
100
|
+
observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')")
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_observe_field_without_frequency
|
104
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.EventObserver('glass', function(element, value) {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:value})})\n//]]>\n</script>),
|
105
|
+
observe_field("glass")
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def test_periodically_call_remote
|
110
|
+
assert_dom_equal %(<script type="text/javascript">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Updater('schremser_bier', 'http://www.example.com/mehr_bier', {asynchronous:true, evalScripts:true})}, 10)\n//]]>\n</script>),
|
111
|
+
periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" })
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_periodically_call_remote_with_frequency
|
115
|
+
assert_dom_equal(
|
116
|
+
"<script type=\"text/javascript\">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true})}, 2)\n//]]>\n</script>",
|
117
|
+
periodically_call_remote(:frequency => 2)
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def test_form_remote_tag
|
123
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
|
124
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast })
|
125
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
|
126
|
+
form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast })
|
127
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
|
128
|
+
form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast })
|
129
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">),
|
130
|
+
form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast })
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_form_remote_tag_with_method
|
134
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\"><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div>),
|
135
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put })
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_form_remote_tag_with_block_in_erb
|
139
|
+
__in_erb_template = ''
|
140
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" }
|
141
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">Hello world!</form>), output_buffer
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_on_callbacks
|
145
|
+
callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure]
|
146
|
+
callbacks.each do |callback|
|
147
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
|
148
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
|
149
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
|
150
|
+
form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
|
151
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({failure:'glass_of_beer'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
|
152
|
+
form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
|
153
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater({success:'glass_of_beer',failure:'glass_of_water'}, 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
|
154
|
+
form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();")
|
155
|
+
end
|
156
|
+
|
157
|
+
#HTTP status codes 200 up to 599 have callbacks
|
158
|
+
#these should work
|
159
|
+
100.upto(599) do |callback|
|
160
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on#{callback.to_s.capitalize}:function(request){monkeys();}, parameters:Form.serialize(this)}); return false;">),
|
161
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
|
162
|
+
end
|
163
|
+
|
164
|
+
#test 200 and 404
|
165
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, parameters:Form.serialize(this)}); return false;">),
|
166
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();")
|
167
|
+
|
168
|
+
#these shouldn't
|
169
|
+
1.upto(99) do |callback|
|
170
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
|
171
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
|
172
|
+
end
|
173
|
+
600.upto(999) do |callback|
|
174
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">),
|
175
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
|
176
|
+
end
|
177
|
+
|
178
|
+
#test ultimate combo
|
179
|
+
assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, on200:function(request){monkeys();}, on404:function(request){bananas();}, onComplete:function(request){c();}, onFailure:function(request){f();}, onLoading:function(request){c1()}, onSuccess:function(request){s()}, parameters:Form.serialize(this)}); return false;\">),
|
180
|
+
form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();")
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_remote_form_for_with_record_identification_with_new_record
|
184
|
+
remote_form_for(@record, {:html => { :id => 'create-author' }}) {}
|
185
|
+
|
186
|
+
expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' id='create-author' method='post'></form>)
|
187
|
+
assert_dom_equal expected, output_buffer
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_remote_form_for_with_record_identification_without_html_options
|
191
|
+
remote_form_for(@record) {}
|
192
|
+
|
193
|
+
expected = %(<form action='#{authors_path}' onsubmit="new Ajax.Request('#{authors_path}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' method='post' id='new_author'></form>)
|
194
|
+
assert_dom_equal expected, output_buffer
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_remote_form_for_with_record_identification_with_existing_record
|
198
|
+
@record.save
|
199
|
+
remote_form_for(@record) {}
|
200
|
+
|
201
|
+
expected = %(<form action='#{author_path(@record)}' id='edit_author_1' method='post' onsubmit="new Ajax.Request('#{author_path(@record)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_author'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
|
202
|
+
assert_dom_equal expected, output_buffer
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_remote_form_for_with_new_object_in_list
|
206
|
+
remote_form_for([@author, @article]) {}
|
207
|
+
|
208
|
+
expected = %(<form action='#{author_articles_path(@author)}' onsubmit="new Ajax.Request('#{author_articles_path(@author)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_article' method='post' id='new_article'></form>)
|
209
|
+
assert_dom_equal expected, output_buffer
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_remote_form_for_with_existing_object_in_list
|
213
|
+
@author.save
|
214
|
+
@article.save
|
215
|
+
remote_form_for([@author, @article]) {}
|
216
|
+
|
217
|
+
expected = %(<form action='#{author_article_path(@author, @article)}' id='edit_article_1' method='post' onsubmit="new Ajax.Request('#{author_article_path(@author, @article)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_article'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
|
218
|
+
assert_dom_equal expected, output_buffer
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
def test_button_to_remote
|
223
|
+
assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true});\" />),
|
224
|
+
button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" })
|
225
|
+
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.reponseText)}});\" />),
|
226
|
+
button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" })
|
227
|
+
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.reponseText)}});\" />),
|
228
|
+
button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" })
|
229
|
+
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
|
230
|
+
button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" })
|
231
|
+
assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}});\" />),
|
232
|
+
button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_submit_to_remote
|
236
|
+
assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});\" type=\"button\" value=\"1000000\" />),
|
237
|
+
submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
def test_link_to_remote
|
242
|
+
assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
|
243
|
+
link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" })
|
244
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
245
|
+
link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" })
|
246
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
247
|
+
link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot" })
|
248
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
249
|
+
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
|
250
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
|
251
|
+
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
|
252
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:false, evalScripts:true}); return false;\">Remote outauthor</a>),
|
253
|
+
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous)
|
254
|
+
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, insertion:'bottom'}); return false;\">Remote outauthor</a>),
|
255
|
+
link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom)
|
256
|
+
end
|
257
|
+
|
258
|
+
def test_link_to_remote_html_options
|
259
|
+
assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>),
|
260
|
+
link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } })
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_link_to_remote_url_quote_escaping
|
264
|
+
assert_dom_equal %(<a href="#" onclick="new Ajax.Request('http://www.example.com/whatnot\\\'s', {asynchronous:true, evalScripts:true}); return false;">Remote</a>),
|
265
|
+
link_to_remote("Remote", { :url => { :action => "whatnot's" } })
|
266
|
+
end
|
267
|
+
|
268
|
+
protected
|
269
|
+
def request_forgery_protection_token
|
270
|
+
nil
|
271
|
+
end
|
272
|
+
|
273
|
+
def protect_against_forgery?
|
274
|
+
false
|
275
|
+
end
|
276
|
+
|
277
|
+
def create_generator
|
278
|
+
block = Proc.new { |*args| yield *args if block_given? }
|
279
|
+
JavaScriptGenerator.new self, &block
|
280
|
+
end
|
281
|
+
|
282
|
+
def author_path(record)
|
283
|
+
"/authors/#{record.id}"
|
284
|
+
end
|
285
|
+
|
286
|
+
def authors_path
|
287
|
+
"/authors"
|
288
|
+
end
|
289
|
+
|
290
|
+
def author_articles_path(author)
|
291
|
+
"/authors/#{author.id}/articles"
|
292
|
+
end
|
293
|
+
|
294
|
+
def author_article_path(author, article)
|
295
|
+
"/authors/#{author.id}/articles/#{article.id}"
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2004 Sean Treadway, Thomas Fuchs
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
=Overview
|
2
|
+
|
3
|
+
The upload progress plugin will alter your rails CGI handling to track the uploaded status of multipart/form encoded posts. The plugin will also add helper methods to create an AJAX updating progress bar.
|
4
|
+
|
5
|
+
==Installing
|
6
|
+
|
7
|
+
Run "script/plugin upload_progress" or checkout the source into your plugins folder.
|
8
|
+
|
9
|
+
svn checkout http://dev.rubyonrails.com/svn/plugins/upload_progress
|
10
|
+
|
11
|
+
Expand this archive in your Rails.root/vendor/plugins directory. The resulting plugin directory should look like:
|
12
|
+
|
13
|
+
Rails.root/vendor/plugins/upload_progress/
|
14
|
+
Rails.root/vendor/plugins/upload_progress/README.txt
|
15
|
+
Rails.root/vendor/plugins/upload_progress/Rakefile
|
16
|
+
Rails.root/vendor/plugins/upload_progress/init.rb
|
17
|
+
Rails.root/vendor/plugins/upload_progress/public/
|
18
|
+
Rails.root/vendor/plugins/upload_progress/public/stylesheets/
|
19
|
+
Rails.root/vendor/plugins/upload_progress/public/stylesheets/upload_progress.css
|
20
|
+
Rails.root/vendor/plugins/upload_progress/lib/
|
21
|
+
Rails.root/vendor/plugins/upload_progress/doc/
|
22
|
+
Rails.root/vendor/plugins/upload_progress/test/
|
23
|
+
|
24
|
+
The stylesheets included in public/stylesheets are used as a guideline for styling your progress bar and status messages. You can copy them directly into your own public/stylesheets folder or copy and paste.
|
25
|
+
|
26
|
+
==Documentation
|
27
|
+
|
28
|
+
You can create the documentation by running:
|
29
|
+
|
30
|
+
rake rdoc
|
31
|
+
|
32
|
+
==Requirements
|
33
|
+
|
34
|
+
The requirments for getting periodic upload progress updates can be found here:
|
35
|
+
|
36
|
+
http://sean.treadway.info/articles/2005/07/18/upload-progress-checklist
|
37
|
+
|
38
|
+
==Credits
|
39
|
+
|
40
|
+
Sean Treadway <seant@superchannel.org> http://sean.treadway.info
|
41
|
+
Thomas Fuchs <thomas@fesch.at> http://mir.aculo.us
|
42
|
+
|
43
|
+
==License
|
44
|
+
|
45
|
+
This plugin is released under the MIT license. See MIT-LICENSE for details
|
@@ -5,18 +5,19 @@ require 'rake/rdoctask'
|
|
5
5
|
desc 'Default: run unit tests.'
|
6
6
|
task :default => :test
|
7
7
|
|
8
|
-
desc 'Test
|
8
|
+
desc 'Test the upload progress plugin.'
|
9
9
|
Rake::TestTask.new(:test) do |t|
|
10
10
|
t.libs << 'lib'
|
11
11
|
t.pattern = 'test/**/*_test.rb'
|
12
12
|
t.verbose = true
|
13
13
|
end
|
14
14
|
|
15
|
-
desc 'Generate documentation for
|
15
|
+
desc 'Generate documentation for the upload progress plugin.'
|
16
16
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
17
|
rdoc.rdoc_dir = 'rdoc'
|
18
|
-
rdoc.title = '
|
19
|
-
rdoc.options << '--line-numbers
|
20
|
-
rdoc.rdoc_files.include('README')
|
18
|
+
rdoc.title = 'Upload progress'
|
19
|
+
rdoc.options << '--line-numbers --inline-source'
|
20
|
+
rdoc.rdoc_files.include('README.txt')
|
21
21
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
22
|
end
|
23
|
+
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# == Overview
|
2
|
+
#
|
3
|
+
# This module will extend the CGI module with methods to track the upload
|
4
|
+
# progress for multipart forms for use with progress meters. The progress is
|
5
|
+
# saved in the session to be used from any request from any server with the
|
6
|
+
# same session. In other words, this module will work across application
|
7
|
+
# instances.
|
8
|
+
#
|
9
|
+
# === Usage
|
10
|
+
#
|
11
|
+
# Just do your file-uploads as you normally would, but include an upload_id in
|
12
|
+
# the query string of your form action. Your form post action should look
|
13
|
+
# like:
|
14
|
+
#
|
15
|
+
# <form method="post" enctype="multipart/form-data" action="postaction?upload_id=SOMEIDYOUSET">
|
16
|
+
# <input type="file" name="client_file"/>
|
17
|
+
# </form>
|
18
|
+
#
|
19
|
+
# Query the upload state in a progress by reading the progress from the session
|
20
|
+
#
|
21
|
+
# class UploadController < ApplicationController
|
22
|
+
# def upload_status
|
23
|
+
# render :text => "Percent complete: " + @session[:uploads]['SOMEIDYOUSET'].completed_percent"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# === Session options
|
28
|
+
#
|
29
|
+
# Upload progress uses the session options defined in
|
30
|
+
# ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS. If you are passing
|
31
|
+
# custom session options to your dispatcher then please follow the
|
32
|
+
# "recommended way to change session options":http://wiki.rubyonrails.com/rails/show/HowtoChangeSessionOptions
|
33
|
+
#
|
34
|
+
# === Update frequency
|
35
|
+
#
|
36
|
+
# During an upload, the progress will be written to the session every 2
|
37
|
+
# seconds. This prevents excessive writes yet maintains a decent picture of
|
38
|
+
# the upload progress for larger files.
|
39
|
+
#
|
40
|
+
# User interfaces that update more often that every 2 seconds will display the same results.
|
41
|
+
# Consider this update frequency when designing your progress polling.
|
42
|
+
#
|
43
|
+
|
44
|
+
require 'cgi'
|
45
|
+
|
46
|
+
class CGI #:nodoc:
|
47
|
+
class ProgressIO < SimpleDelegator #:nodoc:
|
48
|
+
MIN_SAVE_INTERVAL = 1.0 # Number of seconds between session saves
|
49
|
+
|
50
|
+
attr_reader :progress, :session
|
51
|
+
|
52
|
+
def initialize(orig_io, progress, session)
|
53
|
+
@session = session
|
54
|
+
@progress = progress
|
55
|
+
|
56
|
+
@start_time = Time.now
|
57
|
+
@last_save_time = @start_time
|
58
|
+
save_progress
|
59
|
+
|
60
|
+
super(orig_io)
|
61
|
+
end
|
62
|
+
|
63
|
+
def read(*args)
|
64
|
+
data = __getobj__.read(*args)
|
65
|
+
|
66
|
+
if data and data.size > 0
|
67
|
+
now = Time.now
|
68
|
+
elapsed = now - @start_time
|
69
|
+
progress.update!(data.size, elapsed)
|
70
|
+
|
71
|
+
if now - @last_save_time > MIN_SAVE_INTERVAL
|
72
|
+
save_progress
|
73
|
+
@last_save_time = now
|
74
|
+
end
|
75
|
+
else
|
76
|
+
ActionController::Base.logger.debug("CGI::ProgressIO#read returns nothing when it should return nil if IO is finished: [#{args.inspect}], a cancelled upload or old FCGI bindings. Resetting the upload progress")
|
77
|
+
|
78
|
+
progress.reset!
|
79
|
+
save_progress
|
80
|
+
end
|
81
|
+
|
82
|
+
data
|
83
|
+
end
|
84
|
+
|
85
|
+
def save_progress
|
86
|
+
@session.update
|
87
|
+
end
|
88
|
+
|
89
|
+
def finish
|
90
|
+
@session.update
|
91
|
+
ActionController::Base.logger.debug("Finished processing multipart upload in #{@progress.elapsed_seconds.to_s}s")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
module QueryExtension #:nodoc:
|
96
|
+
# Need to do lazy aliasing on the instance that we are extending because of the way QueryExtension
|
97
|
+
# gets included for each instance of the CGI object rather than on a module level. This method is a
|
98
|
+
# bit obtrusive because we are overriding CGI::QueryExtension::extended which could be used in the
|
99
|
+
# future. Need to research a better method
|
100
|
+
def self.extended(obj)
|
101
|
+
obj.instance_eval do
|
102
|
+
# unless defined? will prevent clobbering the progress IO on multiple extensions
|
103
|
+
alias :stdinput_without_progress :stdinput unless defined? stdinput_without_progress
|
104
|
+
alias :stdinput :stdinput_with_progress
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def stdinput_with_progress
|
109
|
+
@stdin_with_progress or stdinput_without_progress
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
# Bootstrapped on UploadProgress::upload_status_for
|
114
|
+
def read_multipart_with_progress(boundary, content_length)
|
115
|
+
begin
|
116
|
+
begin
|
117
|
+
# Session disabled if the default session options have been set to 'false'
|
118
|
+
options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
|
119
|
+
raise RuntimeError.new("Multipart upload progress disabled, no session options") unless options
|
120
|
+
|
121
|
+
options = options.stringify_keys
|
122
|
+
|
123
|
+
# Pull in the application controller to satisfy any dependencies on class definitions
|
124
|
+
# of instances stored in the session.
|
125
|
+
# Be sure to stay compatible with Rails 1.0/const_load!
|
126
|
+
if Object.const_defined?(:Controllers) and Controllers.respond_to?(:const_load!)
|
127
|
+
Controllers.const_load!(:ApplicationController, "application") unless Controllers.const_defined?(:ApplicationController)
|
128
|
+
else
|
129
|
+
require_dependency('application.rb') unless Object.const_defined?(:ApplicationController)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Assumes that @cookies has already been setup
|
133
|
+
# Raises nomethod if upload_id is not defined
|
134
|
+
@params = CGI::parse(read_params_from_query)
|
135
|
+
upload_id = @params[(options['upload_key'] || 'upload_id')].first
|
136
|
+
raise RuntimeError.new("Multipart upload progress disabled, no upload id in query string") unless upload_id
|
137
|
+
|
138
|
+
upload_progress = UploadProgress::Progress.new(content_length)
|
139
|
+
|
140
|
+
session = Session.new(self, options)
|
141
|
+
session[:uploads] = {} unless session[:uploads]
|
142
|
+
session[:uploads].delete(upload_id) # in case the same upload id is used twice
|
143
|
+
session[:uploads][upload_id] = upload_progress
|
144
|
+
|
145
|
+
@stdin_with_progress = CGI::ProgressIO.new(stdinput_without_progress, upload_progress, session)
|
146
|
+
ActionController::Base.logger.debug("Multipart upload with progress (id: #{upload_id}, size: #{content_length})")
|
147
|
+
rescue
|
148
|
+
ActionController::Base.logger.debug("Exception during setup of read_multipart_with_progress: #{$!}")
|
149
|
+
end
|
150
|
+
ensure
|
151
|
+
begin
|
152
|
+
params = read_multipart_without_progress(boundary, content_length)
|
153
|
+
@stdin_with_progress.finish if @stdin_with_progress.respond_to? :finish
|
154
|
+
ensure
|
155
|
+
@stdin_with_progress = nil
|
156
|
+
session.close if session
|
157
|
+
end
|
158
|
+
end
|
159
|
+
params
|
160
|
+
end
|
161
|
+
|
162
|
+
# Required because of changes in [4388]
|
163
|
+
unless private_instance_methods.include?("read_params_from_query")
|
164
|
+
def read_params_from_query
|
165
|
+
read_params(:get,nil)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Prevent redefinition of aliases on multiple includes
|
170
|
+
unless private_instance_methods.include?("read_multipart_without_progress")
|
171
|
+
alias_method :read_multipart_without_progress, :read_multipart
|
172
|
+
alias_method :read_multipart, :read_multipart_with_progress
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|