blacklight 3.0pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+