imagine_cms 3.0.0.beta6 → 3.0.0.beta7

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.
Files changed (278) hide show
  1. data/README.rdoc +1 -1
  2. data/app/assets/images/codepress/line-numbers.png +0 -0
  3. data/app/assets/images/cropper/marqueeHoriz.gif +0 -0
  4. data/app/assets/images/cropper/marqueeVert.gif +0 -0
  5. data/app/assets/images/management/btn_add.gif +0 -0
  6. data/app/assets/images/management/btn_archive.gif +0 -0
  7. data/app/assets/images/management/btn_delete.gif +0 -0
  8. data/app/assets/images/management/btn_duplicate.gif +0 -0
  9. data/app/assets/images/management/btn_edit.gif +0 -0
  10. data/app/assets/images/management/btn_help.gif +0 -0
  11. data/app/assets/images/management/btn_new_page.gif +0 -0
  12. data/app/assets/images/management/btn_preview.gif +0 -0
  13. data/app/assets/images/management/btn_properties.gif +0 -0
  14. data/app/assets/images/management/btn_restore.gif +0 -0
  15. data/app/assets/images/management/btn_top_delete.gif +0 -0
  16. data/app/assets/images/management/btn_top_duplicate.gif +0 -0
  17. data/app/assets/images/management/btn_top_edit.gif +0 -0
  18. data/app/assets/images/management/btn_top_new.gif +0 -0
  19. data/app/assets/images/management/btn_top_preview.gif +0 -0
  20. data/app/assets/images/management/btn_top_properties.gif +0 -0
  21. data/app/assets/{manage → images/management}/bullet.gif +0 -0
  22. data/app/assets/images/management/cvv2_graphic.gif +0 -0
  23. data/app/assets/images/management/error.gif +0 -0
  24. data/app/assets/images/management/gallery_index.gif +0 -0
  25. data/app/assets/images/management/gallery_preview_overlay.png +0 -0
  26. data/app/assets/images/management/gallery_small_drag_overlay.png +0 -0
  27. data/app/assets/images/management/gallery_small_overlay.png +0 -0
  28. data/app/assets/images/management/gallery_sort.gif +0 -0
  29. data/app/assets/images/management/icon_download.gif +0 -0
  30. data/app/assets/images/management/icon_locked.png +0 -0
  31. data/app/assets/images/management/icon_page.gif +0 -0
  32. data/app/assets/images/management/icon_time.gif +0 -0
  33. data/app/assets/images/management/icon_unlocked.png +0 -0
  34. data/app/assets/images/management/page_loading.gif +0 -0
  35. data/app/assets/{manage → images/management}/start.gif +0 -0
  36. data/app/assets/images/management/vcard.gif +0 -0
  37. data/app/assets/javascripts/builder.js +101 -0
  38. data/app/assets/javascripts/codepress/codepress.html +36 -0
  39. data/app/assets/javascripts/codepress/codepress.js +130 -0
  40. data/app/assets/javascripts/codepress/engines/gecko.js +240 -0
  41. data/{vendor/gems/acts_as_tree/test/abstract_unit.rb → app/assets/javascripts/codepress/engines/khtml.js} +0 -0
  42. data/app/assets/javascripts/codepress/engines/msie.js +263 -0
  43. data/{vendor/gems/acts_as_tree/test/database.yml → app/assets/javascripts/codepress/engines/older.js} +0 -0
  44. data/app/assets/javascripts/codepress/engines/opera.js +259 -0
  45. data/app/assets/javascripts/codepress/languages/css.js +23 -0
  46. data/app/assets/javascripts/codepress/languages/generic.js +25 -0
  47. data/app/assets/javascripts/codepress/languages/html.js +63 -0
  48. data/app/assets/javascripts/codepress/languages/java.js +24 -0
  49. data/app/assets/javascripts/codepress/languages/javascript.js +30 -0
  50. data/app/assets/javascripts/codepress/languages/perl.js +27 -0
  51. data/app/assets/javascripts/codepress/languages/php.js +60 -0
  52. data/app/assets/javascripts/codepress/languages/ruby.js +26 -0
  53. data/app/assets/javascripts/codepress/languages/sql.js +30 -0
  54. data/app/assets/javascripts/codepress/languages/text.js +9 -0
  55. data/app/assets/javascripts/cropper.js +568 -0
  56. data/app/assets/javascripts/dojo/dojo.js +14155 -0
  57. data/app/assets/javascripts/dojo/src/html/images/shadowB.png +0 -0
  58. data/app/assets/javascripts/dojo/src/html/images/shadowBL.png +0 -0
  59. data/app/assets/javascripts/dojo/src/html/images/shadowBR.png +0 -0
  60. data/app/assets/javascripts/dojo/src/html/images/shadowL.png +0 -0
  61. data/app/assets/javascripts/dojo/src/html/images/shadowR.png +0 -0
  62. data/app/assets/javascripts/dojo/src/html/images/shadowT.png +0 -0
  63. data/app/assets/javascripts/dojo/src/html/images/shadowTL.png +0 -0
  64. data/app/assets/javascripts/dojo/src/html/images/shadowTR.png +0 -0
  65. data/app/assets/javascripts/dojo/src/widget/templates/Editor2/showtableborder_gecko.css +19 -0
  66. data/app/assets/javascripts/dojo/src/widget/templates/HslColorPicker.svg +30 -0
  67. data/app/assets/javascripts/dojo/src/widget/templates/buttons/aggregate.gif +0 -0
  68. data/app/assets/javascripts/dojo/src/widget/templates/buttons/aggregate.psd +0 -0
  69. data/app/assets/javascripts/dojo/src/widget/templates/buttons/backcolor.gif +0 -0
  70. data/app/assets/javascripts/dojo/src/widget/templates/buttons/bg-fade.png +0 -0
  71. data/app/assets/javascripts/dojo/src/widget/templates/buttons/bold.gif +0 -0
  72. data/app/assets/javascripts/dojo/src/widget/templates/buttons/cancel.gif +0 -0
  73. data/app/assets/javascripts/dojo/src/widget/templates/buttons/copy.gif +0 -0
  74. data/app/assets/javascripts/dojo/src/widget/templates/buttons/createlink.gif +0 -0
  75. data/app/assets/javascripts/dojo/src/widget/templates/buttons/cut.gif +0 -0
  76. data/app/assets/javascripts/dojo/src/widget/templates/buttons/delete.gif +0 -0
  77. data/app/assets/javascripts/dojo/src/widget/templates/buttons/forecolor.gif +0 -0
  78. data/app/assets/javascripts/dojo/src/widget/templates/buttons/hilitecolor.gif +0 -0
  79. data/app/assets/javascripts/dojo/src/widget/templates/buttons/indent.gif +0 -0
  80. data/app/assets/javascripts/dojo/src/widget/templates/buttons/inserthorizontalrule.gif +0 -0
  81. data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertimage.gif +0 -0
  82. data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertorderedlist.gif +0 -0
  83. data/app/assets/javascripts/dojo/src/widget/templates/buttons/inserttable.gif +0 -0
  84. data/app/assets/javascripts/dojo/src/widget/templates/buttons/insertunorderedlist.gif +0 -0
  85. data/app/assets/javascripts/dojo/src/widget/templates/buttons/italic.gif +0 -0
  86. data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifycenter.gif +0 -0
  87. data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyfull.gif +0 -0
  88. data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyleft.gif +0 -0
  89. data/app/assets/javascripts/dojo/src/widget/templates/buttons/justifyright.gif +0 -0
  90. data/app/assets/javascripts/dojo/src/widget/templates/buttons/left_to_right.gif +0 -0
  91. data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_bullet_indent.gif +0 -0
  92. data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_bullet_outdent.gif +0 -0
  93. data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_num_indent.gif +0 -0
  94. data/app/assets/javascripts/dojo/src/widget/templates/buttons/list_num_outdent.gif +0 -0
  95. data/app/assets/javascripts/dojo/src/widget/templates/buttons/outdent.gif +0 -0
  96. data/app/assets/javascripts/dojo/src/widget/templates/buttons/paste.gif +0 -0
  97. data/app/assets/javascripts/dojo/src/widget/templates/buttons/redo.gif +0 -0
  98. data/app/assets/javascripts/dojo/src/widget/templates/buttons/removeformat.gif +0 -0
  99. data/app/assets/javascripts/dojo/src/widget/templates/buttons/right_to_left.gif +0 -0
  100. data/app/assets/javascripts/dojo/src/widget/templates/buttons/save.gif +0 -0
  101. data/app/assets/javascripts/dojo/src/widget/templates/buttons/sep.gif +0 -0
  102. data/app/assets/javascripts/dojo/src/widget/templates/buttons/space.gif +0 -0
  103. data/app/assets/javascripts/dojo/src/widget/templates/buttons/strikethrough.gif +0 -0
  104. data/app/assets/javascripts/dojo/src/widget/templates/buttons/subscript.gif +0 -0
  105. data/app/assets/javascripts/dojo/src/widget/templates/buttons/superscript.gif +0 -0
  106. data/app/assets/javascripts/dojo/src/widget/templates/buttons/underline.gif +0 -0
  107. data/app/assets/javascripts/dojo/src/widget/templates/buttons/undo.gif +0 -0
  108. data/app/assets/javascripts/dojo/src/widget/templates/buttons/wikiword.gif +0 -0
  109. data/app/assets/javascripts/dojo/src/widget/templates/check.gif +0 -0
  110. data/app/assets/javascripts/dojo/src/widget/templates/decrementMonth.gif +0 -0
  111. data/app/assets/javascripts/dojo/src/widget/templates/decrementWeek.gif +0 -0
  112. data/app/assets/javascripts/dojo/src/widget/templates/grabCorner.gif +0 -0
  113. data/app/assets/javascripts/dojo/src/widget/templates/images/floatingPaneClose.gif +0 -0
  114. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaAccordionOff.gif +0 -0
  115. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaAccordionSelected.gif +0 -0
  116. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-c.gif +0 -0
  117. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-l.gif +0 -0
  118. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaActive-r.gif +0 -0
  119. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaBarBg.gif +0 -0
  120. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-c.gif +0 -0
  121. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-l.gif +0 -0
  122. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaButton-r.gif +0 -0
  123. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-c.gif +0 -0
  124. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-l.gif +0 -0
  125. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaDisabled-r.gif +0 -0
  126. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaMenuBg.gif +0 -0
  127. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-c.gif +0 -0
  128. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-l.gif +0 -0
  129. data/app/assets/javascripts/dojo/src/widget/templates/images/soriaPressed-r.gif +0 -0
  130. data/app/assets/javascripts/dojo/src/widget/templates/images/tab_close.gif +0 -0
  131. data/app/assets/javascripts/dojo/src/widget/templates/images/toolbar-bg.gif +0 -0
  132. data/app/assets/javascripts/dojo/src/widget/templates/incrementMonth.gif +0 -0
  133. data/app/assets/javascripts/dojo/src/widget/templates/incrementWeek.gif +0 -0
  134. data/app/assets/javascripts/dojo/src/widget/templates/richtextframe.html +24 -0
  135. data/app/assets/javascripts/imagine.js +1385 -0
  136. data/app/assets/javascripts/jquery_no_conflict.js +9405 -0
  137. data/app/assets/stylesheets/codepress/codepress.css +7 -0
  138. data/app/assets/stylesheets/codepress/languages/css.css +10 -0
  139. data/app/assets/stylesheets/codepress/languages/generic.css +9 -0
  140. data/app/assets/stylesheets/codepress/languages/html.css +18 -0
  141. data/app/assets/stylesheets/codepress/languages/java.css +7 -0
  142. data/app/assets/stylesheets/codepress/languages/javascript.css +8 -0
  143. data/app/assets/stylesheets/codepress/languages/perl.css +11 -0
  144. data/app/assets/stylesheets/codepress/languages/php.css +12 -0
  145. data/app/assets/stylesheets/codepress/languages/ruby.css +10 -0
  146. data/app/assets/stylesheets/codepress/languages/sql.css +10 -0
  147. data/app/assets/stylesheets/codepress/languages/text.css +5 -0
  148. data/app/assets/stylesheets/cropper.css +182 -0
  149. data/app/assets/stylesheets/management.css +96 -0
  150. data/app/assets/stylesheets/reset.css +58 -0
  151. data/app/controllers/cms/content_controller.rb +318 -2
  152. data/app/controllers/management/cms_controller.rb +1669 -0
  153. data/app/controllers/management/user_controller.rb +4 -4
  154. data/app/controllers/management/users_controller.rb +18 -4
  155. data/app/controllers/util_controller.rb +45 -0
  156. data/app/helpers/cms_application_helper.rb +662 -15
  157. data/app/models/cms_content_sweeper.rb +21 -0
  158. data/app/models/cms_page.rb +126 -0
  159. data/app/models/cms_page_object.rb +23 -0
  160. data/app/models/cms_page_tag.rb +5 -0
  161. data/app/models/cms_page_version.rb +3 -0
  162. data/app/models/cms_snippet.rb +16 -0
  163. data/app/models/cms_template.rb +29 -0
  164. data/app/models/user.rb +6 -3
  165. data/app/views/management/cms/_complete_gallery.html.erb +5 -0
  166. data/app/views/management/cms/_create_file_link.html.erb +21 -0
  167. data/app/views/management/cms/_crop_feature_image.html.erb +188 -0
  168. data/app/views/management/cms/_crop_image.html.erb +188 -0
  169. data/app/views/management/cms/_crop_results.html.erb +1 -0
  170. data/app/views/management/cms/_crop_results_feature_image.html.erb +1 -0
  171. data/app/views/management/cms/_crop_results_thumb.html.erb +1 -0
  172. data/app/views/management/cms/_crop_thumb.html.erb +188 -0
  173. data/app/views/management/cms/_dialogs.html.erb +39 -0
  174. data/app/views/management/cms/_edit_page.html.erb +176 -0
  175. data/app/views/management/cms/_gallery_index.html.erb +10 -0
  176. data/app/views/management/cms/_gallery_setup.html.erb +22 -0
  177. data/app/views/management/cms/_image.html.erb +3 -0
  178. data/app/views/management/cms/_image_details.html.erb +26 -0
  179. data/app/views/management/cms/_image_draggable.html.erb +4 -0
  180. data/app/views/management/cms/_list_page.html.erb +8 -0
  181. data/app/views/management/cms/_list_page_select.html.erb +8 -0
  182. data/app/views/management/cms/_list_pages.html.erb +1 -0
  183. data/app/views/management/cms/_list_pages_select.html.erb +1 -0
  184. data/app/views/management/cms/_page_attribute.html.erb +6 -0
  185. data/app/views/management/cms/_page_list.html.erb +171 -0
  186. data/app/views/management/cms/_page_list_source_folder.html.erb +20 -0
  187. data/app/views/management/cms/_page_list_source_tag.html.erb +18 -0
  188. data/app/views/management/cms/_select_gallery.html.erb +117 -0
  189. data/app/views/management/cms/_snippet.html.erb +3 -0
  190. data/app/views/management/cms/_sort_images.html.erb +15 -0
  191. data/app/views/management/cms/_temp.html.erb +3 -0
  192. data/app/views/management/cms/_template_options.html.erb +21 -0
  193. data/app/views/management/cms/_template_reference.html.erb +42 -0
  194. data/app/views/management/cms/_upload_feature_image.html.erb +35 -0
  195. data/app/views/management/cms/_upload_file.html.erb +31 -0
  196. data/app/views/management/cms/_upload_image.html.erb +74 -0
  197. data/app/views/management/cms/_upload_thumb.html.erb +35 -0
  198. data/app/views/management/cms/edit_master.html.erb +48 -0
  199. data/app/views/management/cms/edit_page_content.html.erb +4 -0
  200. data/app/views/management/cms/edit_snippet.html.erb +47 -0
  201. data/app/views/management/cms/edit_template.html.erb +48 -0
  202. data/app/views/management/cms/gallery_management.html.erb +108 -0
  203. data/app/views/management/cms/index.html.erb +20 -0
  204. data/app/views/management/cms/page_tags_for_lookup.html.erb +5 -0
  205. data/app/views/management/cms/pages.html.erb +99 -0
  206. data/app/views/management/cms/permission_denied.html.erb +1 -0
  207. data/app/views/management/cms/select_page.html.erb +57 -0
  208. data/app/views/management/cms/snippets.html.erb +14 -0
  209. data/app/views/management/cms/templates.html.erb +14 -0
  210. data/app/views/management/cms/toolbar_edit.html.erb +269 -0
  211. data/app/views/management/cms/toolbar_preview.html.erb +109 -0
  212. data/app/views/util/_calendar_days.html.erb +72 -0
  213. data/app/views/util/_calendar_month_year.html.erb +1 -0
  214. data/app/views/util/_date_picker.html.erb +56 -0
  215. data/app/views/util/_message.html.erb +1 -0
  216. data/app/views/util/_show_message.js.erb +6 -0
  217. data/app/views/util/_tab.html.erb +4 -0
  218. data/config/routes.rb +4 -1
  219. data/imagine_cms.gemspec +4 -0
  220. data/{vendor/gems → lib}/acts_as_versioned/.document +0 -0
  221. data/{vendor/gems → lib}/acts_as_versioned/.gitignore +2 -0
  222. data/{vendor/gems → lib}/acts_as_versioned/CHANGELOG +0 -0
  223. data/lib/acts_as_versioned/Gemfile +7 -0
  224. data/{vendor/gems → lib}/acts_as_versioned/MIT-LICENSE +0 -0
  225. data/{vendor/gems → lib}/acts_as_versioned/README +0 -0
  226. data/{vendor/gems → lib}/acts_as_versioned/RUNNING_UNIT_TESTS +0 -0
  227. data/{vendor/gems → lib}/acts_as_versioned/Rakefile +1 -1
  228. data/{vendor/gems → lib}/acts_as_versioned/acts_as_versioned.gemspec +4 -4
  229. data/{vendor/gems → lib}/acts_as_versioned/init.rb +0 -0
  230. data/{vendor/gems → lib}/acts_as_versioned/lib/acts_as_versioned.rb +109 -107
  231. data/{vendor/gems → lib}/acts_as_versioned/test/abstract_unit.rb +0 -0
  232. data/{vendor/gems → lib}/acts_as_versioned/test/database.yml +0 -0
  233. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/authors.yml +0 -0
  234. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmark.rb +0 -0
  235. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmark_versions.yml +0 -0
  236. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/landmarks.yml +0 -0
  237. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/locked_pages.yml +0 -0
  238. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/locked_pages_revisions.yml +0 -0
  239. 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
  240. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/page.rb +0 -0
  241. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/page_versions.yml +0 -0
  242. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/pages.yml +0 -0
  243. data/{vendor/gems → lib}/acts_as_versioned/test/fixtures/widget.rb +0 -0
  244. data/{vendor/gems → lib}/acts_as_versioned/test/migration_test.rb +0 -1
  245. data/{vendor/gems → lib}/acts_as_versioned/test/schema.rb +0 -0
  246. data/{vendor/gems → lib}/acts_as_versioned/test/versioned_test.rb +0 -0
  247. data/lib/extensions/action_controller.rb +154 -143
  248. data/lib/imagine_cms/engine.rb +33 -12
  249. data/lib/imagine_cms/version.rb +1 -1
  250. data/lib/imagine_cms.rb +30 -6
  251. data/lib/prototype_legacy_helper/README.markdown +13 -0
  252. data/lib/prototype_legacy_helper/init.rb +1 -0
  253. data/lib/prototype_legacy_helper/lib/prototype_legacy_helper.rb +430 -0
  254. data/lib/prototype_legacy_helper/test/test_prototype_helper.rb +297 -0
  255. data/lib/upload_progress/CHANGELOG +5 -0
  256. data/lib/upload_progress/MIT-LICENSE +20 -0
  257. data/lib/upload_progress/README +45 -0
  258. data/{vendor/gems/acts_as_tree → lib/upload_progress}/Rakefile +6 -5
  259. data/lib/upload_progress/init.rb +7 -0
  260. data/lib/upload_progress/lib/multipart_progress.rb +176 -0
  261. data/lib/upload_progress/lib/progress.rb +145 -0
  262. data/lib/upload_progress/lib/upload_progress.rb +303 -0
  263. data/lib/upload_progress/lib/upload_progress_helper.rb +425 -0
  264. data/lib/upload_progress/public/stylesheets/upload_progress.css +21 -0
  265. data/lib/upload_progress/test/multipart_progress_testx.rb +364 -0
  266. data/lib/upload_progress/test/upload_progress_helper_testx.rb +134 -0
  267. data/lib/upload_progress/test/upload_progress_testx.rb +88 -0
  268. metadata +305 -43
  269. data/app/assets/manage/btn_delete.gif +0 -0
  270. data/vendor/gems/.DS_Store +0 -0
  271. data/vendor/gems/acts_as_tree/README +0 -26
  272. data/vendor/gems/acts_as_tree/init.rb +0 -1
  273. data/vendor/gems/acts_as_tree/lib/active_record/acts/tree.rb +0 -96
  274. data/vendor/gems/acts_as_tree/test/acts_as_tree_test.rb +0 -219
  275. data/vendor/gems/acts_as_tree/test/fixtures/mixin.rb +0 -0
  276. data/vendor/gems/acts_as_tree/test/fixtures/mixins.yml +0 -0
  277. data/vendor/gems/acts_as_tree/test/schema.rb +0 -0
  278. data/vendor/gems/acts_as_versioned/Gemfile +0 -7
@@ -0,0 +1,425 @@
1
+ module UploadProgress
2
+ # Provides a set of methods to be used in your views to help with the
3
+ # rendering of Ajax enabled status updating during a multipart upload.
4
+ #
5
+ # The basic mechanism for upload progress is that the form will post to a
6
+ # hidden <iframe> element, then poll a status action that will replace the
7
+ # contents of a status container. Client Javascript hooks are provided for
8
+ # +begin+ and +finish+ of the update.
9
+ #
10
+ # If you wish to have a DTD that will validate this page, use XHTML
11
+ # Transitional because this DTD supports the <iframe> element.
12
+ #
13
+ # == Typical usage
14
+ #
15
+ # In your upload view:
16
+ #
17
+ # <% form_tag_with_upload_progress({ :action => 'create' }) do %>
18
+ # <%= file_field "document", "file" %>
19
+ # <%= submit_tag "Upload" %>
20
+ # <%= upload_status_tag %>
21
+ # <% end %>
22
+ #
23
+ # In your controller:
24
+ #
25
+ # class DocumentController < ApplicationController
26
+ # upload_status_for :create
27
+ #
28
+ # def create
29
+ # # ... Your document creation action
30
+ # end
31
+ # end
32
+ #
33
+ # == Javascript callback on begin and finished
34
+ #
35
+ # In your upload view:
36
+ #
37
+ # <% form_tag_with_upload_progress({ :action => 'create' }, {
38
+ # :begin => "alert('upload beginning')",
39
+ # :finish => "alert('upload finished')" }) do %>
40
+ # <%= file_field "document", "file" %>
41
+ # <%= submit_tag "Upload" %>
42
+ # <%= upload_status_tag %>
43
+ # <% end %>
44
+ #
45
+ #
46
+ # == CSS Styling of the status text and progress bar
47
+ #
48
+ # See +upload_status_text_tag+ and +upload_status_progress_bar_tag+ for references
49
+ # of the IDs and CSS classes used.
50
+ #
51
+ # Default styling is included with the scaffolding CSS.
52
+ module UploadProgressHelper
53
+ unless const_defined? :FREQUENCY
54
+ # Default number of seconds between client updates
55
+ FREQUENCY = 2.0
56
+
57
+ # Factor to decrease the frequency when the +upload_status+ action returns the same results
58
+ # To disable update decay, set this constant to +false+
59
+ FREQUENCY_DECAY = 1.8
60
+ end
61
+
62
+ # Contains a hash of status messages used for localization of
63
+ # +upload_progress_status+ and +upload_progress_text+. Each string is
64
+ # evaluated in the helper method context so you can include your own
65
+ # calculations and string iterpolations.
66
+ #
67
+ # The following keys are defined:
68
+ #
69
+ # <tt>:begin</tt>:: Displayed before the first byte is received on the server
70
+ # <tt>:update</tt>:: Contains a human representation of the upload progress
71
+ # <tt>:finish</tt>:: Displayed when the file upload is complete, before the action has completed. If you are performing extra activity in your action such as processing of the upload, then inform the user of what you are doing by setting +upload_progress.message+
72
+ #
73
+ @@default_messages = {
74
+ :begin => '"Upload starting..."',
75
+ :update => '"#{human_size(upload_progress.received_bytes)} of #{human_size(upload_progress.total_bytes)} at #{human_size(upload_progress.bitrate)}/s; #{distance_of_time_in_words(0,upload_progress.remaining_seconds,true)} remaining"',
76
+ :finish => 'upload_progress.message.blank? ? "Upload finished." : upload_progress.message',
77
+ }
78
+
79
+
80
+ # Creates a form tag and hidden <iframe> necessary for the upload progress
81
+ # status messages to be displayed in a designated +div+ on your page.
82
+ #
83
+ # == Initializations
84
+ #
85
+ # When the upload starts, the content created by +upload_status_tag+ will be filled out with
86
+ # "Upload starting...". When the upload is finished, "Upload finished." will be used. Every
87
+ # update inbetween the begin and finish events will be determined by the server +upload_status+
88
+ # action. Doing this automatically means that the user can use the same form to upload multiple
89
+ # files without refreshing page while still displaying a reasonable progress.
90
+ #
91
+ # == Upload IDs
92
+ #
93
+ # For the view and the controller to know about the same upload they must share
94
+ # a common +upload_id+. +form_tag_with_upload_progress+ prepares the next available
95
+ # +upload_id+ when called. There are other methods which use the +upload_id+ so the
96
+ # order in which you include your content is important. Any content that depends on the
97
+ # +upload_id+ will use the one defined +form_tag_with_upload_progress+
98
+ # otherwise you will need to explicitly declare the +upload_id+ shared among
99
+ # your progress elements.
100
+ #
101
+ # Status container after the form:
102
+ #
103
+ # <% form_tag_with_upload_progress do %>
104
+ # <% end %>
105
+ #
106
+ # <%= upload_status_tag %>
107
+ #
108
+ # Status container before form:
109
+ #
110
+ # <% my_upload_id = next_upload_id %>
111
+ #
112
+ # <%= upload_status_tag %>
113
+ #
114
+ # <% form_tag_with_upload_progress :upload_id => my_upload_id do %>
115
+ # <% end %>
116
+ #
117
+ # It is recommended that the helpers manage the +upload_id+ parameter.
118
+ #
119
+ # == Options
120
+ #
121
+ # +form_tag_with_upload_progress+ uses similar options as +form_tag+
122
+ # yet accepts another hash for the options used for the +upload_status+ action.
123
+ #
124
+ # <tt>url_for_options</tt>:: The same options used by +form_tag+ including:
125
+ # <tt>:upload_id</tt>:: the upload id used to uniquely identify this upload
126
+ #
127
+ # <tt>options</tt>:: similar options to +form_tag+ including:
128
+ # <tt>:begin</tt>:: Javascript code that executes before the first status update occurs.
129
+ # <tt>:finish</tt>:: Javascript code that executes after the action that receives the post returns.
130
+ # <tt>:frequency</tt>:: number of seconds between polls to the upload status action.
131
+ #
132
+ # <tt>status_url_for_options</tt>:: options passed to +url_for+ to build the url
133
+ # for the upload status action.
134
+ # <tt>:controller</tt>:: defines the controller to be used for a custom update status action
135
+ # <tt>:action</tt>:: defines the action to be used for a custom update status action
136
+ #
137
+ # Parameters passed to the action defined by status_url_for_options
138
+ #
139
+ # <tt>:upload_id</tt>:: the upload_id automatically generated by +form_tag_with_upload_progress+ or the user defined id passed to this method.
140
+ #
141
+ def form_tag_with_upload_progress(url_for_options = {}, options = {}, status_url_for_options = {}, *parameters_for_url_method, &block)
142
+
143
+ ## Setup the action URL and the server-side upload_status action for
144
+ ## polling of status during the upload
145
+
146
+ options = options.dup
147
+
148
+ upload_id = url_for_options.delete(:upload_id) || next_upload_id
149
+ upload_action_url = url_for(url_for_options)
150
+
151
+ if status_url_for_options.is_a? Hash
152
+ status_url_for_options = status_url_for_options.merge({
153
+ :action => 'upload_status',
154
+ :upload_id => upload_id})
155
+ end
156
+
157
+ status_url = url_for(status_url_for_options)
158
+
159
+ ## Prepare the form options. Dynamically change the target and URL to enable the status
160
+ ## updating only if javascript is enabled, otherwise perform the form submission in the same
161
+ ## frame.
162
+
163
+ upload_target = options[:target] || upload_target_id
164
+ upload_id_param = "#{upload_action_url.include?('?') ? '&' : '?'}upload_id=#{upload_id}"
165
+
166
+ ## Externally :begin and :finish are the entry and exit points
167
+ ## Internally, :finish is called :complete
168
+
169
+ js_options = {
170
+ :decay => options[:decay] || FREQUENCY_DECAY,
171
+ :frequency => options[:frequency] || FREQUENCY,
172
+ }
173
+
174
+ updater_options = '{' + js_options.map {|k, v| "#{k}:#{v}"}.sort.join(',') + '}'
175
+
176
+ ## Finish off the updating by forcing the progress bar to 100% and status text because the
177
+ ## results of the post may load and finish in the IFRAME before the last status update
178
+ ## is loaded.
179
+
180
+ options[:complete] = "$('#{status_tag_id}').innerHTML='#{escape_javascript upload_progress_text(:finish)}';"
181
+ options[:complete] << "#{upload_progress_update_bar_js(100)};"
182
+ options[:complete] << "#{upload_update_object} = null"
183
+ options[:complete] = "#{options[:complete]}; #{options[:finish]}".html_safe if options[:finish]
184
+
185
+ options[:script] = true
186
+
187
+ ## Prepare the periodic updater, clearing any previous updater
188
+
189
+ updater = "if (#{upload_update_object}) { #{upload_update_object}.stop(); }"
190
+ updater << "#{upload_update_object} = new Ajax.PeriodicalUpdater('#{status_tag_id}',"
191
+ updater << "'#{status_url}', Object.extend(#{options_for_ajax(options)},#{updater_options}))"
192
+
193
+ updater = "#{options[:begin]}; #{updater}" if options[:begin]
194
+ updater = "#{upload_progress_update_bar_js(0)}; #{updater}"
195
+ updater = "$('#{status_tag_id}').innerHTML='#{escape_javascript upload_progress_text(:begin)}'; #{updater}"
196
+
197
+ ## Touch up the form action and target to use the given target instead of the
198
+ ## default one. Then start the updater
199
+
200
+ options[:onsubmit] = "if (this.action.indexOf('upload_id') < 0){ this.action += '#{escape_javascript upload_id_param}'; }"
201
+ options[:onsubmit] << "this.target = '#{escape_javascript upload_target}';"
202
+ options[:onsubmit] << "#{updater}; return true"
203
+ options[:onsubmit] = options[:onsubmit].html_safe
204
+ options[:multipart] = true
205
+
206
+ [:begin, :finish, :complete, :frequency, :decay, :script].each { |sym| options.delete(sym) }
207
+
208
+ ## Create the tags
209
+ ## If a target for the form is given then avoid creating the hidden IFRAME
210
+
211
+ tag = form_tag(upload_action_url, options, *parameters_for_url_method, &block)
212
+
213
+ unless options[:target]
214
+ tag << content_tag('iframe', '', {
215
+ :id => upload_target,
216
+ :name => upload_target,
217
+ :src => '',
218
+ :style => 'width:0px;height:0px;border:0'
219
+ })
220
+ end
221
+
222
+ tag
223
+ end
224
+
225
+ # This method must be called by the action that receives the form post
226
+ # with the +upload_progress+. By default this method is rendered when
227
+ # the controller declares that the action is the receiver of a
228
+ # +form_tag_with_upload_progress+ posting.
229
+ #
230
+ # This template will do a javascript redirect to the URL specified in +redirect_to+
231
+ # if this method is called anywhere in the controller action. When the action
232
+ # performs a redirect, the +finish+ handler will not be called.
233
+ #
234
+ # If there are errors in the action then you should set the controller
235
+ # instance variable +@errors+. The +@errors+ object will be
236
+ # converted to a javascript array from +@errors.full_messages+ and
237
+ # passed to the +finish+ handler of +form_tag_with_upload_progress+
238
+ #
239
+ # If no errors have occured, the parameter to the +finish+ handler will
240
+ # be +undefined+.
241
+ #
242
+ # == Example (in view)
243
+ #
244
+ # <script>
245
+ # function do_finish(errors) {
246
+ # if (errors) {
247
+ # alert(errors);
248
+ # }
249
+ # }
250
+ # </script>
251
+ #
252
+ # <%= form_tag_with_upload_progress {:action => 'create'}, {finish => 'do_finish(arguments[0])'} %>
253
+ #
254
+ def finish_upload_status(options = {})
255
+ # Always trigger the stop/finish callback
256
+ js = "parent.#{upload_update_object}.stop(#{options[:client_js_argument]});\n"
257
+
258
+ # Redirect if redirect_to was called in controller
259
+ js << "parent.location.replace('#{escape_javascript options[:redirect_to]}');\n" unless options[:redirect_to].blank?
260
+
261
+ # Guard against multiple triggers/redirects on back
262
+ js = "if (parent.#{upload_update_object}) { #{js} }\n".html_safe
263
+
264
+ content_tag("html",
265
+ content_tag("head",
266
+ content_tag("script", "\nfunction finish() { #{js} }".html_safe,
267
+ {:type => "text/javascript", :language => "javascript"})) +
268
+ content_tag("body", '', :onload => 'finish()'))
269
+ end
270
+
271
+ # Renders the HTML to contain the upload progress bar above the
272
+ # default messages
273
+ #
274
+ # Use this method to display the upload status after your +form_tag_with_upload_progress+
275
+ def upload_status_tag(content='', options={})
276
+ upload_status_progress_bar_tag + upload_status_text_tag(content, options)
277
+ end
278
+
279
+ # Content helper that will create a +div+ with the proper ID and class that
280
+ # will contain the contents returned by the +upload_status+ action. The container
281
+ # is defined as
282
+ #
283
+ # <div id="#{status_tag_id}" class="uploadStatus"> </div>
284
+ #
285
+ # Style this container by selecting the +.uploadStatus+ +CSS+ class.
286
+ #
287
+ # The +content+ parameter will be included in the inner most +div+ when
288
+ # rendered.
289
+ #
290
+ # The +options+ will create attributes on the outer most div. To use a different
291
+ # +CSS+ class, pass a different class option.
292
+ #
293
+ # Example +CSS+:
294
+ # .uploadStatus { font-size: 10px; color: grey; }
295
+ #
296
+ def upload_status_text_tag(content=nil, options={})
297
+ content_tag("div", content, {:id => status_tag_id, :class => 'uploadStatus'}.merge(options))
298
+ end
299
+
300
+ # Content helper that will create the element tree that can be easily styled
301
+ # with +CSS+ to create a progress bar effect. The containers are defined as:
302
+ #
303
+ # <div class="progressBar" id="#{progress_bar_id}">
304
+ # <div class="border">
305
+ # <div class="background">
306
+ # <div class="content"> </div>
307
+ # </div>
308
+ # </div>
309
+ # </div>
310
+ #
311
+ # The +content+ parameter will be included in the inner most +div+ when
312
+ # rendered.
313
+ #
314
+ # The +options+ will create attributes on the outer most div. To use a different
315
+ # +CSS+ class, pass a different class option.
316
+ #
317
+ # Example:
318
+ # <%= upload_status_progress_bar_tag('', {:class => 'progress'}) %>
319
+ #
320
+ # Example +CSS+:
321
+ #
322
+ # div.progressBar {
323
+ # margin: 5px;
324
+ # }
325
+ #
326
+ # div.progressBar div.border {
327
+ # background-color: #fff;
328
+ # border: 1px solid grey;
329
+ # width: 100%;
330
+ # }
331
+ #
332
+ # div.progressBar div.background {
333
+ # background-color: #333;
334
+ # height: 18px;
335
+ # width: 0%;
336
+ # }
337
+ #
338
+ def upload_status_progress_bar_tag(content='', options={})
339
+ css = [options[:class], 'progressBar'].compact.join(' ')
340
+
341
+ content_tag("div",
342
+ content_tag("div",
343
+ content_tag("div",
344
+ content_tag("div", content, :class => 'foreground'),
345
+ :class => 'background'),
346
+ :class => 'border'),
347
+ {:id => progress_bar_id}.merge(options).merge({:class => css}))
348
+ end
349
+
350
+ # The text and Javascript returned by the default +upload_status+ controller
351
+ # action which will replace the contents of the div created by +upload_status_text_tag+
352
+ # and grow the progress bar background to the appropriate width.
353
+ #
354
+ # See +upload_progress_text+ and +upload_progress_update_bar_js+
355
+ def upload_progress_status
356
+ "#{upload_progress_text}<script>#{upload_progress_update_bar_js}</script>".html_safe
357
+ end
358
+
359
+ # Javascript helper that will create a script that will change the width
360
+ # of the background progress bar container. Include this in the script
361
+ # portion of your view rendered by your +upload_status+ action to
362
+ # automatically find and update the progress bar.
363
+ #
364
+ # Example (in controller):
365
+ #
366
+ # def upload_status
367
+ # render :inline => "<script><%= update_upload_progress_bar_js %></script>", :layout => false
368
+ # end
369
+ #
370
+ #
371
+ def upload_progress_update_bar_js(percent=nil)
372
+ progress = upload_progress
373
+ percent ||= case
374
+ when progress.nil? || !progress.started? then 0
375
+ when progress.finished? then 100
376
+ else progress.completed_percent
377
+ end.to_i
378
+
379
+ # TODO do animation instead of jumping
380
+ "if($('#{progress_bar_id}')){$('#{progress_bar_id}').firstChild.firstChild.style.width='#{percent}%'}"
381
+ end
382
+
383
+ # Generates a nicely formatted string of current upload progress for
384
+ # +UploadProgress::Progress+ object +progress+. Addtionally, it
385
+ # will return "Upload starting..." if progress has not been initialized,
386
+ # "Receiving data..." if there is no received data yet, and "Upload
387
+ # finished" when all data has been sent.
388
+ #
389
+ # You can overload this method to add you own output to the
390
+ #
391
+ # Example return: 223.5 KB of 1.5 MB at 321.2 KB/s; less than 10 seconds
392
+ # remaining
393
+ def upload_progress_text(state=nil)
394
+ eval case
395
+ when state then @@default_messages[state.to_sym]
396
+ when upload_progress.nil? || !upload_progress.started? then @@default_messages[:begin]
397
+ when upload_progress.finished? then @@default_messages[:finish]
398
+ else @@default_messages[:update]
399
+ end
400
+ end
401
+
402
+ protected
403
+ # Javascript object used to contain the polling methods and keep track of
404
+ # the finished state
405
+ def upload_update_object
406
+ "document.uploadStatus#{current_upload_id}"
407
+ end
408
+
409
+ # Element ID of the progress bar
410
+ def progress_bar_id
411
+ "UploadProgressBar#{current_upload_id}"
412
+ end
413
+
414
+ # Element ID of the progress status container
415
+ def status_tag_id
416
+ "UploadStatus#{current_upload_id}"
417
+ end
418
+
419
+ # Element ID of the target <iframe> used as the target of the form
420
+ def upload_target_id
421
+ "UploadTarget#{current_upload_id}"
422
+ end
423
+
424
+ end
425
+ end
@@ -0,0 +1,21 @@
1
+ div.uploadStatus {
2
+ margin: 5px;
3
+ }
4
+
5
+ div.progressBar {
6
+ margin: 5px;
7
+ }
8
+
9
+ div.progressBar div.border {
10
+ background-color: #fff;
11
+ border: 1px solid grey;
12
+ width: 100%;
13
+ }
14
+
15
+ div.progressBar div.background {
16
+ background-color: #333;
17
+ height: 18px;
18
+ width: 0%;
19
+ }
20
+
21
+