sufia 0.0.1.pre2 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/Gemfile +2 -5
  2. data/README.md +44 -12
  3. data/app/assets/javascripts/{scholarsphere.js → sufia.js} +0 -0
  4. data/app/assets/stylesheets/{scholarsphere.css → sufia.css} +0 -0
  5. data/app/controllers/batch_controller.rb +2 -3
  6. data/app/controllers/batch_edits_controller.rb +4 -4
  7. data/app/controllers/dashboard_controller.rb +5 -3
  8. data/app/controllers/generic_files_controller.rb +77 -41
  9. data/app/controllers/users_controller.rb +9 -21
  10. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +2 -2
  11. data/app/helpers/sufia_helper.rb +23 -2
  12. data/app/models/datastreams/file_content_datastream.rb +17 -9
  13. data/app/models/generic_file.rb +1 -809
  14. data/app/views/_add_assets_links.html.erb +1 -1
  15. data/app/views/_user_util_links.html.erb +2 -2
  16. data/app/views/batch/_metadata.html.erb +16 -16
  17. data/app/views/batch/edit.html.erb +16 -10
  18. data/app/views/batch_edits/_metadata.html.erb +16 -16
  19. data/app/views/catalog/_index_partials/_list_files.html.erb +2 -2
  20. data/app/views/catalog/index.html.erb +5 -0
  21. data/app/views/dashboard/_facet_limit.html.erb +1 -1
  22. data/app/views/dashboard/_facet_pagination.html.erb +4 -4
  23. data/app/views/dashboard/_facet_selected.html.erb +1 -1
  24. data/app/views/dashboard/_index_partials/_list_files.html.erb +8 -8
  25. data/app/views/dashboard/_search_form.html.erb +1 -1
  26. data/app/views/dashboard/_sort_and_per_page.html.erb +1 -1
  27. data/app/views/dashboard/index.html.erb +0 -1
  28. data/app/views/generic_files/_breadcrumbs.html.erb +1 -1
  29. data/app/views/generic_files/_descriptions.html.erb +3 -3
  30. data/app/views/generic_files/_field_form +3 -3
  31. data/app/views/generic_files/_media_display.html.erb +5 -4
  32. data/app/views/generic_files/_permission.html.erb +5 -5
  33. data/app/views/generic_files/_versioning.html.erb +1 -1
  34. data/app/views/generic_files/edit.html.erb +2 -2
  35. data/app/views/generic_files/show.html.erb +3 -3
  36. data/app/views/static/versions.html.erb +1 -1
  37. data/app/views/users/edit.html.erb +5 -3
  38. data/app/views/users/index.html.erb +3 -3
  39. data/app/views/users/show.html.erb +6 -6
  40. data/config/jetty.yml +6 -0
  41. data/config/routes.rb +8 -4
  42. data/features/browse_dashboard_files.feature +11 -1
  43. data/features/browse_files.feature +6 -5
  44. data/features/contact_form.feature +4 -0
  45. data/features/display_dashboard.feature +6 -3
  46. data/features/ingest_upload_files.feature +2 -2
  47. data/features/step_definitions/fixture_steps.rb +6 -5
  48. data/features/step_definitions/scholarsphere.rb +24 -1
  49. data/features/step_definitions/user_steps.rb +2 -2
  50. data/features/step_definitions/web_steps.rb +1 -1
  51. data/features/support/env.rb +26 -0
  52. data/features/users.feature +18 -0
  53. data/lib/active_support/core_ext/marshal.rb +22 -0
  54. data/lib/generators/sufia/sufia_generator.rb +18 -2
  55. data/lib/generators/sufia/templates/catalog_controller.rb +3 -4
  56. data/{spec/support → lib/generators/sufia/templates}/config/redis.yml +0 -0
  57. data/lib/generators/sufia/templates/config/sufia.rb +68 -0
  58. data/lib/generators/sufia/templates/migrations/add_ldap_attrs_to_user.rb +41 -0
  59. data/lib/kaminari/helpers/tag.rb +11 -0
  60. data/lib/sufia.rb +30 -7
  61. data/lib/sufia/controller.rb +1 -5
  62. data/lib/sufia/generic_file.rb +200 -0
  63. data/lib/sufia/generic_file/audit.rb +119 -0
  64. data/lib/sufia/generic_file/characterization.rb +82 -0
  65. data/lib/sufia/generic_file/export.rb +339 -0
  66. data/lib/sufia/generic_file/permissions.rb +64 -0
  67. data/lib/sufia/generic_file/thumbnail.rb +68 -0
  68. data/{app/models → lib/sufia/jobs}/audit_job.rb +13 -3
  69. data/lib/sufia/jobs/batch_update_job.rb +86 -0
  70. data/lib/sufia/jobs/characterize_job.rb +35 -0
  71. data/{app/models → lib/sufia/jobs}/content_delete_event_job.rb +3 -1
  72. data/{app/models → lib/sufia/jobs}/content_deposit_event_job.rb +1 -1
  73. data/{app/models → lib/sufia/jobs}/content_new_version_event_job.rb +1 -1
  74. data/{app/models → lib/sufia/jobs}/content_restored_version_event_job.rb +8 -0
  75. data/{app/models → lib/sufia/jobs}/content_update_event_job.rb +1 -1
  76. data/{app/models → lib/sufia/jobs}/event_job.rb +7 -3
  77. data/{app/models → lib/sufia/jobs}/resolrize_job.rb +4 -2
  78. data/lib/sufia/jobs/transcode_video_job.rb +79 -0
  79. data/{app/models → lib/sufia/jobs}/unzip_job.rb +11 -3
  80. data/{app/models → lib/sufia/jobs}/user_edit_profile_event_job.rb +6 -0
  81. data/{app/models → lib/sufia/jobs}/user_follow_event_job.rb +9 -4
  82. data/{app/models → lib/sufia/jobs}/user_unfollow_event_job.rb +6 -0
  83. data/lib/sufia/queue/resque.rb +30 -0
  84. data/lib/sufia/role_mapper.rb +0 -1
  85. data/{app/models/characterize_job.rb → lib/sufia/solr_document_behavior.rb} +6 -7
  86. data/lib/sufia/user.rb +3 -3
  87. data/lib/sufia/version.rb +1 -1
  88. data/lib/tasks/fixtures.rake +38 -38
  89. data/lib/tasks/resque.rake +1 -0
  90. data/solr_conf/conf/solrconfig.xml +32 -1615
  91. data/solr_conf/solr.xml +1 -1
  92. data/spec/active_fedora/unsaved_digital_object_spec.rb +4 -4
  93. data/spec/config/host_to_vhost_spec.rb +4 -4
  94. data/spec/controllers/authorities_controller_spec.rb +1 -1
  95. data/spec/controllers/batch_controller_spec.rb +12 -10
  96. data/spec/controllers/catalog_controller_spec.rb +13 -13
  97. data/spec/controllers/dashboard_controller_spec.rb +15 -15
  98. data/spec/controllers/downloads_controller_spec.rb +14 -14
  99. data/spec/controllers/generic_files_controller_spec.rb +88 -46
  100. data/spec/controllers/mailbox_controller_spec.rb +2 -2
  101. data/spec/controllers/sessions_controller_spec.rb +1 -1
  102. data/spec/controllers/single_use_link_controller_spec.rb +18 -18
  103. data/spec/controllers/users_controller_spec.rb +47 -31
  104. data/spec/fixtures/countdown.avi +0 -0
  105. data/spec/fixtures/sufia/.gitignore +1 -0
  106. data/spec/fixtures/{scholarsphere → sufia}/bg_header.jpg +0 -0
  107. data/spec/fixtures/sufia/sufia_test1.descMeta.txt +12 -0
  108. data/spec/fixtures/{scholarsphere → sufia}/sufia_test1.foxml.erb +2 -2
  109. data/spec/fixtures/{scholarsphere/scholarsphere_test1.txt → sufia/sufia_test1.txt} +0 -0
  110. data/spec/fixtures/sufia/sufia_test2.descMeta.txt +12 -0
  111. data/spec/fixtures/{scholarsphere/scholarsphere_test2.docx → sufia/sufia_test2.docx} +0 -0
  112. data/spec/fixtures/{scholarsphere/scholarsphere_test2.foxml.erb → sufia/sufia_test2.foxml.erb} +6 -6
  113. data/spec/fixtures/sufia/sufia_test3.descMeta.txt +12 -0
  114. data/spec/fixtures/{scholarsphere/scholarsphere_test3.foxml.erb → sufia/sufia_test3.foxml.erb} +6 -6
  115. data/spec/fixtures/{scholarsphere/scholarsphere_test3.xls → sufia/sufia_test3.xls} +0 -0
  116. data/spec/fixtures/sufia/sufia_test4.descMeta.txt +12 -0
  117. data/spec/fixtures/{scholarsphere/scholarsphere_test4.foxml.erb → sufia/sufia_test4.foxml.erb} +6 -6
  118. data/spec/fixtures/{scholarsphere/scholarsphere_test4.pdf → sufia/sufia_test4.pdf} +0 -0
  119. data/spec/fixtures/sufia/sufia_test5.descMeta.txt +19 -0
  120. data/spec/fixtures/{scholarsphere → sufia}/sufia_test5.foxml.erb +3 -3
  121. data/spec/fixtures/{scholarsphere/scholarsphere_test5.mp3 → sufia/sufia_test5.mp3} +0 -0
  122. data/spec/fixtures/sufia/sufia_test5.txt +1 -0
  123. data/spec/fixtures/sufia/sufia_test6.descMeta.txt +12 -0
  124. data/spec/fixtures/{scholarsphere/scholarsphere_test6.foxml.erb → sufia/sufia_test6.foxml.erb} +6 -6
  125. data/spec/fixtures/{scholarsphere/scholarsphere_test6.jp2 → sufia/sufia_test6.jp2} +0 -0
  126. data/spec/fixtures/sufia/sufia_test6.txt +1 -0
  127. data/spec/fixtures/sufia_generic_stub.descMeta.txt +12 -0
  128. data/spec/fixtures/{scholarsphere_generic_stub.foxml.erb → sufia_generic_stub.foxml.erb} +3 -3
  129. data/spec/fixtures/sufia_generic_stub.txt +1 -0
  130. data/spec/lib/sufia/role_mapper_spec.rb +1 -1
  131. data/spec/models/audit_job_spec.rb +8 -11
  132. data/spec/models/batch_spec.rb +5 -5
  133. data/spec/models/batch_update_job_spec.rb +18 -15
  134. data/spec/models/checksum_audit_log_spec.rb +6 -19
  135. data/spec/models/event_jobs_spec.rb +23 -23
  136. data/spec/models/file_content_datastream_spec.rb +14 -14
  137. data/spec/models/fits_datastream_spec.rb +1 -1
  138. data/spec/models/generic_file_spec.rb +88 -41
  139. data/spec/models/single_use_link_spec.rb +3 -3
  140. data/spec/models/transcode_video_job_spec.rb +30 -0
  141. data/spec/models/unzip_job_spec.rb +6 -4
  142. data/spec/rake/{scholarsphere_fixtures_spec.rb → sufia_fixtures_spec.rb} +16 -16
  143. data/spec/routing/route_spec.rb +4 -8
  144. data/spec/spec_helper.rb +0 -7
  145. data/spec/support/Gemfile +4 -2
  146. data/spec/support/fedora_conf/fedora.fcfg +953 -0
  147. data/spec/support/lib/generators/test_app_generator.rb +1 -11
  148. data/sufia.gemspec +5 -3
  149. data/tasks/{scholarsphere-db.rake → sufia-db.rake} +1 -1
  150. data/tasks/{scholarsphere-dev.rake → sufia-dev.rake} +7 -3
  151. data/tasks/{scholarsphere-fixtures.rake → sufia-fixtures.rake} +43 -43
  152. data/tasks/{scholarsphere.rake → sufia.rake} +2 -2
  153. metadata +126 -95
  154. data/app/models/batch_update_job.rb +0 -82
  155. data/app/models/solr_document.rb +0 -50
  156. data/lib/sufia/permissions.rb +0 -43
  157. data/spec/fixtures/scholarsphere/scholarsphere_test1.descMeta.txt +0 -12
  158. data/spec/fixtures/scholarsphere/scholarsphere_test2.descMeta.txt +0 -12
  159. data/spec/fixtures/scholarsphere/scholarsphere_test3.descMeta.txt +0 -12
  160. data/spec/fixtures/scholarsphere/scholarsphere_test4.descMeta.txt +0 -12
  161. data/spec/fixtures/scholarsphere/scholarsphere_test5.descMeta.txt +0 -19
  162. data/spec/fixtures/scholarsphere/scholarsphere_test5.txt +0 -1
  163. data/spec/fixtures/scholarsphere/scholarsphere_test6.descMeta.txt +0 -12
  164. data/spec/fixtures/scholarsphere/scholarsphere_test6.txt +0 -1
  165. data/spec/fixtures/scholarsphere/sufia_scholarsphere1.descMeta.txt +0 -12
  166. data/spec/fixtures/scholarsphere/sufia_scholarsphere1.foxml.erb +0 -79
  167. data/spec/fixtures/scholarsphere/sufia_scholarsphere1.txt +0 -1
  168. data/spec/fixtures/scholarsphere_generic_stub.descMeta.txt +0 -12
  169. data/spec/fixtures/scholarsphere_generic_stub.txt +0 -1
@@ -16,7 +16,7 @@ require 'open3'
16
16
  class FileContentDatastream < ActiveFedora::Datastream
17
17
  include Open3
18
18
 
19
- def extract_metadata
19
+ def to_tempfile &block
20
20
  return if content.nil?
21
21
  f = Tempfile.new("#{pid}-#{dsVersionID}")
22
22
  f.binmode
@@ -27,15 +27,23 @@ class FileContentDatastream < ActiveFedora::Datastream
27
27
  end
28
28
  f.close
29
29
  content.rewind if content.respond_to? :rewind
30
- command = "#{fits_path} -i #{f.path}"
31
- stdin, stdout, stderr = popen3(command)
32
- stdin.close
33
- out = stdout.read
34
- stdout.close
35
- err = stderr.read
36
- stderr.close
37
- raise "Unable to execute command \"#{command}\"\n#{err}" unless err.empty? or err.include? "Error parsing Exiftool XML Output"
30
+ yield(f)
38
31
  f.unlink
32
+
33
+ end
34
+
35
+ def extract_metadata
36
+ out = nil
37
+ to_tempfile do |f|
38
+ command = "#{fits_path} -i #{f.path}"
39
+ stdin, stdout, stderr = popen3(command)
40
+ stdin.close
41
+ out = stdout.read
42
+ stdout.close
43
+ err = stderr.read
44
+ stderr.close
45
+ raise "Unable to execute command \"#{command}\"\n#{err}" unless err.empty? or err.include? "Error parsing Exiftool XML Output"
46
+ end
39
47
  out
40
48
  end
41
49
 
@@ -11,814 +11,6 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
-
15
- require 'datastreams/fits_datastream'
16
- require 'datastreams/generic_file_rdf_datastream'
17
- require 'datastreams/properties_datastream'
18
- require 'datastreams/paranoid_rights_datastream'
19
- require 'datastreams/file_content_datastream'
20
-
21
14
  class GenericFile < ActiveFedora::Base
22
- include ActiveModel::Validations::HelperMethods
23
- include ActiveFedora::Validations
24
- include Hydra::ModelMixins::CommonMetadata
25
- include Hydra::ModelMixins::RightsMetadata
26
- include Sufia::ModelMethods
27
- include Sufia::Noid
28
-
29
- @@FIELD_LABEL_MAP = {"based_near"=>"Location", 'description'=>"Abstract or Summary", 'tag'=>"Keyword", 'date_created'=>"Date Created", 'related_url'=>"Related URL"}
30
-
31
- has_metadata :name => "characterization", :type => FitsDatastream
32
- has_metadata :name => "descMetadata", :type => GenericFileRdfDatastream
33
- has_metadata :name => "properties", :type => PropertiesDatastream
34
- has_metadata :name => "rightsMetadata", :type => ParanoidRightsDatastream
35
- has_file_datastream :name => "content", :type => FileContentDatastream
36
- has_file_datastream :name => "thumbnail", :type => FileContentDatastream
37
-
38
- belongs_to :batch, :property => :is_part_of
39
-
40
- delegate_to :properties, [:relative_path, :depositor], :unique => true
41
- delegate_to :descMetadata, [:date_uploaded, :date_modified], :unique => true
42
- delegate_to :descMetadata, [:related_url, :based_near, :part_of, :creator,
43
- :contributor, :title, :tag, :description, :rights,
44
- :publisher, :date_created, :subject, :format,
45
- :resource_type, :identifier, :language]
46
- delegate :mime_type, :to => :characterization, :unique => true
47
- delegate_to :characterization, [:format_label, :file_size, :last_modified,
48
- :filename, :original_checksum, :rights_basis,
49
- :copyright_basis, :copyright_note,
50
- :well_formed, :valid, :status_message,
51
- :file_title, :file_author, :page_count,
52
- :file_language, :word_count, :character_count,
53
- :paragraph_count, :line_count, :table_count,
54
- :graphics_count, :byte_order, :compression,
55
- :width, :height, :color_space, :profile_name,
56
- :profile_version, :orientation, :color_map,
57
- :image_producer, :capture_device,
58
- :scanning_software, :exif_version,
59
- :gps_timestamp, :latitude, :longitude,
60
- :character_set, :markup_basis,
61
- :markup_language, :duration, :bit_depth,
62
- :sample_rate, :channels, :data_format, :offset]
63
-
64
- around_save :characterize_if_changed, :retry_warming
65
- validate :paranoid_permissions
66
-
67
-
68
- NO_RUNS = 999
69
-
70
- #make sure the terms of service is present and set to 1 before saving
71
- # note GenericFile.create will no longer save a GenericFile as the terms_of_service will not be set
72
- terms_of_service = nil
73
- validates_acceptance_of :terms_of_service, :allow_nil => false
74
-
75
- # set the terms of service on create so an empty generic file can be created
76
- #before_validation(:on => :create) do
77
- # logger.info "!!!! Before create !!!!"
78
- # self.terms_of_service = '1'
79
- #end
80
-
81
- def self.get_label(key)
82
- label = @@FIELD_LABEL_MAP[key]
83
- puts "label = #{label}"
84
- label = key.gsub('_',' ').titleize if label.blank?
85
- return label
86
- end
87
-
88
- def persistent_url
89
- "#{Sufia::Engine.config.persistent_hostpath}#{noid}"
90
- end
91
-
92
- def paranoid_permissions
93
- # let the rightsMetadata ds make this determination
94
- # - the object instance is passed in for easier access to the props ds
95
- rightsMetadata.validate(self)
96
- end
97
-
98
- ## Updates those permissions that are provided to it. Does not replace any permissions unless they are provided
99
- def permissions=(params)
100
- perm_hash = permission_hash
101
- params[:new_user_name].each { |name, access| perm_hash['person'][name] = access } if params[:new_user_name].present?
102
- params[:new_group_name].each { |name, access| perm_hash['group'][name] = access } if params[:new_group_name].present?
103
-
104
- params[:user].each { |name, access| perm_hash['person'][name] = access} if params[:user]
105
- params[:group].each { |name, access| perm_hash['group'][name] = access} if params[:group]
106
- rightsMetadata.update_permissions(perm_hash)
107
- end
108
-
109
- def retry_warming
110
- save_tries = 0
111
- conflict_tries = 0
112
- begin
113
- yield
114
- rescue RSolr::Error::Http => error
115
- save_tries += 1
116
- logger.warn "Retry Solr caught RSOLR error on #{self.pid}: #{error.inspect}"
117
- # fail for good if the tries is greater than 3
118
- rescue_action_without_handler(error) if save_tries >=3
119
- sleep 0.01
120
- retry
121
- rescue ActiveResource::ResourceConflict => error
122
- conflict_tries += 1
123
- logger.warn "Retry caught Active Resource Conflict #{self.pid}: #{error.inspect}"
124
- rescue_action_without_handler(error) if conflict_tries >=10
125
- sleep 0.01
126
- retry
127
- rescue =>error
128
- if (error.to_s.downcase.include? "conflict")
129
- conflict_tries += 1
130
- logger.warn "Retry caught Active Resource Conflict #{self.pid}: #{error.inspect}"
131
- rescue_action_without_handler(error) if conflict_tries >=10
132
- sleep 0.01
133
- retry
134
- else
135
- rescue_action_without_handler(error)
136
- end
137
-
138
- end
139
- end
140
-
141
- def characterize_if_changed
142
- content_changed = self.content.changed?
143
- yield
144
- #logger.debug "DOING CHARACTERIZE ON #{self.pid}"
145
- begin
146
- Resque.enqueue(CharacterizeJob, self.pid) if content_changed
147
- rescue Redis::CannotConnectError
148
- logger.error "Redis is down!"
149
- end
150
- end
151
-
152
- ## Extract the metadata from the content datastream and record it in the characterization datastream
153
- def characterize
154
- self.characterization.content = self.content.extract_metadata
155
- self.append_metadata
156
- self.filename = self.label
157
- self.terms_of_service = '1'
158
- save unless self.new_object?
159
- end
160
-
161
- def related_files
162
- relateds = begin
163
- self.batch.generic_files
164
- rescue NoMethodError => e
165
- #batch is nil
166
- batch_id = self.object_relations["isPartOf"].first || self.object_relations[:is_part_of].first
167
- return [] if batch_id.nil?
168
- self.class.find(:is_part_of_s => batch_id)
169
- end
170
- relateds.reject { |gf| gf.pid == self.pid }
171
- end
172
-
173
- # Create thumbnail requires that the characterization has already been run (so mime_type, width and height is available)
174
- # and that the object is already has a pid set
175
- def create_thumbnail
176
- return if self.content.content.nil?
177
- if ["application/pdf"].include? self.mime_type
178
- create_pdf_thumbnail
179
- elsif ["image/png","image/jpeg", "image/gif"].include? self.mime_type
180
- create_image_thumbnail
181
- # TODO: if we can figure out how to do video (ffmpeg?)
182
- #elsif ["video/mpeg", "video/mp4"].include? self.mime_type
183
- end
184
- end
185
-
186
- # redefine find so that it sets the terms of service
187
- def self.find(args, opts={})
188
- gf = super
189
- # use the field type to see if the return will be one item or multiple
190
- if args.is_a? String
191
- gf.terms_of_service = '1'
192
- elsif gf.respond_to? :each
193
- gf.each {|f| f.terms_of_service = '1'}
194
- end
195
- return gf
196
- end
197
-
198
- def create_pdf_thumbnail
199
- retryCnt = 0
200
- stat = false;
201
- for retryCnt in 1..3
202
- begin
203
- pdf = Magick::ImageList.new
204
- pdf.from_blob(content.content)
205
- first = pdf.to_a[0]
206
- first.format = "PNG"
207
- thumb = first.scale(338, 493)
208
- self.thumbnail.content = thumb.to_blob { self.format = "PNG" }
209
- #logger.debug "Has the content changed before saving? #{self.content.changed?}"
210
- self.terms_of_service = '1'
211
- stat = self.save
212
- break
213
- rescue => e
214
- logger.warn "Rescued an error #{e.inspect} retry count = #{retryCnt}"
215
- sleep 1
216
- end
217
- end
218
- return stat
219
- end
220
-
221
- def create_image_thumbnail
222
- img = Magick::ImageList.new
223
- img.from_blob(content.content)
224
- # horizontal img
225
- height = self.height.first.to_i
226
- width = self.width.first.to_i
227
- scale = height / width
228
- if width > height
229
- if width > 150 and height > 105
230
- thumb = img.scale(150, height/scale)
231
- else
232
- thumb = img.scale(width, height)
233
- end
234
- # vertical img
235
- else
236
- if width > 150 and height > 200
237
- thumb = img.scale(150*scale, 200)
238
- else
239
- thumb = img.scale(width, height)
240
- end
241
- end
242
- self.thumbnail.content = thumb.to_blob
243
- self.terms_of_service = '1'
244
- #logger.debug "Has the content before saving? #{self.content.changed?}"
245
- self.save
246
- end
247
-
248
- def append_metadata
249
- terms = self.characterization_terms
250
- Sufia::Engine.config.fits_to_desc_mapping.each_pair do |k, v|
251
- if terms.has_key?(k)
252
- # coerce to array to remove a conditional
253
- terms[k] = [terms[k]] unless terms[k].is_a? Array
254
- terms[k].each do |term_value|
255
- proxy_term = self.send(v)
256
- if proxy_term.kind_of?(Array)
257
- proxy_term << term_value unless proxy_term.include?(term_value)
258
- else
259
- # these are single-valued terms which cannot be appended to
260
- self.send("#{v}=", term_value)
261
- end
262
- end
263
- end
264
- end
265
- end
266
-
267
- def set_visibility(params)
268
- # only set explicit permissions
269
- if params[:visibility] == "open"
270
- self.datastreams["rightsMetadata"].permissions({:group=>"public"}, "read")
271
- elsif params[:visibility] == "psu"
272
- self.datastreams["rightsMetadata"].permissions({:group=>"registered"}, "read")
273
- self.datastreams["rightsMetadata"].permissions({:group=>"public"}, "none")
274
- elsif params[:visibility] == "restricted"
275
- self.datastreams["rightsMetadata"].permissions({:group=>"registered"}, "none")
276
- self.datastreams["rightsMetadata"].permissions({:group=>"public"}, "none")
277
- #params[:generic_file][:permissions][:group][:public] = "none"
278
- #params[:generic_file][:permissions][:group][:registered] = "none"
279
- end
280
- end
281
-
282
- def to_solr(solr_doc={}, opts={})
283
- super(solr_doc, opts)
284
- solr_doc["label_t"] = self.label
285
- solr_doc["noid_s"] = noid
286
- solr_doc["file_format_t"] = file_format
287
- solr_doc["file_format_facet"] = solr_doc["file_format_t"]
288
- # remap dates as a valid xml date not to_s
289
- solr_doc['generic_file__date_uploaded_dt'] = Time.parse(date_uploaded).utc.to_s.sub(' ','T').sub(' UTC','Z') rescue Time.new(date_uploaded).utc.to_s.sub(' ','T').sub(' UTC','Z') unless date_uploaded.blank?
290
- solr_doc['generic_file__date_modified_dt'] = Time.parse(date_modified).utc.to_s.sub(' ','T').sub(' UTC','Z') rescue Time.new(date_modified).utc.to_s.sub(' ','T').sub(' UTC','Z') unless date_modified.blank?
291
- return solr_doc
292
- end
293
-
294
- def file_format
295
- return nil if self.mime_type.blank? and self.format_label.blank?
296
- return self.mime_type.split('/')[1]+ " ("+self.format_label.join(", ")+")" unless self.mime_type.blank? or self.format_label.blank?
297
- return self.mime_type.split('/')[1] unless self.mime_type.blank?
298
- return self.format_label
299
- end
300
-
301
- # Redefine this for more intuitive keys in Redis
302
- def to_param
303
- noid
304
- end
305
-
306
- def label=(new_label)
307
- @inner_object.label = new_label
308
- if self.title.empty?
309
- self.title = new_label
310
- end
311
- end
312
-
313
- def to_jq_upload
314
- return {
315
- "name" => self.title,
316
- "size" => self.file_size,
317
- "url" => "/files/#{noid}",
318
- "thumbnail_url" => self.pid,
319
- "delete_url" => "deleteme", # generic_file_path(:id => id),
320
- "delete_type" => "DELETE"
321
- }
322
- end
323
-
324
- def get_terms
325
- terms = []
326
- self.descMetadata.class.config[:predicate_mapping].each do |uri, mappings|
327
- new_terms = mappings.keys.map(&:to_s).select do |term|
328
- term.start_with? "generic_file__" and !['type', 'behaviors'].include? term.split('__').last
329
- end
330
- terms.concat(new_terms)
331
- end
332
- terms
333
- end
334
-
335
- def get_values
336
- terms = get_terms
337
- values = {}
338
- terms.each do |t|
339
- next if t.empty?
340
- key = t.to_s.split("generic_file__").last
341
- next if ['part_of', 'date_modified', 'date_uploaded', 'format'].include?(key)
342
- values[key] = self.send(key) if self.respond_to?(key)
343
- end
344
- return values
345
- end
346
-
347
- def characterization_terms
348
- h = {}
349
- self.characterization.class.terminology.terms.each_pair do |k, v|
350
- next unless v.respond_to? :proxied_term
351
- term = v.proxied_term
352
- begin
353
- value = self.send(term.name)
354
- h[term.name] = value unless value.empty?
355
- rescue NoMethodError
356
- next
357
- end
358
- end
359
- h
360
- end
361
-
362
- # MIME: 'application/x-endnote-refer'
363
- def export_as_endnote
364
- end_note_format = {
365
- '%T' => [:title, lambda { |x| x.first }],
366
- '%Q' => [:title, lambda { |x| x.drop(1) }],
367
- '%A' => [:creator],
368
- '%C' => [:publication_place],
369
- '%D' => [:date_created],
370
- '%8' => [:date_uploaded],
371
- '%E' => [:contributor],
372
- '%I' => [:publisher],
373
- '%J' => [:series_title],
374
- '%@' => [:isbn],
375
- '%U' => [:related_url],
376
- '%7' => [:edition_statement],
377
- '%R' => [:persistent_url],
378
- '%X' => [:description],
379
- '%G' => [:language],
380
- '%[' => [:date_modified],
381
- '%9' => [:resource_type],
382
- '%~' => Application.config.application_name,
383
- '%W' => 'Penn State University'
384
- }
385
- text = []
386
- text << "%0 GenericFile"
387
- end_note_format.each do |endnote_key, mapping|
388
- if mapping.is_a? String
389
- values = [mapping]
390
- else
391
- values = self.send(mapping[0]) if self.respond_to? mapping[0]
392
- values = mapping[1].call(values) if mapping.length == 2
393
- values = [values] unless values.is_a? Array
394
- end
395
- next if values.empty? or values.first.nil?
396
- spaced_values = values.join("; ")
397
- text << "#{endnote_key} #{spaced_values}"
398
- end
399
- return text.join("\n")
400
- end
401
-
402
- # MIME type: 'application/x-openurl-ctx-kev'
403
- def export_as_openurl_ctx_kev
404
- export_text = []
405
- export_text << "url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Adc&rfr_id=info%3Asid%2Fblacklight.rubyforge.org%3Agenerator"
406
- field_map = {
407
- :title => 'title',
408
- :creator => 'creator',
409
- :subject => 'subject',
410
- :description => 'description',
411
- :publisher => 'publisher',
412
- :contributor => 'contributor',
413
- :date_created => 'date',
414
- :resource_type => 'format',
415
- :identifier => 'identifier',
416
- :language => 'language',
417
- :tag => 'relation',
418
- :based_near => 'coverage',
419
- :rights => 'rights'
420
- }
421
- field_map.each do |element, kev|
422
- values = self.send(element)
423
- next if values.empty? or values.first.nil?
424
- values.each do |value|
425
- export_text << "rft.#{kev}=#{CGI::escape(value)}"
426
- end
427
- end
428
- export_text.join('&') unless export_text.blank?
429
- end
430
-
431
- def export_as_apa_citation
432
- text = ''
433
- authors_list = []
434
- authors_list_final = []
435
-
436
- #setup formatted author list
437
- authors = get_author_list
438
- authors.each do |author|
439
- next if author.blank?
440
- authors_list.push(abbreviate_name(author))
441
- end
442
- authors_list.each do |author|
443
- if author == authors_list.first #first
444
- authors_list_final.push(author.strip)
445
- elsif author == authors_list.last #last
446
- authors_list_final.push(", &amp; " + author.strip)
447
- else #all others
448
- authors_list_final.push(", " + author.strip)
449
- end
450
- end
451
- text << authors_list_final.join
452
- unless text.blank?
453
- if text[-1,1] != "."
454
- text << ". "
455
- else
456
- text << " "
457
- end
458
- end
459
- # Get Pub Date
460
- text << "(" + setup_pub_date + "). " unless setup_pub_date.nil?
461
-
462
- # setup title info
463
- title_info = setup_title_info
464
- text << "<i>" + title_info + "</i> " unless title_info.nil?
465
-
466
- # Publisher info
467
- text << setup_pub_info unless setup_pub_info.nil?
468
- unless text.blank?
469
- if text[-1,1] != "."
470
- text += "."
471
- end
472
- end
473
- text.html_safe
474
- end
475
-
476
- def export_as_mla_citation
477
- text = ''
478
- authors_final = []
479
-
480
- #setup formatted author list
481
- authors = get_author_list
482
-
483
- if authors.length < 4
484
- authors.each do |author|
485
- if author == authors.first #first
486
- authors_final.push(author)
487
- elsif author == authors.last #last
488
- authors_final.push(", and " + name_reverse(author) + ".")
489
- else #all others
490
- authors_final.push(", " + name_reverse(author))
491
- end
492
- end
493
- text << authors_final.join
494
- unless text.blank?
495
- if text[-1,1] != "."
496
- text << ". "
497
- else
498
- text << " "
499
- end
500
- end
501
- else
502
- text << authors.first + ", et al. "
503
- end
504
- # setup title
505
- title_info = setup_title_info
506
- text << "<i>" + mla_citation_title(title_info) + "</i> " unless title.blank?
507
-
508
- # Publication
509
- text << setup_pub_info + ", " unless setup_pub_info.nil?
510
-
511
- # Get Pub Date
512
- text << setup_pub_date unless setup_pub_date.nil?
513
- if text[-1,1] != "."
514
- text << "." unless text.blank?
515
- end
516
- text.html_safe
517
- end
518
-
519
- def export_as_chicago_citation
520
- author_text = ""
521
- authors = get_all_authors
522
- unless authors.blank?
523
- if authors.length > 10
524
- authors.each_with_index do |author, index|
525
- if index < 7
526
- if index == 0
527
- author_text << "#{author}"
528
- if author.ends_with?(",")
529
- author_text << " "
530
- else
531
- author_text << ", "
532
- end
533
- else
534
- author_text << "#{name_reverse(author)}, "
535
- end
536
- end
537
- end
538
- author_text << " et al."
539
- elsif authors.length > 1
540
- authors.each_with_index do |author,index|
541
- if index == 0
542
- author_text << "#{author}"
543
- if author.ends_with?(",")
544
- author_text << " "
545
- else
546
- author_text << ", "
547
- end
548
- elsif index + 1 == authors.length
549
- author_text << "and #{name_reverse(author)}."
550
- else
551
- author_text << "#{name_reverse(author)}, "
552
- end
553
- end
554
- else
555
- author_text << authors.first
556
- end
557
- end
558
- title_info = ""
559
- title_info << citation_title(clean_end_punctuation(CGI::escapeHTML(title.first)).strip) unless title.blank?
560
-
561
- pub_info = ""
562
- place = self.based_near.first
563
- publisher = self.publisher.first
564
- unless place.blank?
565
- place = CGI::escapeHTML(place)
566
- pub_info << place
567
- pub_info << ": " unless publisher.blank?
568
- end
569
- unless publisher.blank?
570
- publisher = CGI::escapeHTML(publisher)
571
- pub_info << publisher
572
- pub_info << ", " unless setup_pub_date.nil?
573
- end
574
- unless setup_pub_date.nil?
575
- pub_info << setup_pub_date
576
- end
577
-
578
- citation = ""
579
- citation << "#{author_text} " unless author_text.blank?
580
- citation << "<i>#{title_info}.</i> " unless title_info.blank?
581
- citation << "#{pub_info}." unless pub_info.blank?
582
- citation.html_safe
583
- end
584
-
585
- def logs(dsid)
586
- ChecksumAuditLog.where(:dsid=>dsid, :pid=>self.pid).order('created_at desc, id desc')
587
- end
588
-
589
- def audit!
590
- audit(true)
591
- end
592
-
593
- def audit_stat!
594
- audit_stat(true)
595
- end
596
-
597
- def audit_stat(force = false)
598
- logs = audit(force)
599
- audit_results = logs.collect { |result| result["pass"] }
600
-
601
- # check how many non runs we had
602
- non_runs =audit_results.reduce(0) { |sum, value| (value == NO_RUNS) ? sum = sum+1 : sum }
603
- if (non_runs == 0)
604
- result =audit_results.reduce(true) { |sum, value| sum && value }
605
- return result
606
- elsif (non_runs < audit_results.length)
607
- result =audit_results.reduce(true) { |sum, value| (value == NO_RUNS) ? sum : sum && value }
608
- return 'Some audits have not been run, but the ones run were '+ ((result)? 'passing' : 'failing') + '.'
609
- else
610
- return 'Audits have not yet been run on this file.'
611
- end
612
- end
613
-
614
- def audit(force = false)
615
- logs = []
616
- self.per_version do |ver|
617
- logs << GenericFile.audit(ver, force)
618
- end
619
- logs
620
- end
621
-
622
- def per_version(&block)
623
- self.datastreams.each do |dsid, ds|
624
- ds.versions.each do |ver|
625
- block.call(ver)
626
- end
627
- end
628
- end
629
-
630
- def self.audit!(version)
631
- GenericFile.audit(version, true)
632
- end
633
-
634
- def self.audit(version, force = false)
635
- #logger.debug "***AUDIT*** log for #{version.inspect}"
636
- latest_audit = self.find(version.pid).logs(version.dsid).first
637
- unless force
638
- return latest_audit unless GenericFile.needs_audit?(version, latest_audit)
639
- end
640
- begin
641
- Resque.enqueue(AuditJob, version.pid, version.dsid, version.versionID)
642
- rescue Redis::CannotConnectError
643
- logger.error "Redis is down!"
644
- end
645
-
646
- # run the find just incase the job has finished already
647
- latest_audit = self.find(version.pid).logs(version.dsid).first
648
- latest_audit = ChecksumAuditLog.new(:pass=>NO_RUNS, :pid=>version.pid, :dsid=>version.dsid, :version=>version.versionID) unless latest_audit
649
- return latest_audit
650
- end
651
-
652
- def self.needs_audit?(version, latest_audit)
653
- if latest_audit and latest_audit.updated_at
654
- #logger.debug "***AUDIT*** last audit = #{latest_audit.updated_at.to_date}"
655
- days_since_last_audit = (DateTime.now - latest_audit.updated_at.to_date).to_i
656
- #logger.debug "***AUDIT*** days since last audit: #{days_since_last_audit}"
657
- if days_since_last_audit < Sufia::Engine.config.max_days_between_audits
658
- #logger.debug "***AUDIT*** No audit needed for #{version.pid} #{version.versionID} (#{latest_audit.updated_at})"
659
- return false
660
- end
661
- else
662
- logger.warn "***AUDIT*** problem with audit log! Latest Audit is not nil, but updated_at is not set #{latest_audit}" unless latest_audit.nil?
663
- end
664
- #logger.info "***AUDIT*** Audit needed for #{version.pid} #{version.versionID}"
665
- return true
666
- end
667
-
668
- def self.audit_everything(force = false)
669
- GenericFile.find(:all, :rows => GenericFile.count).each do |gf|
670
- gf.per_version do |ver|
671
- GenericFile.audit(ver, force)
672
- end
673
- end
674
- end
675
-
676
- def self.audit_everything!
677
- GenericFile.audit_everything(true)
678
- end
679
-
680
- def self.run_audit(version)
681
- if version.dsChecksumValid
682
- #logger.info "***AUDIT*** Audit passed for #{version.pid} #{version.versionID}"
683
- passing = 1
684
- ChecksumAuditLog.prune_history(version)
685
- else
686
- logger.warn "***AUDIT*** Audit failed for #{version.pid} #{version.versionID}"
687
- passing = 0
688
- end
689
- check = ChecksumAuditLog.create!(:pass=>passing, :pid=>version.pid,
690
- :dsid=>version.dsid, :version=>version.versionID)
691
- return check
692
- end
693
-
694
- # Is this file in the middle of being processed by a batch?
695
- def processing?
696
- return false if self.batch.blank?
697
- return false if !self.batch.methods.include? :status
698
- return (!self.batch.status.empty?) && (self.batch.status.count == 1) && (self.batch.status[0] == "processing")
699
- end
700
-
701
- private
702
-
703
- def permission_hash
704
- old_perms = self.permissions
705
- user_perms = {}
706
- old_perms.select{|r| r[:type] == 'user'}.each do |r|
707
- user_perms[r[:name]] = r[:access]
708
- end
709
- user_perms
710
- group_perms = {}
711
- old_perms.select{|r| r[:type] == 'group'}.each do |r|
712
- group_perms[r[:name]] = r[:access]
713
- end
714
- {'person'=>user_perms, 'group'=>group_perms}
715
- end
716
-
717
- def setup_pub_date
718
- first_date = self.date_created.first
719
- unless first_date.blank?
720
- first_date = CGI::escapeHTML(first_date)
721
- date_value = first_date.gsub(/[^0-9|n\.d\.]/, "")[0,4]
722
- return nil if date_value.nil?
723
- end
724
- clean_end_punctuation(date_value) if date_value
725
- end
726
-
727
- def setup_pub_info
728
- text = ''
729
- place = self.based_near.first
730
- publisher = self.publisher.first
731
- unless place.blank?
732
- place = CGI::escapeHTML(place)
733
- text << place
734
- text << ": " unless publisher.blank?
735
- end
736
- unless publisher.blank?
737
- publisher = CGI::escapeHTML(publisher)
738
- text << publisher
739
- end
740
- return nil if text.strip.blank?
741
- clean_end_punctuation(text.strip)
742
- end
743
-
744
- def mla_citation_title(text)
745
- no_upcase = ["a","an","and","but","by","for","it","of","the","to","with"]
746
- new_text = []
747
- word_parts = text.split(" ")
748
- word_parts.each do |w|
749
- if !no_upcase.include? w
750
- new_text.push(w.capitalize)
751
- else
752
- new_text.push(w)
753
- end
754
- end
755
- new_text.join(" ")
756
- end
757
-
758
- def citation_title(title_text)
759
- prepositions = ["a","about","across","an","and","before","but","by","for","it","of","the","to","with","without"]
760
- new_text = []
761
- title_text.split(" ").each_with_index do |word,index|
762
- if (index == 0 and word != word.upcase) or (word.length > 1 and word != word.upcase and !prepositions.include?(word))
763
- # the split("-") will handle the capitalization of hyphenated words
764
- new_text << word.split("-").map!{|w| w.capitalize }.join("-")
765
- else
766
- new_text << word
767
- end
768
- end
769
- new_text.join(" ")
770
- end
771
-
772
- def setup_title_info
773
- text = ''
774
- title = self.title.first
775
- unless title.blank?
776
- title = CGI::escapeHTML(title)
777
- title_info = clean_end_punctuation(title.strip)
778
- text << title_info
779
- end
780
-
781
- return nil if text.strip.blank?
782
- clean_end_punctuation(text.strip) + "."
783
- end
784
-
785
- def clean_end_punctuation(text)
786
- if [".",",",":",";","/"].include? text[-1,1]
787
- return text[0,text.length-1]
788
- end
789
- text
790
- end
791
-
792
- def get_author_list
793
- self.creator.map { |author| clean_end_punctuation(CGI::escapeHTML(author)) }.uniq
794
- end
795
-
796
- def get_all_authors
797
- authors = self.creator
798
- return authors.empty? ? nil : authors.map { |author| CGI::escapeHTML(author) }
799
- end
800
-
801
- def abbreviate_name(name)
802
- abbreviated_name = ''
803
- name = name.join('') if name.is_a? Array
804
- # make sure we handle "Cher" correctly
805
- return name if !name.include?(' ') and !name.include?(',')
806
- surnames_first = name.include?(',')
807
- delimiter = surnames_first ? ', ' : ' '
808
- name_segments = name.split(delimiter)
809
- given_names = surnames_first ? name_segments.last.split(' ') : name_segments.first.split(' ')
810
- surnames = surnames_first ? name_segments.first.split(' ') : name_segments.last.split(' ')
811
- abbreviated_name << surnames.join(' ')
812
- abbreviated_name << ', '
813
- abbreviated_name << given_names.map { |n| "#{n[0]}." }.join if given_names.is_a? Array
814
- abbreviated_name << "#{given_names[0]}." if given_names.is_a? String
815
- abbreviated_name
816
- end
817
-
818
- def name_reverse(name)
819
- name = clean_end_punctuation(name)
820
- return name unless name =~ /,/
821
- temp_name = name.split(", ")
822
- return temp_name.last + " " + temp_name.first
823
- end
15
+ include Sufia::GenericFile
824
16
  end