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,145 @@
1
+ module UploadProgress #:nodoc:
2
+ # Upload Progress abstracts the progress of an upload. It's used by the
3
+ # multipart progress IO that keeps track of the upload progress and creating
4
+ # the application depends on. It contians methods to update the progress
5
+ # during an upload and read the statistics such as +received_bytes+,
6
+ # +total_bytes+, +completed_percent+, +bitrate+, and
7
+ # +remaining_seconds+
8
+ #
9
+ # You can get the current +Progress+ object by calling +upload_progress+ instance
10
+ # method in your controller or view.
11
+ #
12
+ class Progress
13
+ unless const_defined? :MIN_SAMPLE_TIME
14
+ # Number of seconds between bitrate samples. Updates that occur more
15
+ # frequently than +MIN_SAMPLE_TIME+ will not be queued until this
16
+ # time passes. This behavior gives a good balance of accuracy and load
17
+ # for both fast and slow transfers.
18
+ MIN_SAMPLE_TIME = 0.150
19
+
20
+ # Number of seconds between updates before giving up to try and calculate
21
+ # bitrate anymore
22
+ MIN_STALL_TIME = 10.0
23
+
24
+ # Number of samples used to calculate bitrate
25
+ MAX_SAMPLES = 20
26
+ end
27
+
28
+ # Number bytes received from the multipart post
29
+ attr_reader :received_bytes
30
+
31
+ # Total number of bytes expected from the mutlipart post
32
+ attr_reader :total_bytes
33
+
34
+ # The last time the upload history was updated
35
+ attr_reader :last_update_time
36
+
37
+ # A message you can set from your controller or view to be rendered in the
38
+ # +upload_status_text+ helper method. If you set a messagein a controller
39
+ # then call <code>session.update</code> to make that message available to
40
+ # your +upload_status+ action.
41
+ attr_accessor :message
42
+
43
+ # Create a new Progress object passing the expected number of bytes to receive
44
+ def initialize(total)
45
+ @total_bytes = total
46
+ reset!
47
+ end
48
+
49
+ # Resets the received_bytes, last_update_time, message and bitrate, but
50
+ # but maintains the total expected bytes
51
+ def reset!
52
+ @received_bytes, @last_update_time, @stalled, @message = 0, 0, false, ''
53
+ reset_history
54
+ end
55
+
56
+ # Number of bytes left for this upload
57
+ def remaining_bytes
58
+ @total_bytes - @received_bytes
59
+ end
60
+
61
+ # Completed percent in integer form from 0..100
62
+ def completed_percent
63
+ (@received_bytes * 100 / @total_bytes).to_i rescue 0
64
+ end
65
+
66
+ # Updates this UploadProgress object with the number of bytes received
67
+ # since last update time and the absolute number of seconds since the
68
+ # beginning of the upload.
69
+ #
70
+ # This method is used by the +MultipartProgress+ module and should
71
+ # not be called directly.
72
+ def update!(bytes, elapsed_seconds)#:nodoc:
73
+ if @received_bytes + bytes > @total_bytes
74
+ #warn "Progress#update received bytes exceeds expected bytes"
75
+ bytes = @total_bytes - @received_bytes
76
+ end
77
+
78
+ @received_bytes += bytes
79
+
80
+ # Age is the duration of time since the last update to the history
81
+ age = elapsed_seconds - @last_update_time
82
+
83
+ # Record the bytes received in the first element of the history
84
+ # in case the sample rate is exceeded and we shouldn't record at this
85
+ # time
86
+ @history.first[0] += bytes
87
+ @history.first[1] += age
88
+
89
+ history_age = @history.first[1]
90
+
91
+ @history.pop while @history.size > MAX_SAMPLES
92
+ @history.unshift([0,0]) if history_age > MIN_SAMPLE_TIME
93
+
94
+ if history_age > MIN_STALL_TIME
95
+ @stalled = true
96
+ reset_history
97
+ else
98
+ @stalled = false
99
+ end
100
+
101
+ @last_update_time = elapsed_seconds
102
+
103
+ self
104
+ end
105
+
106
+ # Calculates the bitrate in bytes/second. If the transfer is stalled or
107
+ # just started, the bitrate will be 0
108
+ def bitrate
109
+ history_bytes, history_time = @history.transpose.map { |vals| vals.inject { |sum, v| sum + v } }
110
+ history_bytes / history_time rescue 0
111
+ end
112
+
113
+ # Number of seconds elapsed since the start of the upload
114
+ def elapsed_seconds
115
+ @last_update_time
116
+ end
117
+
118
+ # Calculate the seconds remaining based on the current bitrate. Returns
119
+ # O seconds if stalled or if no bytes have been received
120
+ def remaining_seconds
121
+ remaining_bytes / bitrate rescue 0
122
+ end
123
+
124
+ # Returns true if there are bytes pending otherwise returns false
125
+ def finished?
126
+ remaining_bytes <= 0
127
+ end
128
+
129
+ # Returns true if some bytes have been received
130
+ def started?
131
+ @received_bytes > 0
132
+ end
133
+
134
+ # Returns true if there has been a delay in receiving bytes. The delay
135
+ # is set by the constant MIN_STALL_TIME
136
+ def stalled?
137
+ @stalled
138
+ end
139
+
140
+ private
141
+ def reset_history
142
+ @history = [[0,0]]
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,303 @@
1
+ module UploadProgress
2
+ def self.append_features(base) #:nodoc:
3
+ super
4
+ base.extend(ClassMethods)
5
+ base.helper_method :upload_progress, :next_upload_id, :last_upload_id, :current_upload_id
6
+ end
7
+
8
+ # == Action Pack Upload Progress for multipart uploads
9
+ #
10
+ # The UploadProgress module aids in the process of viewing an Ajax driven
11
+ # upload status when working with multipart forms. It offers a macro that
12
+ # will prepare an action for handling the cleanup of the Ajax updating including
13
+ # passing the redirect URL and custom parameters to the Javascript finish handler.
14
+ #
15
+ # UploadProgress is available for all multipart uploads when the +upload_status_for+
16
+ # macro is called in one of your controllers.
17
+ #
18
+ # The progress is stored as an UploadProgress::Progress object in the session and
19
+ # is accessible in the controller and view with the +upload_progress+ method.
20
+ #
21
+ # For help rendering the UploadProgress enabled form and supported elements, see
22
+ # ActionView::Helpers::UploadProgressHelper.
23
+ #
24
+ # === Automatic updating on upload actions
25
+ #
26
+ # class DocumentController < ApplicationController
27
+ # upload_status_for :create
28
+ #
29
+ # def create
30
+ # # ... Your document creation action
31
+ # end
32
+ # end
33
+ #
34
+ # The +upload_status_for+ macro will override the rendering of the action passed
35
+ # if +upload_id+ is found in the query string. This allows for default
36
+ # behavior if Javascript is disabled. If you are tracking the upload progress
37
+ # then +create+ will now return the cleanup scripts that will terminate the polling
38
+ # of the upload status.
39
+ #
40
+ # === Customized status rendering
41
+ #
42
+ # class DocumentController < ApplicationController
43
+ # upload_status_for :create, :status => :custom_status
44
+ #
45
+ # def create
46
+ # # ... Your document creation action
47
+ # end
48
+ #
49
+ # def custom_status
50
+ # # ... Override this action to return content to be replaced in
51
+ # # the status container
52
+ # render :inline => "<%= upload_progress.completed_percent rescue 0 %> % complete", :layout => false
53
+ # end
54
+ #
55
+ # The default status action is +upload_status+. The results of this action
56
+ # are added used to replace the contents of the HTML elements defined in
57
+ # +upload_status_tag+. Within +upload_status+, you can load the Progress
58
+ # object from the session with the +upload_progress+ method and display your own
59
+ # results.
60
+ #
61
+ # Completion of the upload status updating occurs automatically with an +after_filter+ call to
62
+ # +finish_upload_status+. Because the upload must be posted into a hidden IFRAME to enable
63
+ # Ajax updates during the upload, +finish_upload_status+ overwrites the results of any previous
64
+ # +render+ or +redirect_to+ so it can render the necessary Javascript that will properly terminate
65
+ # the status updating loop, trigger the completion callback or redirect to the appropriate URL.
66
+ #
67
+ # ==== Basic Example (View):
68
+ #
69
+ # <%= form_tag_with_upload_progress({:action => 'create'}, {:finish => 'alert("Document Uploaded")'}) %>
70
+ # <%= upload_status_tag %>
71
+ # <%= file_field 'document', 'file' %>
72
+ # <%= end_form_tag %>
73
+ #
74
+ # ==== Basic Example (Controller):
75
+ #
76
+ # class DocumentController < ApplicationController
77
+ # upload_status_for :create
78
+ #
79
+ # def create
80
+ # @document = Document.create(params[:document])
81
+ # end
82
+ # end
83
+ #
84
+ # ==== Extended Example (View):
85
+ #
86
+ # <%= form_tag_with_upload_progress({:action => 'create'}, {}, {:action => :custom_status}) %>
87
+ # <%= upload_status_tag %>
88
+ # <%= file_field 'document', 'file' %>
89
+ # <%= submit_tag "Upload" %>
90
+ # <%= end_form_tag %>
91
+ #
92
+ # <%= form_tag_with_upload_progress({:action => 'add_preview'}, {:finish => 'alert(arguments[0])'}, {:action => :custom_status}) %>
93
+ # <%= upload_status_tag %>
94
+ # <%= submit_tag "Upload" %>
95
+ # <%= file_field 'preview', 'file' %>
96
+ # <%= end_form_tag %>
97
+ #
98
+ # ==== Extended Example (Controller):
99
+ #
100
+ # class DocumentController < ApplicationController
101
+ # upload_status_for :add_preview, :create, {:status => :custom_status}
102
+ #
103
+ # def add_preview
104
+ # @document = Document.find(params[:id])
105
+ # @document.preview = Preview.create(params[:preview])
106
+ # if @document.save
107
+ # finish_upload_status "'Preview added'"
108
+ # else
109
+ # finish_upload_status "'Preview not added'"
110
+ # end
111
+ # end
112
+ #
113
+ # def create
114
+ # @document = Document.new(params[:document])
115
+ #
116
+ # upload_progress.message = "Processing document..."
117
+ # session.update
118
+ #
119
+ # @document.save
120
+ # redirect_to :action => 'show', :id => @document.id
121
+ # end
122
+ #
123
+ # def custom_status
124
+ # render :inline => '<%= upload_progress_status %> <div>Updated at <%= Time.now %></div>', :layout => false
125
+ # end
126
+ #
127
+ # ==== Environment checklist
128
+ #
129
+ # This is an experimental feature that requires a specific webserver environment. Use the following checklist
130
+ # to confirm that you have an environment that supports upload progress.
131
+ #
132
+ # ===== Ruby:
133
+ #
134
+ # * Running the command `ruby -v` should print "ruby 1.8.2 (2004-12-25)" or older
135
+ #
136
+ # ===== Web server:
137
+ #
138
+ # * Apache 1.3, Apache 2.0 or Lighttpd *1.4* (need to build lighttpd from CVS)
139
+ #
140
+ # ===== FastCGI bindings:
141
+ #
142
+ # * > 0.8.6 and must be the compiled C version of the bindings
143
+ # * The command `ruby -e "p require('fcgi.so')"` should print "true"
144
+ #
145
+ # ===== Apache/Lighttpd FastCGI directives:
146
+ #
147
+ # * You must allow more than one FCGI server process to allow concurrent requests.
148
+ # * If there is only a single FCGI process you will not get the upload status updates.
149
+ # * You can check this by taking a look for running FCGI servers in your process list during a progress upload.
150
+ # * Apache directive: FastCGIConfig -minProcesses 2
151
+ # * Lighttpd directives taken from config/lighttpd.conf (min-procs):
152
+ #
153
+ # fastcgi.server = (
154
+ # ".fcgi" => (
155
+ # "APP_NAME" => (
156
+ # "socket" => "/tmp/APP_NAME1.socket",
157
+ # "bin-path" => "Rails.root/public/dispatch.fcgi",
158
+ # "min-procs" => 2
159
+ # )
160
+ # )
161
+ # )
162
+ #
163
+ # ===== config/environment.rb:
164
+ #
165
+ # * Add the following line to your config/environment.rb and restart your web server.
166
+ # * <tt>ActionController::Base.enable_upload_progress</tt>
167
+ #
168
+ # ===== Development log:
169
+ #
170
+ # * When the upload progress is enabled by you will find something the following lines:
171
+ # * "Multipart upload with progress (id: 1, size: 85464)"
172
+ # * "Finished processing multipart upload in 0.363729s"
173
+ # * If you are properly running multiple FCGI processes, then you will see multiple entries for rendering the "upload_status" action before the "Finish processing..." log entry. This is a *good thing* :)
174
+ #
175
+ module ClassMethods
176
+ # Creates an +after_filter+ which will call +finish_upload_status+
177
+ # creating the document that will be loaded into the hidden IFRAME, terminating
178
+ # the status polling forms created with +form_with_upload_progress+.
179
+ #
180
+ # Also defines an action +upload_status+ or a action name passed as
181
+ # the <tt>:status</tt> option. This status action must match the one expected
182
+ # in the +form_tag_with_upload_progress+ helper.
183
+ #
184
+ def upload_status_for(*actions)
185
+ after_filter :finish_upload_status, :only => actions
186
+
187
+ define_method(actions.last.is_a?(Hash) && actions.last[:status] || :upload_status) do
188
+ render(:inline => '<%= upload_progress_status %>', :layout => false)
189
+ end
190
+ end
191
+ end
192
+
193
+ # Overwrites the body rendered if the upload comes from a form that tracks
194
+ # the progress of the upload. After clearing the body and any redirects, this
195
+ # method then renders the helper +finish_upload_status+
196
+ #
197
+ # This method only needs to be called if you wish to pass a
198
+ # javascript parameter to your finish event handler that you optionally
199
+ # define in +form_with_upload_progress+
200
+ #
201
+ # === Parameter:
202
+ #
203
+ # client_js_argument:: a string containing a Javascript expression that will
204
+ # be evaluated and passed to your +finish+ handler of
205
+ # +form_tag_with_upload_progress+.
206
+ #
207
+ # You can pass a String, Number or Boolean.
208
+ #
209
+ # === Strings
210
+ #
211
+ # Strings contain Javascript code that will be evaluated on the client. If you
212
+ # wish to pass a string to the client finish callback, you will need to include
213
+ # quotes in the +client_js_argument+ you pass to this method.
214
+ #
215
+ # ==== Example
216
+ #
217
+ # finish_upload_status("\"Finished\"")
218
+ # finish_upload_status("'Finished #{@document.title}'")
219
+ # finish_upload_status("{success: true, message: 'Done!'}")
220
+ # finish_upload_status("function() { alert('Uploaded!'); }")
221
+ #
222
+ # === Numbers / Booleans
223
+ #
224
+ # Numbers and Booleans can either be passed as Number objects or string versions
225
+ # of number objects as they are evaluated by Javascript the same way as in Ruby.
226
+ #
227
+ # ==== Example
228
+ #
229
+ # finish_upload_status(0)
230
+ # finish_upload_status(@document.file.size)
231
+ # finish_upload_status("10")
232
+ #
233
+ # === Nil
234
+ #
235
+ # To pass +nil+ to the finish callback, use a string "undefined"
236
+ #
237
+ # ==== Example
238
+ #
239
+ # finish_upload_status(@message || "undefined")
240
+ #
241
+ # == Redirection
242
+ #
243
+ # If you action performs a redirection then +finish_upload_status+ will recognize
244
+ # the redirection and properly create the Javascript to perform the redirection in
245
+ # the proper location.
246
+ #
247
+ # It is possible to redirect and pass a parameter to the finish callback.
248
+ #
249
+ # ==== Example
250
+ #
251
+ # redirect_to :action => 'show', :id => @document.id
252
+ # finish_upload_status("'Redirecting you to your new file'")
253
+ #
254
+ #
255
+ def finish_upload_status(client_js_argument='')
256
+ if not @rendered_finish_upload_status and params[:upload_id]
257
+ @rendered_finish_upload_status = true
258
+
259
+ # erase_render_results
260
+ # location = erase_redirect_results || ''
261
+
262
+ ## TODO determine if #inspect is the appropriate way to marshall values
263
+ ## in inline templates
264
+
265
+ template = "<%= finish_upload_status({"
266
+ template << ":client_js_argument => #{client_js_argument.inspect}, "
267
+ template << ":redirect_to => #{location.to_s.inspect}, "
268
+ template << "}) %>"
269
+
270
+ render({ :inline => template, :layout => false })
271
+ end
272
+ end
273
+
274
+ # Returns and saves the next unique +upload_id+ in the instance variable
275
+ # <tt>@upload_id</tt>
276
+ def next_upload_id
277
+ @upload_id = last_upload_id.succ
278
+ end
279
+
280
+ # Either returns the last saved +upload_id+ or looks in the session
281
+ # for the last used +upload_id+ and saves it as the intance variable
282
+ # <tt>@upload_id</tt>
283
+ def last_upload_id
284
+ @upload_id ||= ((session[:uploads] || {}).keys.map{|k| k.to_i}.sort.last || 0).to_s
285
+ end
286
+
287
+ # Returns the +upload_id+ from the query parameters or if it cannot be found
288
+ # in the query parameters, then return the +last_upload_id+
289
+ def current_upload_id
290
+ params[:upload_id] or last_upload_id
291
+ end
292
+
293
+ # Get the UploadProgress::Progress object for the supplied +upload_id+ from the
294
+ # session. If no +upload_id+ is given, then use the +current_upload_id+
295
+ #
296
+ # If an UploadProgress::Progress object cannot be found, a new instance will be
297
+ # returned with <code>total_bytes == 0</code>, <code>started? == false</code>,
298
+ # and <code>finished? == true</code>.
299
+ def upload_progress(upload_id = nil)
300
+ upload_id ||= current_upload_id
301
+ session[:uploads] && session[:uploads][upload_id] || UploadProgress::Progress.new(0)
302
+ end
303
+ end