vkhater-social_stream-documents 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/.gitignore +15 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +5 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +6 -0
  6. data/Rakefile +26 -0
  7. data/app/assets/flash/Jplayer.swf +0 -0
  8. data/app/assets/images/16/audio.png +0 -0
  9. data/app/assets/images/16/btn_documents.png +0 -0
  10. data/app/assets/images/16/default.png +0 -0
  11. data/app/assets/images/16/doc.png +0 -0
  12. data/app/assets/images/16/mpeg.png +0 -0
  13. data/app/assets/images/16/pdf.png +0 -0
  14. data/app/assets/images/16/photo.png +0 -0
  15. data/app/assets/images/16/plain.png +0 -0
  16. data/app/assets/images/16/ppt.png +0 -0
  17. data/app/assets/images/16/rar.png +0 -0
  18. data/app/assets/images/16/video.png +0 -0
  19. data/app/assets/images/16/xls.png +0 -0
  20. data/app/assets/images/16/zip.png +0 -0
  21. data/app/assets/images/48/audio.png +0 -0
  22. data/app/assets/images/48/default.png +0 -0
  23. data/app/assets/images/48/excel.png +0 -0
  24. data/app/assets/images/48/mpeg.png +0 -0
  25. data/app/assets/images/48/pdf.png +0 -0
  26. data/app/assets/images/48/ppt.png +0 -0
  27. data/app/assets/images/48/rar.png +0 -0
  28. data/app/assets/images/48/resource.png +0 -0
  29. data/app/assets/images/48/video.png +0 -0
  30. data/app/assets/images/48/word.png +0 -0
  31. data/app/assets/images/48/zip.png +0 -0
  32. data/app/assets/images/btn/btn_audio.png +0 -0
  33. data/app/assets/images/btn/btn_document.png +0 -0
  34. data/app/assets/images/btn/btn_excel.png +0 -0
  35. data/app/assets/images/btn/btn_pdf.png +0 -0
  36. data/app/assets/images/btn/btn_picture.png +0 -0
  37. data/app/assets/images/btn/btn_resource.png +0 -0
  38. data/app/assets/images/btn/btn_video.png +0 -0
  39. data/app/assets/images/btn/btn_word.png +0 -0
  40. data/app/assets/images/btn/document.png +0 -0
  41. data/app/assets/javascripts/documents.js.erb +71 -0
  42. data/app/assets/javascripts/social_stream-documents.js +3 -0
  43. data/app/assets/stylesheets/documents.css.scss +271 -0
  44. data/app/assets/stylesheets/show.css.scss +111 -0
  45. data/app/assets/stylesheets/social_stream-documents.css +6 -0
  46. data/app/controllers/audios_controller.rb +2 -0
  47. data/app/controllers/documents_controller.rb +97 -0
  48. data/app/controllers/pictures_controller.rb +2 -0
  49. data/app/controllers/videos_controller.rb +2 -0
  50. data/app/helpers/documents_helper.rb +30 -0
  51. data/app/models/audio.rb +24 -0
  52. data/app/models/document.rb +78 -0
  53. data/app/models/picture.rb +30 -0
  54. data/app/models/video.rb +29 -0
  55. data/app/views/audios/_audio.html.erb +27 -0
  56. data/app/views/audios/_audio_focus_search.html.erb +3 -0
  57. data/app/views/audios/_audio_global_search.html.erb +3 -0
  58. data/app/views/audios/_audio_processed.html.erb +45 -0
  59. data/app/views/audios/_audio_processing.html.erb +7 -0
  60. data/app/views/audios/_audio_show.html.erb +62 -0
  61. data/app/views/audios/_audio_with_details.html.erb +3 -0
  62. data/app/views/audios/_new.html.erb +1 -0
  63. data/app/views/audios/destroy.js.erb +1 -0
  64. data/app/views/audios/index.html.erb +1 -0
  65. data/app/views/audios/show.html.erb +2 -0
  66. data/app/views/common_documents/_document_info.html.erb +48 -0
  67. data/app/views/common_documents/_edit_form.html.erb +57 -0
  68. data/app/views/common_documents/_headers.html.erb +72 -0
  69. data/app/views/common_documents/_index.html.erb +68 -0
  70. data/app/views/common_documents/_show.html.erb +34 -0
  71. data/app/views/documents/_document.html.erb +15 -0
  72. data/app/views/documents/_document_focus_search.html.erb +2 -0
  73. data/app/views/documents/_document_global_search.html.erb +2 -0
  74. data/app/views/documents/_document_show.html.erb +7 -0
  75. data/app/views/documents/_document_with_details.html.erb +13 -0
  76. data/app/views/documents/_new_activity.html.erb +1 -0
  77. data/app/views/documents/_new_activity_fields.html.erb +1 -0
  78. data/app/views/documents/destroy.js.erb +1 -0
  79. data/app/views/documents/index.html.erb +1 -0
  80. data/app/views/documents/show.html.erb +2 -0
  81. data/app/views/pictures/_new.html.erb +1 -0
  82. data/app/views/pictures/_picture.html.erb +42 -0
  83. data/app/views/pictures/_picture_focus_search.html.erb +2 -0
  84. data/app/views/pictures/_picture_global_search.html.erb +2 -0
  85. data/app/views/pictures/_picture_show.html.erb +26 -0
  86. data/app/views/pictures/_picture_with_details.html.erb +2 -0
  87. data/app/views/pictures/destroy.js.erb +1 -0
  88. data/app/views/pictures/index.html.erb +1 -0
  89. data/app/views/pictures/show.html.erb +2 -0
  90. data/app/views/videos/_new.html.erb +1 -0
  91. data/app/views/videos/_video.html.erb +29 -0
  92. data/app/views/videos/_video_focus_search.html.erb +2 -0
  93. data/app/views/videos/_video_global_search.html.erb +2 -0
  94. data/app/views/videos/_video_processed.html.erb +50 -0
  95. data/app/views/videos/_video_processing.html.erb +7 -0
  96. data/app/views/videos/_video_show.html.erb +64 -0
  97. data/app/views/videos/_video_with_details.html.erb +2 -0
  98. data/app/views/videos/destroy.js.erb +1 -0
  99. data/app/views/videos/index.html.erb +1 -0
  100. data/app/views/videos/show.html.erb +2 -0
  101. data/app/worker/audioencoder.rb +7 -0
  102. data/app/worker/videoencoder.rb +7 -0
  103. data/config/locales/en.yml +67 -0
  104. data/config/locales/es.yml +67 -0
  105. data/config/routes.rb +22 -0
  106. data/db/migrate/20110615143707_create_social_stream_documents.rb +24 -0
  107. data/db/migrate/20110922173707_add_file_processing_to_document.rb +9 -0
  108. data/db/migrate/20111005112707_add_title_and_description_to_document.rb +11 -0
  109. data/lib/generators/social_stream/documents/install_generator.rb +24 -0
  110. data/lib/social_stream-documents.rb +25 -0
  111. data/lib/social_stream/documents/engine.rb +46 -0
  112. data/lib/social_stream/documents/version.rb +5 -0
  113. data/lib/social_stream/migrations/documents.rb +9 -0
  114. data/lib/social_stream/toolbar_config/documents.rb +27 -0
  115. data/social_stream-documents.gemspec +30 -0
  116. data/spec/controllers/documents_controller_spec.rb +118 -0
  117. data/spec/controllers/pictures_controller_spec.rb +80 -0
  118. data/spec/dummy/.gitignore +1 -0
  119. data/spec/dummy/Rakefile +7 -0
  120. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  121. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  122. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  123. data/spec/dummy/config.ru +4 -0
  124. data/spec/dummy/config/application.rb +45 -0
  125. data/spec/dummy/config/boot.rb +10 -0
  126. data/spec/dummy/config/database.yml +22 -0
  127. data/spec/dummy/config/environment.rb +5 -0
  128. data/spec/dummy/config/environments/development.rb +25 -0
  129. data/spec/dummy/config/environments/production.rb +49 -0
  130. data/spec/dummy/config/environments/test.rb +37 -0
  131. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  132. data/spec/dummy/config/initializers/devise.rb +176 -0
  133. data/spec/dummy/config/initializers/inflections.rb +10 -0
  134. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  135. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  136. data/spec/dummy/config/initializers/session_store.rb +8 -0
  137. data/spec/dummy/config/locales/en.yml +5 -0
  138. data/spec/dummy/config/navigation.rb +0 -0
  139. data/spec/dummy/config/relations.yml +39 -0
  140. data/spec/dummy/config/routes.rb +60 -0
  141. data/spec/dummy/db/.gitkeep +0 -0
  142. data/spec/dummy/public/404.html +26 -0
  143. data/spec/dummy/public/422.html +26 -0
  144. data/spec/dummy/public/500.html +26 -0
  145. data/spec/dummy/public/favicon.ico +0 -0
  146. data/spec/dummy/public/javascripts/application.js +2 -0
  147. data/spec/dummy/public/javascripts/controls.js +965 -0
  148. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  149. data/spec/dummy/public/javascripts/effects.js +1123 -0
  150. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  151. data/spec/dummy/public/javascripts/rails.js +191 -0
  152. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  153. data/spec/dummy/script/rails +6 -0
  154. data/spec/factories/document.rb +20 -0
  155. data/spec/factories/files/privado.png +0 -0
  156. data/spec/factories/files/rails.png +0 -0
  157. data/spec/factories/files/small.pdf +157 -0
  158. data/spec/factories/picture.rb +21 -0
  159. data/spec/integration/navigation_spec.rb +9 -0
  160. data/spec/models/video_spec.rb +9 -0
  161. data/spec/socialstream_documents_spec.rb +7 -0
  162. data/spec/spec_helper.rb +46 -0
  163. data/spec/support/cancan.rb +1 -0
  164. data/spec/support/db.rb +8 -0
  165. data/spec/support/devise.rb +4 -0
  166. data/spec/support/mock.rb +4 -0
  167. data/vendor/assets/javascripts/jquery.jplayer.js +78 -0
  168. data/vendor/assets/stylesheets/jplayer.blue.monday.css +448 -0
  169. data/vendor/assets/stylesheets/jplayer.blue.monday.jpg +0 -0
  170. data/vendor/assets/stylesheets/jplayer.blue.monday.video.play.hover.png +0 -0
  171. data/vendor/assets/stylesheets/jplayer.blue.monday.video.play.png +0 -0
  172. data/vendor/assets/stylesheets/jquery.lightbox.css +101 -0
  173. data/vendor/assets/stylesheets/pbar-ani.gif +0 -0
  174. metadata +322 -0
@@ -0,0 +1,111 @@
1
+ @import "colors";
2
+
3
+ .picture.content_size .picture_show img{
4
+ max-width: 500px;
5
+ max-height: 500px;
6
+ margin-left: 30px;
7
+ border: 2px solid black;
8
+ -moz-border-radius: 5px;
9
+ -webkit-border-radius: 5px;
10
+ border-radius: 5px;
11
+ }
12
+
13
+ .video_show div.jp-video-270p{
14
+ padding-left: 40px;
15
+ }
16
+ .audio_show div.jp-audio{
17
+ padding-left: 40px;
18
+ }
19
+ .document_show{
20
+ padding-left: 40px;
21
+ }
22
+
23
+ #document_info .details{
24
+ color: $details-color;
25
+ }
26
+ #document_info #dates, #document_info #date_labels{
27
+ display: inline-block;
28
+ }
29
+
30
+ #document_info {
31
+ margin-top: 10px;
32
+ }
33
+ #document_info .right, #document_info .left{
34
+ }
35
+ #document_info .right{
36
+ float: right;
37
+ margin-right: 30px;
38
+ text-align: right;
39
+ }
40
+ #document_info .left{
41
+ float: left;
42
+ margin-left: 30px;
43
+ }
44
+ #document_info #description{
45
+ margin: 15px 40px;
46
+ text-align: justify;
47
+ }
48
+ #document_info #actions{
49
+ margin-top: 10px;
50
+ margin-left: 30px;
51
+ }
52
+ #document_info #actions .action{
53
+ margin-right: 10px;
54
+ padding: 5px;
55
+ }
56
+ #document_info #actions .action.selected{
57
+ border: thin solid $separation-color;
58
+ background: $secondary-color;
59
+ }
60
+ #document_info #actions .action img{
61
+ width: 16px;
62
+ height: 16px;
63
+ }
64
+ #document_details {
65
+ padding: 10px 0px;
66
+ }
67
+ #document_edit, #document_details {
68
+ margin: 5px 5px 0px 5px;
69
+ border: thin solid $separation-color;
70
+ background: $secondary-color;
71
+
72
+ }
73
+ #document_edit textarea{
74
+ resize: none;
75
+ height: 75px;
76
+ }
77
+ #document_edit .form_row {
78
+ padding: 5px 10px;
79
+ }
80
+ #document_edit .form_label {
81
+ width: 100px;
82
+ }
83
+ #document_edit .form_field {
84
+ width: 415px;
85
+ padding: 0;
86
+ }
87
+ #document_edit .form_tag {
88
+ width: 100%;
89
+ }
90
+
91
+ #document_comments {
92
+ margin-top: 20px;
93
+ padding-top: 10px;
94
+ border-top: thin solid $separation-color;
95
+ }
96
+
97
+ #document_comments .new_comment.new_comment_shown {
98
+ width: 500px;
99
+ }
100
+
101
+ #document_comments .new_comment {
102
+ width: 550px;
103
+ }
104
+
105
+ #document_comments .activity, #document_comments .subactivity {
106
+ background: transparent;
107
+ border: none;
108
+ }
109
+ #document_comments .activity_new_comment {
110
+ display:block;
111
+ }
@@ -0,0 +1,6 @@
1
+ /*
2
+ *= require jplayer.blue.monday
3
+ *= require jquery.lightbox
4
+
5
+ *= require_tree .
6
+ */
@@ -0,0 +1,2 @@
1
+ class AudiosController < DocumentsController
2
+ end
@@ -0,0 +1,97 @@
1
+ class DocumentsController < ApplicationController
2
+ include ActionView::Helpers::SanitizeHelper
3
+ include SocialStream::Controllers::Objects
4
+
5
+ belongs_to_subjects :optional => true
6
+
7
+ before_filter :profile_subject!, :only => :index
8
+
9
+ PER_PAGE=20
10
+ SEND_FILE_METHOD = :default
11
+
12
+ def index
13
+ super do |format|
14
+ if params[:no_layout].present?
15
+ format.html { render :action => :index, :layout => false }
16
+ else
17
+ format.html { render :action => :index }
18
+ end
19
+ end
20
+ end
21
+
22
+ def create
23
+ super do |format|
24
+ format.all {redirect_to request.referer || home_path}
25
+ end
26
+ end
27
+
28
+ def update
29
+ update! do |success, failure|
30
+ failure.html { render :action => :show }
31
+ success.html { render :action => :show }
32
+ end
33
+ end
34
+
35
+ def show
36
+ respond_to do |format|
37
+ format.html {render :action => :show}
38
+ format.all {
39
+ path = resource.file.path(params[:style] || params[:format])
40
+
41
+ send_file path,
42
+ :filename => resource.file_file_name,
43
+ :disposition => "inline"
44
+ }
45
+ end
46
+ end
47
+
48
+ #TODO: we have to add the mimetype as in videos_controller
49
+ def download
50
+ path = @document.file.path(params[:style])
51
+
52
+ head(:bad_request) and return unless File.exist?(path)
53
+
54
+ send_file_options = {
55
+ :filename => @document.file_file_name,
56
+ :type => @document.file_content_type
57
+ }
58
+
59
+ # Ask Victor about the rationale of this:
60
+ case SEND_FILE_METHOD
61
+ when :apache then send_file_options[:x_sendfile] = true
62
+ when :nginx then head(:x_accel_redirect => path.gsub(Rails.root, ''))
63
+ end
64
+
65
+ send_file(path, send_file_options)
66
+ end
67
+
68
+ private
69
+
70
+ def collection
71
+ @activities = profile_subject.wall(:profile,
72
+ :for => current_subject,
73
+ :object_type => Array(self.class.index_object_type))
74
+ if params[:query].present?
75
+ @activities = @activities.joins(:activity_objects => :document).where('documents.title LIKE ?', get_search_query)
76
+ end
77
+ @activities = @activities.page(params[:page]).per(PER_PAGE)
78
+ end
79
+
80
+ class << self
81
+ def index_object_type
82
+ [ :Audio, :Video, :Picture, :Document ]
83
+ end
84
+ end
85
+
86
+ def get_search_query
87
+ search_query = ""
88
+ param = strip_tags(params[:query]) || ""
89
+ bare_query = param unless bare_query.html_safe?
90
+ search_query_words = bare_query.strip.split
91
+ search_query_words.each_index do |i|
92
+ search_query+= search_query_words[i] + " " if i < (search_query_words.size - 1)
93
+ search_query+= "%" + search_query_words[i] + "% " if i == (search_query_words.size - 1)
94
+ end
95
+ return search_query.strip
96
+ end
97
+ end
@@ -0,0 +1,2 @@
1
+ class PicturesController < DocumentsController
2
+ end
@@ -0,0 +1,2 @@
1
+ class VideosController < DocumentsController
2
+ end
@@ -0,0 +1,30 @@
1
+ module DocumentsHelper
2
+
3
+ FORMATS = ["msword","vnd.ms-powerpoint","msexcel","rar","zip","mp3","plain","pdf"]
4
+
5
+
6
+ #size can be any of the names that the document has size for
7
+ def thumb_for(document, size)
8
+ image_tag document.thumb(size, self)
9
+ end
10
+
11
+ def thumb_file_for(document, size)
12
+ document.thumb(size, self)
13
+ end
14
+
15
+ def image_tag_for (document)
16
+ image_tag download_document_path document,
17
+ :id => dom_id(document) + "_img"
18
+ end
19
+
20
+ def link_for_wall(document)
21
+ format = Mime::Type.lookup(document.file_content_type)
22
+
23
+ polymorphic_path(document, :format => format, :style => 'thumb0')
24
+ end
25
+
26
+ def show_view_for(document)
27
+ render :partial => document.class.to_s.pluralize.downcase + '/' + document.class.to_s.downcase + "_show",
28
+ :locals => {document.class.to_s.downcase.to_sym => document}
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ class Audio < Document
2
+ has_attached_file :file,
3
+ :url => '/:class/:id.:extension',
4
+ :path => ':rails_root/documents/:class/:id_partition/:style',
5
+ :styles => {:webma => {:format => 'webm'}
6
+ },:processors => [:ffmpeg]
7
+
8
+ process_in_background :file
9
+
10
+ define_index do
11
+ indexes title
12
+ indexes file_file_name, :as => :file_name
13
+ indexes description
14
+ indexes activity_object.tags.name, :as => :tags
15
+
16
+ has created_at
17
+ end
18
+
19
+ # Thumbnail file
20
+ def thumb(size, helper)
21
+ "#{ size.to_s }/audio.png"
22
+ end
23
+
24
+ end
@@ -0,0 +1,78 @@
1
+ class Document < ActiveRecord::Base
2
+ include SocialStream::Models::Object
3
+
4
+ IMAGE_FORMATS = ["doc","ppt","xls","rar","zip","mpeg","plain","pdf"]
5
+
6
+ has_attached_file :file,
7
+ :url => '/:class/:id.:extension',
8
+ :path => ':rails_root/documents/:class/:id_partition/:style/:filename.:extension'
9
+
10
+ validates_attachment_presence :file
11
+ validates_presence_of :title
12
+
13
+ before_validation(:on => :create) do
14
+ set_title
15
+ end
16
+
17
+ define_index do
18
+ indexes title
19
+ indexes file_file_name, :as => :file_name
20
+ indexes description
21
+ indexes activity_object.tags.name, :as => :tags
22
+
23
+ where "type IS NULL"
24
+
25
+ has created_at
26
+ end
27
+
28
+ class << self
29
+ def new(*args)
30
+ if !(self.name == "Document")
31
+ return super
32
+ end
33
+ doc = super
34
+
35
+ if(doc.file_content_type.nil?)
36
+ return doc
37
+ end
38
+
39
+ if !(doc.file_content_type =~ /^image.*/).nil?
40
+ return Picture.new *args
41
+ end
42
+
43
+ if !(doc.file_content_type =~ /^audio.*/).nil?
44
+ return Audio.new *args
45
+ end
46
+
47
+ if !(doc.file_content_type =~ /^video.*/).nil?
48
+ return Video.new *args
49
+ end
50
+
51
+ return doc
52
+ end
53
+ end
54
+
55
+ def mime_type
56
+ Mime::Type.lookup(file_content_type)
57
+ end
58
+
59
+ def format
60
+ mime_type.to_sym
61
+ end
62
+
63
+ # Thumbnail file
64
+ def thumb(size, helper)
65
+ if format && IMAGE_FORMATS.include?(format.to_s)
66
+ "#{ size.to_s }/#{ format }.png"
67
+ else
68
+ "#{ size.to_s }/default.png"
69
+ end
70
+ end
71
+
72
+ protected
73
+
74
+ def set_title
75
+ self.title = self.file_file_name
76
+ end
77
+
78
+ end
@@ -0,0 +1,30 @@
1
+ class Picture < Document
2
+ has_attached_file :file,
3
+ :url => '/:class/:id.:extension',
4
+ :path => ':rails_root/documents/:class/:id_partition/:style',
5
+ :styles => {:thumb => ["48x48#"],
6
+ :thumb0 => ["130x80#"]
7
+ }
8
+
9
+ define_index do
10
+ indexes title
11
+ indexes file_file_name, :as => :file_name
12
+ indexes description
13
+ indexes activity_object.tags.name, :as => :tags
14
+
15
+ has created_at
16
+ end
17
+ # Thumbnail file
18
+ def thumb(size, helper)
19
+ case size
20
+ when 16
21
+ "#{ size.to_s }/photo.png"
22
+ when 48
23
+ helper.picture_path self, :format => format, :style => 'thumb'
24
+ when 130
25
+ helper.picture_path self, :format => format, :style => 'thumb0'
26
+ end
27
+ end
28
+
29
+
30
+ end
@@ -0,0 +1,29 @@
1
+ class Video < Document
2
+ has_attached_file :file,
3
+ :url => '/:class/:id.:extension',
4
+ :path => ':rails_root/documents/:class/:id_partition/:style',
5
+ :styles => {
6
+ :webm => {:format => 'webm'},
7
+ :flv => { :format => 'flv' },
8
+ :thumb => {:geometry => "48x48" , :format => 'png', :time => 5},
9
+ :thumb0 => {:geometry => "130x80", :format => 'png', :time => 5}
10
+ },
11
+ :processors => [:ffmpeg]
12
+
13
+ process_in_background :file
14
+
15
+ define_index do
16
+ indexes title
17
+ indexes file_file_name, :as => :file_name
18
+ indexes description
19
+ indexes activity_object.tags.name, :as => :tags
20
+
21
+ has created_at
22
+ end
23
+
24
+ # Thumbnail file
25
+ def thumb(size, helper)
26
+ "#{ size.to_s }/video.png"
27
+ end
28
+
29
+ end
@@ -0,0 +1,27 @@
1
+ <% content_for :javascript do %>
2
+
3
+ $("#jpId<%=audio.id%>").jPlayer( {
4
+ ready: function () {
5
+ $(this).jPlayer("setMedia", {
6
+ webma: "<%= audio_url(audio, :format => 'webma') %>" // Defines the webma url
7
+ });
8
+ },
9
+ supplied: "webma",
10
+ preload: "none",
11
+ swfPath: "assets",
12
+ cssSelectorAncestor: "#jp_interface_<%=audio.id%>"
13
+ });
14
+
15
+ <% end %>
16
+
17
+ <%= div_for audio, :class => 'content_size' do %>
18
+
19
+
20
+ <% if audio.file_processing %>
21
+ <%= render :partial => 'audios/audio_processing', :locals => {:audio => audio}%>
22
+ <% else %>
23
+ <%= render :partial => 'audios/audio_processed', :locals => {:audio => audio}%>
24
+ <% end %>
25
+
26
+
27
+ <% end %>