blacklight 3.0pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. data/.gitignore +17 -0
  2. data/.gitmodules +6 -0
  3. data/.yardopts +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +14 -0
  6. data/README.rdoc +168 -0
  7. data/Rakefile +9 -0
  8. data/app/controllers/bookmarks_controller.rb +98 -0
  9. data/app/controllers/feedback_controller.rb +37 -0
  10. data/app/controllers/folder_controller.rb +49 -0
  11. data/app/controllers/saved_searches_controller.rb +45 -0
  12. data/app/controllers/search_history_controller.rb +25 -0
  13. data/app/helpers/blacklight_helper.rb +606 -0
  14. data/app/helpers/bookmarks_helper.rb +3 -0
  15. data/app/helpers/catalog_helper.rb +65 -0
  16. data/app/helpers/feedback_helper.rb +2 -0
  17. data/app/helpers/hash_as_hidden_fields.rb +57 -0
  18. data/app/helpers/render_constraints_helper.rb +120 -0
  19. data/app/helpers/saved_searches_helper.rb +2 -0
  20. data/app/helpers/search_history_helper.rb +2 -0
  21. data/app/models/bookmark.rb +6 -0
  22. data/app/models/record_mailer.rb +43 -0
  23. data/app/models/search.rb +19 -0
  24. data/app/views/_flash_msg.html.erb +6 -0
  25. data/app/views/_user_util_links.html.erb +13 -0
  26. data/app/views/bookmarks/index.html.erb +33 -0
  27. data/app/views/catalog/_bookmark_control.html.erb +25 -0
  28. data/app/views/catalog/_bookmark_form.html.erb +8 -0
  29. data/app/views/catalog/_citation.html.erb +15 -0
  30. data/app/views/catalog/_constraints.html.erb +7 -0
  31. data/app/views/catalog/_constraints_element.html.erb +33 -0
  32. data/app/views/catalog/_did_you_mean.html.erb +10 -0
  33. data/app/views/catalog/_document_list.html.erb +30 -0
  34. data/app/views/catalog/_email_form.html.erb +11 -0
  35. data/app/views/catalog/_facet_limit.html.erb +33 -0
  36. data/app/views/catalog/_facet_pagination.html.erb +28 -0
  37. data/app/views/catalog/_facets.html.erb +9 -0
  38. data/app/views/catalog/_folder_control.html.erb +12 -0
  39. data/app/views/catalog/_home.html.erb +6 -0
  40. data/app/views/catalog/_home_text.html.erb +6 -0
  41. data/app/views/catalog/_index_partials/_default.erb +11 -0
  42. data/app/views/catalog/_marc_view.html.erb +33 -0
  43. data/app/views/catalog/_opensearch_response_metadata.html.erb +3 -0
  44. data/app/views/catalog/_previous_next_doc.html.erb +6 -0
  45. data/app/views/catalog/_refworks_form.html.erb +7 -0
  46. data/app/views/catalog/_results_pagination.html.erb +11 -0
  47. data/app/views/catalog/_search_form.html.erb +14 -0
  48. data/app/views/catalog/_show_partials/_default.html.erb +9 -0
  49. data/app/views/catalog/_show_sidebar.html.erb +1 -0
  50. data/app/views/catalog/_show_tools.html.erb +46 -0
  51. data/app/views/catalog/_sms_form.html.erb +23 -0
  52. data/app/views/catalog/_solr_request.html.erb +5 -0
  53. data/app/views/catalog/_sort_and_per_page.html.erb +20 -0
  54. data/app/views/catalog/_unapi_microformat.html.erb +1 -0
  55. data/app/views/catalog/citation.html.erb +1 -0
  56. data/app/views/catalog/email.erb +1 -0
  57. data/app/views/catalog/endnote.endnote.erb +1 -0
  58. data/app/views/catalog/facet.html.erb +28 -0
  59. data/app/views/catalog/index.atom.builder +108 -0
  60. data/app/views/catalog/index.html.erb +37 -0
  61. data/app/views/catalog/index.rss.builder +19 -0
  62. data/app/views/catalog/librarian_view.html.erb +3 -0
  63. data/app/views/catalog/opensearch.json.erb +0 -0
  64. data/app/views/catalog/opensearch.xml.erb +11 -0
  65. data/app/views/catalog/send_email_record.erb +0 -0
  66. data/app/views/catalog/show.endnote.erb +1 -0
  67. data/app/views/catalog/show.html.erb +42 -0
  68. data/app/views/catalog/show.refworks.erb +1 -0
  69. data/app/views/catalog/sms.erb +1 -0
  70. data/app/views/catalog/unapi.xml.builder +6 -0
  71. data/app/views/feedback/complete.html.erb +3 -0
  72. data/app/views/feedback/show.html.erb +20 -0
  73. data/app/views/folder/_tools.html.erb +23 -0
  74. data/app/views/folder/index.html.erb +44 -0
  75. data/app/views/layouts/blacklight.html.erb +49 -0
  76. data/app/views/record_mailer/email_record.erb +6 -0
  77. data/app/views/record_mailer/sms_record.erb +4 -0
  78. data/app/views/saved_searches/index.html.erb +27 -0
  79. data/app/views/search_history/index.html.erb +23 -0
  80. data/blacklight.gemspec +50 -0
  81. data/config.ru +4 -0
  82. data/config/routes.rb +54 -0
  83. data/db/seeds.rb +7 -0
  84. data/features/generators.feature +77 -0
  85. data/features/support/aruba.rb +9 -0
  86. data/install.rb +0 -0
  87. data/install/solr.yml +8 -0
  88. data/lib/blacklight.rb +121 -0
  89. data/lib/blacklight/catalog.rb +311 -0
  90. data/lib/blacklight/comma_link_renderer.rb +27 -0
  91. data/lib/blacklight/configurable.rb +46 -0
  92. data/lib/blacklight/controller.rb +121 -0
  93. data/lib/blacklight/engine.rb +32 -0
  94. data/lib/blacklight/exceptions.rb +13 -0
  95. data/lib/blacklight/marc.rb +46 -0
  96. data/lib/blacklight/marc/citation.rb +251 -0
  97. data/lib/blacklight/search_fields.rb +107 -0
  98. data/lib/blacklight/solr.rb +7 -0
  99. data/lib/blacklight/solr/document.rb +239 -0
  100. data/lib/blacklight/solr/document/dublin_core.rb +40 -0
  101. data/lib/blacklight/solr/document/email.rb +15 -0
  102. data/lib/blacklight/solr/document/marc.rb +84 -0
  103. data/lib/blacklight/solr/document/marc_export.rb +430 -0
  104. data/lib/blacklight/solr/document/sms.rb +13 -0
  105. data/lib/blacklight/solr/facet_paginator.rb +93 -0
  106. data/lib/blacklight/solr_helper.rb +413 -0
  107. data/lib/blacklight/user.rb +55 -0
  108. data/lib/blacklight/version.rb +3 -0
  109. data/lib/colorize.rb +196 -0
  110. data/lib/generators/blacklight/blacklight_generator.rb +134 -0
  111. data/lib/generators/blacklight/templates/SolrMarc.jar +0 -0
  112. data/lib/generators/blacklight/templates/catalog_controller.rb +8 -0
  113. data/lib/generators/blacklight/templates/config/SolrMarc/config-test.properties +37 -0
  114. data/lib/generators/blacklight/templates/config/SolrMarc/config.properties +37 -0
  115. data/lib/generators/blacklight/templates/config/SolrMarc/index.properties +97 -0
  116. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/dewey.bsh +47 -0
  117. data/lib/generators/blacklight/templates/config/SolrMarc/index_scripts/format.bsh +126 -0
  118. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/README_MAPS +1 -0
  119. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/callnumber_map.properties +407 -0
  120. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/composition_era_map.properties +56 -0
  121. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/country_map.properties +379 -0
  122. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/format_map.properties +50 -0
  123. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/instrument_map.properties +101 -0
  124. data/lib/generators/blacklight/templates/config/SolrMarc/translation_maps/language_map.properties +490 -0
  125. data/lib/generators/blacklight/templates/config/blacklight_config.rb +245 -0
  126. data/lib/generators/blacklight/templates/config/solr.yml +6 -0
  127. data/lib/generators/blacklight/templates/migrations/add_user_types_to_bookmarks_searches.rb +11 -0
  128. data/lib/generators/blacklight/templates/migrations/create_bookmarks.rb +17 -0
  129. data/lib/generators/blacklight/templates/migrations/create_searches.rb +15 -0
  130. data/lib/generators/blacklight/templates/migrations/remove_editable_fields_from_bookmarks.rb +11 -0
  131. data/lib/generators/blacklight/templates/public/images/blacklight/bg.png +0 -0
  132. data/lib/generators/blacklight/templates/public/images/blacklight/border.png +0 -0
  133. data/lib/generators/blacklight/templates/public/images/blacklight/bul_sq_gry.gif +0 -0
  134. data/lib/generators/blacklight/templates/public/images/blacklight/checkmark.gif +0 -0
  135. data/lib/generators/blacklight/templates/public/images/blacklight/logo.png +0 -0
  136. data/lib/generators/blacklight/templates/public/images/blacklight/magnifying_glass.gif +0 -0
  137. data/lib/generators/blacklight/templates/public/images/blacklight/remove.gif +0 -0
  138. data/lib/generators/blacklight/templates/public/images/blacklight/separator.gif +0 -0
  139. data/lib/generators/blacklight/templates/public/images/blacklight/start_over.gif +0 -0
  140. data/lib/generators/blacklight/templates/public/javascripts/blacklight.js +485 -0
  141. data/lib/generators/blacklight/templates/public/javascripts/jquery-1.4.2.min.js +154 -0
  142. data/lib/generators/blacklight/templates/public/javascripts/jquery-ui-1.8.1.custom.min.js +756 -0
  143. data/lib/generators/blacklight/templates/public/stylesheets/blacklight.css +487 -0
  144. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  145. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  146. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  147. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  148. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  149. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  150. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_gloss-wave_35_558fd0_500x100.png +0 -0
  151. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  152. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  153. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  154. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  155. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_2e4f81_256x240.png +0 -0
  156. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  157. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  158. data/lib/generators/blacklight/templates/public/stylesheets/jquery/ui-lightness/jquery-ui-1.8.1.custom.css +486 -0
  159. data/lib/generators/blacklight/templates/public/stylesheets/yui.css +31 -0
  160. data/lib/generators/blacklight/templates/solr_document.rb +30 -0
  161. data/lib/railties/blacklight.rake +66 -0
  162. data/lib/railties/cucumber.rake +53 -0
  163. data/lib/railties/rspec.rake +188 -0
  164. data/lib/railties/solr_marc.rake +148 -0
  165. data/lib/railties/test_solr_server.rb +130 -0
  166. data/spec/helpers/catalog_helper_spec.rb +111 -0
  167. data/spec/views/catalog/_sms_form.html.erb_spec.rb +19 -0
  168. data/tasks/blacklight_tasks.rake +4 -0
  169. data/uninstall.rb +1 -0
  170. metadata +431 -0
@@ -0,0 +1,245 @@
1
+ # You can configure Blacklight from here.
2
+ #
3
+ # Blacklight.configure(:environment) do |config| end
4
+ #
5
+ # :shared (or leave it blank) is used by all environments.
6
+ # You can override a shared key by using that key in a particular
7
+ # environment's configuration.
8
+ #
9
+ # If you have no configuration beyond :shared for an environment, you
10
+ # do not need to call configure() for that envirnoment.
11
+ #
12
+ # For specific environments:
13
+ #
14
+ # Blacklight.configure(:test) {}
15
+ # Blacklight.configure(:development) {}
16
+ # Blacklight.configure(:production) {}
17
+ #
18
+
19
+ Blacklight.configure(:shared) do |config|
20
+
21
+ config[:default_solr_params] = {
22
+ :qt => "search",
23
+ :per_page => 10
24
+ }
25
+
26
+ # solr field values given special treatment in the show (single result) view
27
+ config[:show] = {
28
+ :html_title => "title_display",
29
+ :heading => "title_display",
30
+ :display_type => "format"
31
+ }
32
+
33
+ # solr fld values given special treatment in the index (search results) view
34
+ config[:index] = {
35
+ :show_link => "title_display",
36
+ :record_display_type => "format"
37
+ }
38
+
39
+ # solr fields that will be treated as facets by the blacklight application
40
+ # The ordering of the field names is the order of the display
41
+ # TODO: Reorganize facet data structures supplied in config to make simpler
42
+ # for human reading/writing, kind of like search_fields. Eg,
43
+ # config[:facet] << {:field_name => "format", :label => "Format", :limit => 10}
44
+ config[:facet] = {
45
+ :field_names => (facet_fields = [
46
+ "format",
47
+ "pub_date",
48
+ "subject_topic_facet",
49
+ "language_facet",
50
+ "lc_1letter_facet",
51
+ "subject_geo_facet",
52
+ "subject_era_facet"
53
+ ]),
54
+ :labels => {
55
+ "format" => "Format",
56
+ "pub_date" => "Publication Year",
57
+ "subject_topic_facet" => "Topic",
58
+ "language_facet" => "Language",
59
+ "lc_1letter_facet" => "Call Number",
60
+ "subject_era_facet" => "Era",
61
+ "subject_geo_facet" => "Region"
62
+ },
63
+ # Setting a limit will trigger Blacklight's 'more' facet values link.
64
+ # * If left unset, then all facet values returned by solr will be displayed.
65
+ # * If set to an integer, then "f.somefield.facet.limit" will be added to
66
+ # solr request, with actual solr request being +1 your configured limit --
67
+ # you configure the number of items you actually want _displayed_ in a page.
68
+ # * If set to 'true', then no additional parameters will be sent to solr,
69
+ # but any 'sniffed' request limit parameters will be used for paging, with
70
+ # paging at requested limit -1. Can sniff from facet.limit or
71
+ # f.specific_field.facet.limit solr request params. This 'true' config
72
+ # can be used if you set limits in :default_solr_params, or as defaults
73
+ # on the solr side in the request handler itself. Request handler defaults
74
+ # sniffing requires solr requests to be made with "echoParams=all", for
75
+ # app code to actually have it echo'd back to see it.
76
+ :limits => {
77
+ "subject_topic_facet" => 20,
78
+ "language_facet" => true
79
+ }
80
+ }
81
+
82
+ # Have BL send all facet field names to Solr, which has been the default
83
+ # previously. Simply remove these lines if you'd rather use Solr request
84
+ # handler defaults, or have no facets.
85
+ config[:default_solr_params] ||= {}
86
+ config[:default_solr_params][:"facet.field"] = facet_fields
87
+
88
+ # solr fields to be displayed in the index (search results) view
89
+ # The ordering of the field names is the order of the display
90
+ config[:index_fields] = {
91
+ :field_names => [
92
+ "title_display",
93
+ "title_vern_display",
94
+ "author_display",
95
+ "author_vern_display",
96
+ "format",
97
+ "language_facet",
98
+ "published_display",
99
+ "published_vern_display",
100
+ "lc_callnum_display"
101
+ ],
102
+ :labels => {
103
+ "title_display" => "Title:",
104
+ "title_vern_display" => "Title:",
105
+ "author_display" => "Author:",
106
+ "author_vern_display" => "Author:",
107
+ "format" => "Format:",
108
+ "language_facet" => "Language:",
109
+ "published_display" => "Published:",
110
+ "published_vern_display" => "Published:",
111
+ "lc_callnum_display" => "Call number:"
112
+ }
113
+ }
114
+
115
+ # solr fields to be displayed in the show (single result) view
116
+ # The ordering of the field names is the order of the display
117
+ config[:show_fields] = {
118
+ :field_names => [
119
+ "title_display",
120
+ "title_vern_display",
121
+ "subtitle_display",
122
+ "subtitle_vern_display",
123
+ "author_display",
124
+ "author_vern_display",
125
+ "format",
126
+ "url_fulltext_display",
127
+ "url_suppl_display",
128
+ "material_type_display",
129
+ "language_facet",
130
+ "published_display",
131
+ "published_vern_display",
132
+ "lc_callnum_display",
133
+ "isbn_t"
134
+ ],
135
+ :labels => {
136
+ "title_display" => "Title:",
137
+ "title_vern_display" => "Title:",
138
+ "subtitle_display" => "Subtitle:",
139
+ "subtitle_vern_display" => "Subtitle:",
140
+ "author_display" => "Author:",
141
+ "author_vern_display" => "Author:",
142
+ "format" => "Format:",
143
+ "url_fulltext_display" => "URL:",
144
+ "url_suppl_display" => "More Information:",
145
+ "material_type_display" => "Physical description:",
146
+ "language_facet" => "Language:",
147
+ "published_display" => "Published:",
148
+ "published_vern_display" => "Published:",
149
+ "lc_callnum_display" => "Call number:",
150
+ "isbn_t" => "ISBN:"
151
+ }
152
+ }
153
+
154
+
155
+ # "fielded" search configuration. Used by pulldown among other places.
156
+ # For supported keys in hash, see rdoc for Blacklight::SearchFields
157
+ #
158
+ # Search fields will inherit the :qt solr request handler from
159
+ # config[:default_solr_parameters], OR can specify a different one
160
+ # with a :qt key/value. Below examples inherit, except for subject
161
+ # that specifies the same :qt as default for our own internal
162
+ # testing purposes.
163
+ #
164
+ # The :key is what will be used to identify this BL search field internally,
165
+ # as well as in URLs -- so changing it after deployment may break bookmarked
166
+ # urls. A display label will be automatically calculated from the :key,
167
+ # or can be specified manually to be different.
168
+ config[:search_fields] ||= []
169
+
170
+ # This one uses all the defaults set by the solr request handler. Which
171
+ # solr request handler? The one set in config[:default_solr_parameters][:qt],
172
+ # since we aren't specifying it otherwise.
173
+ config[:search_fields] << {
174
+ :key => "all_fields",
175
+ :display_label => 'All Fields'
176
+ }
177
+
178
+ # Now we see how to over-ride Solr request handler defaults, in this
179
+ # case for a BL "search field", which is really a dismax aggregate
180
+ # of Solr search fields.
181
+ config[:search_fields] << {
182
+ :key => 'title',
183
+ # solr_parameters hash are sent to Solr as ordinary url query params.
184
+ :solr_parameters => {
185
+ :"spellcheck.dictionary" => "title"
186
+ },
187
+ # :solr_local_parameters will be sent using Solr LocalParams
188
+ # syntax, as eg {! qf=$title_qf }. This is neccesary to use
189
+ # Solr parameter de-referencing like $title_qf.
190
+ # See: http://wiki.apache.org/solr/LocalParams
191
+ :solr_local_parameters => {
192
+ :qf => "$title_qf",
193
+ :pf => "$title_pf"
194
+ }
195
+ }
196
+ config[:search_fields] << {
197
+ :key =>'author',
198
+ :solr_parameters => {
199
+ :"spellcheck.dictionary" => "author"
200
+ },
201
+ :solr_local_parameters => {
202
+ :qf => "$author_qf",
203
+ :pf => "$author_pf"
204
+ }
205
+ }
206
+
207
+ # Specifying a :qt only to show it's possible, and so our internal automated
208
+ # tests can test it. In this case it's the same as
209
+ # config[:default_solr_parameters][:qt], so isn't actually neccesary.
210
+ config[:search_fields] << {
211
+ :key => 'subject',
212
+ :qt=> 'search',
213
+ :solr_parameters => {
214
+ :"spellcheck.dictionary" => "subject"
215
+ },
216
+ :solr_local_parameters => {
217
+ :qf => "$subject_qf",
218
+ :pf => "$subject_pf"
219
+ }
220
+ }
221
+
222
+ # "sort results by" select (pulldown)
223
+ # label in pulldown is followed by the name of the SOLR field to sort by and
224
+ # whether the sort is ascending or descending (it must be asc or desc
225
+ # except in the relevancy case).
226
+ # label is key, solr field is value
227
+ config[:sort_fields] ||= []
228
+ config[:sort_fields] << ['relevance', 'score desc, pub_date_sort desc, title_sort asc']
229
+ config[:sort_fields] << ['year', 'pub_date_sort desc, title_sort asc']
230
+ config[:sort_fields] << ['author', 'author_sort asc, title_sort asc']
231
+ config[:sort_fields] << ['title', 'title_sort asc, pub_date_sort desc']
232
+
233
+ # If there are more than this many search results, no spelling ("did you
234
+ # mean") suggestion is offered.
235
+ config[:spell_max] = 5
236
+
237
+ # Add documents to the list of object formats that are supported for all objects.
238
+ # This parameter is a hash, identical to the Blacklight::Solr::Document#export_formats
239
+ # output; keys are format short-names that can be exported. Hash includes:
240
+ # :content-type => mime-content-type
241
+ config[:unapi] = {
242
+ 'oai_dc_xml' => { :content_type => 'text/xml' }
243
+ }
244
+ end
245
+
@@ -0,0 +1,6 @@
1
+ development:
2
+ url: http://127.0.0.1:8983/solr
3
+ test:
4
+ url: http://127.0.0.1:8888/solr
5
+ cucumber:
6
+ url: http://127.0.0.1:8888/solr
@@ -0,0 +1,11 @@
1
+ class AddUserTypesToBookmarksSearches < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :searches, :user_type, :string
4
+ add_column :bookmarks, :user_type, :string
5
+ end
6
+
7
+ def self.down
8
+ remove_column :searches, :user_type, :string
9
+ remove_column :bookmarks, :user_type, :string
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ class CreateBookmarks < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :bookmarks do |t|
4
+ t.integer :user_id, :null=>false
5
+ t.text :url
6
+ t.string :document_id
7
+ t.string :title
8
+ t.text :notes
9
+ t.timestamps
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :bookmarks
15
+ end
16
+
17
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSearches < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :searches do |t|
4
+ t.text :query_params
5
+ t.integer :user_id
6
+
7
+ t.timestamps
8
+ end
9
+ add_index :searches, :user_id
10
+ end
11
+
12
+ def self.down
13
+ drop_table :searches
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ class RemoveEditableFieldsFromBookmarks < ActiveRecord::Migration
2
+ def self.up
3
+ remove_column :bookmarks, :notes
4
+ remove_column :bookmarks, :url
5
+ end
6
+
7
+ def self.down
8
+ add_column :bookmarks, :notes, :text
9
+ add_column :bookmarks, :url, :string
10
+ end
11
+ end
@@ -0,0 +1,485 @@
1
+ /* Blacklight has a Javascript setup meant to support local disabling,
2
+ modification, and use of Blacklight behaviors.
3
+
4
+ There is a global Blacklight object, available to your local JS.
5
+
6
+ Individual logic to apply JS behaviors to particular elements is
7
+ stored in functions on that Blacklight object.
8
+
9
+ The actual behaviors themselves are implemented as JQuery plugins,
10
+ JQuery-UI widgets (a special kind of JQuery plugin), or in some cases
11
+ just as logic in the Blacklight global object.
12
+
13
+ All of these things can be modified by your local JS code -- these functions
14
+ are all set up on js load, and only called on document ready, so do your
15
+ modifications just on js load, and they'll be made by the time document ready
16
+ comes along.
17
+
18
+ Examples, in your application's own JS:
19
+
20
+ Change what items zebra_striping gets applied to:
21
+
22
+ Blacklight.do_zebra_stripe.selector = ".my_class .even";
23
+ //Or even add on to existing:
24
+ Blacklight.do_zebra_stripe.selector = Blacklight.do_zebra_stripe.selector + " .my_class .even";
25
+
26
+ Turn off adding of behavior to facet 'more' links, using a no-op function:
27
+
28
+ Blacklight.do_more_facets_behavior = function() {};
29
+
30
+ Change the implementation of facet 'more' link behavior to use entirely
31
+ different JS.
32
+
33
+ Blacklight.do_more_facets_behavior = function() {
34
+ $(Blacklight.do_more_facets_behavior.selector).each(function() {
35
+ //my own thing!
36
+ });
37
+ };
38
+ */
39
+
40
+ Blacklight = {};
41
+
42
+
43
+ $(document).ready(function() {
44
+ Blacklight.do_zebra_stripe();
45
+
46
+ Blacklight.do_select_submit();
47
+
48
+ Blacklight.do_more_facets_behavior();
49
+
50
+ Blacklight.do_lightbox_dialog();
51
+
52
+ Blacklight.do_bookmark_toggle_behavior();
53
+
54
+ Blacklight.do_folder_toggle_behavior();
55
+
56
+ Blacklight.do_facet_expand_contract_behavior();
57
+ });
58
+
59
+
60
+
61
+ //Note all of these definitions are NOT in document ready, they get defined on
62
+ //page load, and later called on document ready.
63
+ (function($) {
64
+
65
+ // adds classes for zebra striping table rows
66
+ Blacklight.do_zebra_stripe = function() {
67
+ $(Blacklight.do_zebra_stripe.selector).addClass('zebra_stripe');
68
+ };
69
+ Blacklight.do_zebra_stripe.selector = "table.zebra tr:even, ul.zebra li:even";
70
+
71
+ //add ajaxy dialogs to certain links, using the ajaxyDialog widget.
72
+ Blacklight.do_more_facets_behavior = function () {
73
+ $( Blacklight.do_more_facets_behavior.selector ).ajaxyDialog({
74
+ width: $(window).width() / 2,
75
+ chainAjaxySelector: "a.next_page, a.prev_page, a.sort_change"
76
+ });
77
+ };
78
+ Blacklight.do_more_facets_behavior.selector = "a.more_facets_link";
79
+
80
+ // Used for sort-by and per-page controls, hide the submit button
81
+ // and make the select auto-submit
82
+ Blacklight.do_select_submit = function() {
83
+ $(Blacklight.do_select_submit.selector).each(function() {
84
+ var select = $(this);
85
+ select.closest("form").find("input[type=submit]").hide();
86
+ select.bind("change", function() {
87
+ this.form.submit();
88
+ });
89
+ });
90
+ };
91
+ Blacklight.do_select_submit.selector = "form.sort select, form.per_page select";
92
+
93
+
94
+ Blacklight.do_lightbox_dialog = function() {
95
+ $("a.lightboxLink").ajaxyDialog({
96
+ chainAjaxySelector: false
97
+ });
98
+ //But make the librarian link wider than 300px default.
99
+ $('a.lightboxLink#librarianLink').ajaxyDialog("option", "width", 650);
100
+ //And the email one too needs to be wider to fit the textarea
101
+ $("a.lightboxLink#emailLink").ajaxyDialog("option", "width", 500);
102
+ };
103
+
104
+ //change form submit toggle to checkbox
105
+ Blacklight.do_bookmark_toggle_behavior = function() {
106
+ $(Blacklight.do_bookmark_toggle_behavior.selector).bl_checkbox_submit({
107
+ checked_label: "In Bookmarks",
108
+ unchecked_label: "Bookmark",
109
+ progress_label: "Saving...",
110
+ //css_class is added to elements added, plus used for id base
111
+ css_class: "toggle_bookmark"
112
+ });
113
+ };
114
+ Blacklight.do_bookmark_toggle_behavior.selector = "form.bookmark_toggle";
115
+
116
+ Blacklight.do_folder_toggle_behavior = function() {
117
+ $( Blacklight.do_folder_toggle_behavior.selector ).bl_checkbox_submit({
118
+ checked_label: "Selected",
119
+ unchecked_label: "Select",
120
+ css_class: "toggle_folder",
121
+ success: function(new_state) {
122
+
123
+ if (new_state) {
124
+ $("#folder_number").text(parseInt($("#folder_number").text()) + 1);
125
+ }
126
+ else {
127
+ $("#folder_number").text(parseInt($("#folder_number").text()) - 1);
128
+ }
129
+ }
130
+ });
131
+ };
132
+ Blacklight.do_folder_toggle_behavior.selector = "form.folder_toggle";
133
+
134
+ Blacklight.do_facet_expand_contract_behavior = function() {
135
+ $( Blacklight.do_facet_expand_contract_behavior.selector ).each (
136
+ Blacklight.facet_expand_contract
137
+ );
138
+ }
139
+ Blacklight.do_facet_expand_contract_behavior.selector = '#facets h3';
140
+
141
+ /* Behavior that makes facet limit headings in sidebar expand/contract
142
+ their contents. This is kind of fragile code targeted specifically
143
+ at how we currently render facet HTML, which is why I put it in a function
144
+ on Blacklight instead of in a jquery plugin. Perhaps in the future this
145
+ could/should be expanded to a general purpose jquery plugin -- or
146
+ we should just use one of the existing ones for expand/contract? */
147
+ Blacklight.facet_expand_contract = function() {
148
+ $(this).next("ul, div").each(function(){
149
+ var f_content = $(this);
150
+ $(f_content).prev('h3').addClass('twiddle');
151
+ // find all f_content's that don't have any span descendants with a class of "selected"
152
+ if($('span.selected', f_content).length == 0){
153
+ // hide it
154
+ f_content.hide();
155
+ } else {
156
+ $(this).prev('h3').addClass('twiddle-open');
157
+ }
158
+
159
+ // attach the toggle behavior to the h3 tag
160
+ $('h3', f_content.parent()).click(function(){
161
+ // toggle the content
162
+ $(this).toggleClass('twiddle-open');
163
+ $(f_content).slideToggle();
164
+ });
165
+ });
166
+ };
167
+
168
+ })(jQuery);
169
+
170
+
171
+
172
+
173
+
174
+
175
+ /* A JQuery plugin (should this be implemented as a widget instead? not sure)
176
+ that will convert a "toggle" form, with single submit button to add/remove
177
+ something, like used for Bookmarks/Folder, into an AJAXy checkbox instead.
178
+
179
+ Apply to a form. Does require certain assumption about the form:
180
+ 1) The same form 'action' href must be used for both ADD and REMOVE
181
+ actions, with the different being the hidden input name="_method"
182
+ being set to "put" or "delete" -- that's the Rails method to pretend
183
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
184
+ to remove. This plugin assumes that.
185
+
186
+ Plus, the form this is applied to should provide a data-doc-id
187
+ attribute (HTML5-style doc-*) that contains the id/primary key
188
+ of the object in question -- used by plugin for a unique value for
189
+ DOM id's.
190
+
191
+ Pass in options for your class name and labels:
192
+ $("form.something").bl_checkbox_submit({
193
+ checked_label: "Selected",
194
+ unchecked_label: "Select",
195
+ progress_label: "Saving...",
196
+ //css_class is added to elements added, plus used for id base
197
+ css_class: "toggle_my_kinda_form",
198
+ success: function(after_success_check_state) {
199
+ #optional callback
200
+ }
201
+ });
202
+ */
203
+ (function($) {
204
+ $.fn.bl_checkbox_submit = function(arg_opts) {
205
+
206
+ this.each(function() {
207
+ var options = $.extend({}, $.fn.bl_checkbox_submit.defaults, arg_opts);
208
+
209
+
210
+ var form = $(this);
211
+ form.children().hide();
212
+ //We're going to use the existing form to actually send our add/removes
213
+ //This works conveneintly because the exact same action href is used
214
+ //for both bookmarks/$doc_id. But let's take out the irrelevant parts
215
+ //of the form to avoid any future confusion.
216
+ form.find("input[type=submit]").remove();
217
+
218
+ //View needs to set data-doc-id so we know a unique value
219
+ //for making DOM id
220
+ var unique_id = form.attr("data-doc-id") || Math.random();
221
+ // if form is currently using method delete to change state,
222
+ // then checkbox is currently checked
223
+ var checked = (form.find("input[name=_method][value=delete]").size() != 0);
224
+
225
+ var checkbox = $('<input type="checkbox">')
226
+ .addClass( options.css_class )
227
+ .attr("id", options.css_class + "_" + unique_id);
228
+ var label = $('<label>')
229
+ .addClass( options.css_class )
230
+ .attr("for", options.css_class + '_' + unique_id)
231
+ .attr("title", form.attr("title"));
232
+
233
+
234
+ function update_state_for(state) {
235
+ checkbox.attr("checked", state);
236
+ label.toggleClass("checked", state);
237
+ if (state) {
238
+ //Set the Rails hidden field that fakes an HTTP verb
239
+ //properly for current state action.
240
+ form.find("input[name=_method]").val("delete");
241
+ label.text(options.checked_label);
242
+ } else {
243
+ form.find("input[name=_method]").val("put");
244
+ label.text(options.unchecked_label);
245
+ }
246
+ }
247
+
248
+ form.append(checkbox).append(" ").append(label);
249
+ update_state_for(checked);
250
+
251
+ checkbox.click(function() {
252
+ label.text(options.progress_label).attr("disabled", "disabled");
253
+ checkbox.attr("disabled", "disabled");
254
+
255
+ $.ajax({
256
+ url: form.attr("action"),
257
+ type: form.attr("method").toUpperCase(),
258
+ data: form.serialize(),
259
+ error: function() {
260
+ alert("Error");
261
+ update_state_for(checked);
262
+ label.removeAttr("disabled");
263
+ checkbox.removeAttr("disabled");
264
+ },
265
+ success: function(data, status, xhr) {
266
+ //if app isn't running at all, xhr annoyingly
267
+ //reports success with status 0.
268
+ if (xhr.status != 0) {
269
+ checked = ! checked;
270
+ update_state_for(checked);
271
+ label.removeAttr("disabled");
272
+ checkbox.removeAttr("disabled");
273
+ options.success.call(form, checked);
274
+ } else {
275
+ alert("Error");
276
+ update_state_for(checked);
277
+ label.removeAttr("disabled");
278
+ checkbox.removeAttr("disabled");
279
+ }
280
+ }
281
+ });
282
+
283
+ return false;
284
+ }); //checkbox.click
285
+
286
+
287
+ }); //this.each
288
+ return this;
289
+ };
290
+
291
+ $.fn.bl_checkbox_submit.defaults = {
292
+ checked_label: "",
293
+ unchecked_label: "",
294
+ progress_label: "Saving...",
295
+ //css_class is added to elements added, plus used for id base
296
+ css_class: "bl_checkbox_submit",
297
+ success: function() {} //callback
298
+ };
299
+
300
+ })(jQuery);
301
+
302
+
303
+ /* A widget written by jrochkind to make a link or form result in
304
+ an in-window ajaxy dialog, instead of page load, using JQuery UI
305
+ Dialog widget.
306
+
307
+ This widget is actually hosted at: https://github.com/jrochkind/jquery.uiExt.ajaxyDialog
308
+
309
+
310
+ Included in this main file because I just couldn't bare to add yet
311
+ another JS file to our app, until we figure out a good fix those too
312
+ many JS files are serious page load speed problem. */
313
+
314
+ (function($) {
315
+ var widgetNamespace = "uiExt";
316
+ var widgetName = "ajaxyDialog";
317
+
318
+ $.widget(widgetNamespace + "." + widgetName, {
319
+ options: {
320
+ extractTitleSelector: "h1, h2, h3, h4, h5",
321
+ chainAjaxySelector: "a:not([target]), form:not([target])",
322
+ closeDialogSelector: "a.dialog-close"
323
+ },
324
+
325
+ _create: function() {
326
+ var self = this;
327
+ var element = self.element[0];
328
+ if (element.tagName.toUpperCase() == "A") {
329
+ $(element).bind("click."+self.widgetName, function(event, ui) {
330
+ self._handleClick();
331
+ return false;
332
+ });
333
+ }
334
+ else if (element.tagName.toUpperCase() == "FORM") {
335
+ $(element).bind("submit."+self.widgetName, function(event, ui) {
336
+ self._handleSubmit();
337
+ return false;
338
+ });
339
+ }
340
+ },
341
+
342
+ open: function() {
343
+ var self = this;
344
+ var element = self.element[0];
345
+
346
+ if ( element.tagName.toUpperCase() == "A") {
347
+ self._handleClick();
348
+ } else if (element.tagName.toUpperCase() == "FORM") {
349
+ self._handleSubmit();
350
+ }
351
+ },
352
+
353
+ close: function() {
354
+ this.dialogContainer().dialog("close");
355
+ },
356
+
357
+ _handleClick: function() {
358
+ var self = this;
359
+ var url = this.element.attr("href");
360
+ var requestDialog = self.dialogContainer();
361
+
362
+ $("body").css("cursor", "progress");
363
+
364
+ $.ajax({
365
+ url: url,
366
+ dataType: "html",
367
+ success: function(resp, status, xhr) {
368
+ if (xhr.status != 0) {
369
+ self._loadToDialog(resp);
370
+ } else {
371
+ //stupid jquery calling this 'success', it's
372
+ //network unavailable.
373
+ self._displayFailure(url, xhr, status);
374
+ }
375
+ },
376
+ error: function(xhr, msg) {
377
+ self._displayFailure(url, xhr, msg);
378
+ }
379
+ });
380
+ },
381
+
382
+ _handleSubmit: function() {
383
+ var self = this;
384
+ var form = self.element;
385
+ var actionUri = form.attr("action");
386
+ var serialized = form.serialize();
387
+
388
+ $("body").css("cursor", "progress");
389
+
390
+ $.ajax({
391
+ url: actionUri,
392
+ data: serialized,
393
+ type: form.attr("method").toUpperCase(),
394
+ dataType: "html",
395
+ success: function(resp, status, xhr) {
396
+ if (xhr.status != 0) {
397
+ self._loadToDialog(resp);
398
+ } else {
399
+ //stupid jquery calling this 'success', it's
400
+ //network unavailable.
401
+ self._displayFailure(url, xhr, status);
402
+ }
403
+ },
404
+ error: function(xhr, msg) {
405
+ self._displayFailure(actionUri, xhr, msg);
406
+ }
407
+ });
408
+ },
409
+
410
+ _loadToDialog: function(html_content) {
411
+ var self = this;
412
+ var dialog = self.dialogContainer();
413
+ //Cheesy way to restore it to it's default options, plus
414
+ //our own local options, since its' a reuseable dialog.
415
+ //for now we insist on modal:true.
416
+ dialog.dialog($.extend({},
417
+ $.ui.dialog.prototype.options,
418
+ self.options,
419
+ {autoOpen:false, modal:true}
420
+ ));
421
+
422
+ if (self._trigger('beforeDisplay', 0, html_content) !== false) {
423
+ dialog.html( html_content );
424
+
425
+ //extract and set title
426
+ var title;
427
+ self.options.extractTitleSelector &&
428
+ (title = dialog.find(self.options.extractTitleSelector).first().remove().text());
429
+ title = title ||
430
+ self.element.attr("title")
431
+ title && dialog.dialog("option", "title", title);
432
+
433
+ //Make any hyperlinks or forms ajaxified, by applying
434
+ //this very same plugin to em, and passing on our options.
435
+ if (self.options.chainAjaxySelector) {
436
+ dialog.find(self.options.chainAjaxySelector).ajaxyDialog(self.options);
437
+ }
438
+
439
+ //Make any links marked dialog-close do so
440
+ if ( self.options.closeDialogSelector ) {
441
+ dialog.find(self.options.closeDialogSelector).unbind("click." + widgetName);
442
+ dialog.find(self.options.closeDialogSelector).bind("click." + widgetName, function() {
443
+ dialog.dialog("close");
444
+ return false;
445
+ });
446
+ }
447
+
448
+ dialog.dialog("open");
449
+ }
450
+ $("body").css("cursor", "auto");
451
+ },
452
+
453
+ _displayFailure: function(uri, xhr, serverMsg) {
454
+ if ( this._trigger("error", 0, {uri:uri, xhr: xhr, serverMsg: serverMsg}) !== false) {
455
+ var dialog = this.dialogContainer();
456
+
457
+ dialog.html("<div class='ui-state-error' style='padding: 1em;'><p><span style='float: left; margin-right: 0.3em;' class='ui-icon ui-icon-alert'></span>Sorry, a software error has occured.</p><p>" + uri + ": " + xhr.status + " " + serverMsg+"</p></div>");
458
+ dialog.dialog("option", "title", "Sorry, an error has occured.");
459
+ dialog.dialog("option", "buttons", {"OK": function() { dialog.dialog("close"); }});
460
+ dialog.dialog("open");
461
+ }
462
+ $("body").css("cursor", "auto");
463
+ },
464
+
465
+ // The DOM element which has a ui dialog() called on it.
466
+ // Right now we insist upon modal dialogs, and re-use the same
467
+ // <div>.dialog() for all of them. It's lazily created here.
468
+ // If client calls dialog("destroy") on it, no problem, it'll
469
+ // be lazily created if it's needed again.
470
+ dialogContainer: function() {
471
+ var existing = $("#reusableModalDialog");
472
+ if ( existing.size() > 0) {
473
+ return existing.first();
474
+ }
475
+ else {
476
+ //single shared element for modal dialogs
477
+ var requestDialog = $('<div id="reusableModalDialog" style="display:none"></div>').appendTo('body').
478
+ dialog({autoOpen: false});
479
+ return requestDialog;
480
+ }
481
+ }
482
+
483
+ });
484
+ }(jQuery));
485
+