calagator 0.0.1.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (384) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +75 -0
  4. data/Rakefile +12 -0
  5. data/app/assets/images/confused-alligator-sm.png +0 -0
  6. data/app/assets/images/disk.png +0 -0
  7. data/app/assets/images/edit.png +0 -0
  8. data/app/assets/images/external_sites/epdx.png +0 -0
  9. data/app/assets/images/external_sites/external.gif +0 -0
  10. data/app/assets/images/external_sites/facebook.png +0 -0
  11. data/app/assets/images/external_sites/foursquare.png +0 -0
  12. data/app/assets/images/external_sites/gowalla.png +0 -0
  13. data/app/assets/images/external_sites/lanyrd.png +0 -0
  14. data/app/assets/images/external_sites/meetup.png +0 -0
  15. data/app/assets/images/external_sites/plancast.png +0 -0
  16. data/app/assets/images/external_sites/shizzow.png +0 -0
  17. data/app/assets/images/external_sites/upcoming.png +0 -0
  18. data/app/assets/images/external_sites/yelp.png +0 -0
  19. data/app/assets/images/feed.png +0 -0
  20. data/app/assets/images/heart.png +0 -0
  21. data/app/assets/images/icon_ical.gif +0 -0
  22. data/app/assets/images/information.png +0 -0
  23. data/app/assets/images/nav_marker.gif +0 -0
  24. data/app/assets/images/nav_marker.png +0 -0
  25. data/app/assets/images/plus.png +0 -0
  26. data/app/assets/images/redx.png +0 -0
  27. data/app/assets/images/site-icon.png +0 -0
  28. data/app/assets/images/spinner.gif +0 -0
  29. data/app/assets/images/star.png +0 -0
  30. data/app/assets/images/subnav_marker.gif +0 -0
  31. data/app/assets/images/subnav_marker.png +0 -0
  32. data/app/assets/images/tag_blue.png +0 -0
  33. data/app/assets/images/tag_icons/angular.png +0 -0
  34. data/app/assets/images/tag_icons/beer.png +0 -0
  35. data/app/assets/images/tag_icons/bitcoin.png +0 -0
  36. data/app/assets/images/tag_icons/free.png +0 -0
  37. data/app/assets/images/tag_icons/golang.png +0 -0
  38. data/app/assets/images/tag_icons/html.png +0 -0
  39. data/app/assets/images/tag_icons/javascript.png +0 -0
  40. data/app/assets/images/tag_icons/linux.png +0 -0
  41. data/app/assets/images/tag_icons/php.png +0 -0
  42. data/app/assets/images/tag_icons/pizza.png +0 -0
  43. data/app/assets/images/tag_icons/python.png +0 -0
  44. data/app/assets/images/tag_icons/rails.png +0 -0
  45. data/app/assets/images/tag_icons/ruby.png +0 -0
  46. data/app/assets/images/tag_icons/sass.png +0 -0
  47. data/app/assets/images/tag_icons/swift.png +0 -0
  48. data/app/assets/images/transmit_blue.png +0 -0
  49. data/app/assets/images/weekday_background.gif +0 -0
  50. data/app/assets/javascripts/calagator.js +18 -0
  51. data/app/assets/javascripts/calagator/forms.js +60 -0
  52. data/app/assets/stylesheets/calagator.scss +8 -0
  53. data/app/assets/stylesheets/calagator/common.scss +14 -0
  54. data/app/assets/stylesheets/calagator/datepicker.scss +177 -0
  55. data/app/assets/stylesheets/calagator/errors.css +110 -0
  56. data/app/assets/stylesheets/calagator/forms.scss +4 -0
  57. data/app/assets/stylesheets/calagator/reset.css +40 -0
  58. data/app/assets/stylesheets/calagator/theme.css +0 -0
  59. data/app/controllers/calagator/admin_controller.rb +27 -0
  60. data/app/controllers/calagator/application_controller.rb +82 -0
  61. data/app/controllers/calagator/events_controller.rb +171 -0
  62. data/app/controllers/calagator/site_controller.rb +35 -0
  63. data/app/controllers/calagator/sources_controller.rb +94 -0
  64. data/app/controllers/calagator/venues_controller.rb +133 -0
  65. data/app/controllers/calagator/versions_controller.rb +20 -0
  66. data/app/helpers/calagator/application_helper.rb +128 -0
  67. data/app/helpers/calagator/events_helper.rb +184 -0
  68. data/app/helpers/calagator/google_event_export_helper.rb +67 -0
  69. data/app/helpers/calagator/mapping_helper.rb +103 -0
  70. data/app/helpers/calagator/sources_helper.rb +10 -0
  71. data/app/helpers/calagator/tags_helper.rb +42 -0
  72. data/app/helpers/calagator/time_range_helper.rb +166 -0
  73. data/app/models/calagator/event.rb +246 -0
  74. data/app/models/calagator/event/cloner.rb +39 -0
  75. data/app/models/calagator/event/ical_renderer.rb +155 -0
  76. data/app/models/calagator/event/overview.rb +45 -0
  77. data/app/models/calagator/event/saver.rb +58 -0
  78. data/app/models/calagator/event/search.rb +72 -0
  79. data/app/models/calagator/event/search_engine.rb +28 -0
  80. data/app/models/calagator/event/search_engine/apache_sunspot.rb +106 -0
  81. data/app/models/calagator/event/search_engine/sql.rb +107 -0
  82. data/app/models/calagator/source.rb +115 -0
  83. data/app/models/calagator/source/importer.rb +46 -0
  84. data/app/models/calagator/source/parser.rb +128 -0
  85. data/app/models/calagator/source/parser/facebook.rb +67 -0
  86. data/app/models/calagator/source/parser/hcal.rb +108 -0
  87. data/app/models/calagator/source/parser/http_authentication_required_error.rb +6 -0
  88. data/app/models/calagator/source/parser/ical.rb +186 -0
  89. data/app/models/calagator/source/parser/meetup.rb +72 -0
  90. data/app/models/calagator/source/parser/not_found.rb +9 -0
  91. data/app/models/calagator/source/parser/plancast.rb +59 -0
  92. data/app/models/calagator/venue.rb +161 -0
  93. data/app/models/calagator/venue/geocoder.rb +51 -0
  94. data/app/models/calagator/venue/search.rb +63 -0
  95. data/app/models/calagator/venue/search_engine.rb +24 -0
  96. data/app/models/calagator/venue/search_engine/apache_sunspot.rb +85 -0
  97. data/app/models/calagator/venue/search_engine/sql.rb +68 -0
  98. data/app/models/event/search_engine/base.rb +0 -0
  99. data/app/observers/calagator/cache_observer.rb +37 -0
  100. data/app/views/calagator/admin/events.html.erb +28 -0
  101. data/app/views/calagator/admin/index.html.erb +8 -0
  102. data/app/views/calagator/events/_feed_item.html.erb +62 -0
  103. data/app/views/calagator/events/_form.html.erb +63 -0
  104. data/app/views/calagator/events/_gcal_reminder.html.erb +1 -0
  105. data/app/views/calagator/events/_hcal.html.erb +41 -0
  106. data/app/views/calagator/events/_item.html.erb +120 -0
  107. data/app/views/calagator/events/_list.html.erb +30 -0
  108. data/app/views/calagator/events/_list_item.html.erb +37 -0
  109. data/app/views/calagator/events/_search_section.html.erb +15 -0
  110. data/app/views/calagator/events/_subnav.html.erb +13 -0
  111. data/app/views/calagator/events/_table.html.erb +74 -0
  112. data/app/views/calagator/events/duplicates.html.erb +58 -0
  113. data/app/views/calagator/events/edit.html.erb +3 -0
  114. data/app/views/calagator/events/index.atom.builder +32 -0
  115. data/app/views/calagator/events/index.html.erb +51 -0
  116. data/app/views/calagator/events/index.kml.erb +20 -0
  117. data/app/views/calagator/events/new.html.erb +7 -0
  118. data/app/views/calagator/events/search.html.erb +25 -0
  119. data/app/views/calagator/events/show.html.erb +81 -0
  120. data/app/views/calagator/site/_appropriateness_message.html.erb +15 -0
  121. data/app/views/calagator/site/_description.html.erb +3 -0
  122. data/app/views/calagator/site/about.html.erb +7 -0
  123. data/app/views/calagator/site/defunct.html.erb +15 -0
  124. data/app/views/calagator/site/export.html.erb +4 -0
  125. data/app/views/calagator/site/index.html.erb +40 -0
  126. data/app/views/calagator/site/opensearch.xml.builder +8 -0
  127. data/app/views/calagator/sources/_subnav.html.erb +0 -0
  128. data/app/views/calagator/sources/edit.html.erb +12 -0
  129. data/app/views/calagator/sources/import.html.erb +10 -0
  130. data/app/views/calagator/sources/index.html.erb +22 -0
  131. data/app/views/calagator/sources/new.html.erb +32 -0
  132. data/app/views/calagator/sources/show.html.erb +16 -0
  133. data/app/views/calagator/venues/_form.html.erb +57 -0
  134. data/app/views/calagator/venues/_subnav.html.erb +10 -0
  135. data/app/views/calagator/venues/duplicates.html.erb +65 -0
  136. data/app/views/calagator/venues/edit.html.erb +3 -0
  137. data/app/views/calagator/venues/index.html.erb +93 -0
  138. data/app/views/calagator/venues/index.kml.erb +15 -0
  139. data/app/views/calagator/venues/map.html.erb +4 -0
  140. data/app/views/calagator/venues/new.html.erb +5 -0
  141. data/app/views/calagator/venues/show.html.erb +113 -0
  142. data/app/views/calagator/versions/_chooser.html.erb +28 -0
  143. data/app/views/calagator/versions/_edit_with_chooser.html.erb +16 -0
  144. data/app/views/layouts/calagator/application.html.erb +110 -0
  145. data/config/deploy/local.rb +37 -0
  146. data/config/deploy/lucca.rb +33 -0
  147. data/config/initializers/dates.rb +7 -0
  148. data/config/initializers/formtastic.rb +82 -0
  149. data/config/initializers/geokit.rb +74 -0
  150. data/config/initializers/ics_renderer.rb +6 -0
  151. data/config/initializers/load_tag_model_extensions.rb +3 -0
  152. data/config/initializers/mime_types.rb +9 -0
  153. data/config/initializers/observers.rb +1 -0
  154. data/config/initializers/search_engine.rb +4 -0
  155. data/config/initializers/set_default_url_host.rb +5 -0
  156. data/config/initializers/time_get_zone.rb +8 -0
  157. data/config/locales/en.yml +5 -0
  158. data/config/routes.rb +55 -0
  159. data/config/secrets.yml.blag +75 -0
  160. data/config/sunspot.yml +23 -0
  161. data/db/development.sqlite3 +0 -0
  162. data/db/development.sqlite3.bak +0 -0
  163. data/db/development.sqlite3.old +0 -0
  164. data/db/development~20111112@110950.sqlite3 +0 -0
  165. data/db/migrate/001_create_events.rb +17 -0
  166. data/db/migrate/002_create_venues.rb +17 -0
  167. data/db/migrate/003_create_sources.rb +16 -0
  168. data/db/migrate/004_add_detailed_fields_to_venue.rb +19 -0
  169. data/db/migrate/005_add_end_time_to_events.rb +9 -0
  170. data/db/migrate/006_add_source_id_to_events.rb +9 -0
  171. data/db/migrate/008_add_source_id_to_venues.rb +10 -0
  172. data/db/migrate/009_add_duplicate_of_column_to_venues.rb +9 -0
  173. data/db/migrate/010_add_duplicate_of_column_to_events.rb +9 -0
  174. data/db/migrate/011_change_lat_long_type.rb +12 -0
  175. data/db/migrate/012_add_source_reimport.rb +9 -0
  176. data/db/migrate/013_change_end_time_to_duration.rb +11 -0
  177. data/db/migrate/014_remove_format_type_from_source.rb +9 -0
  178. data/db/migrate/015_create_updates.rb +15 -0
  179. data/db/migrate/016_remove_next_update_from_source.rb +9 -0
  180. data/db/migrate/20080705163959_change_duration_to_end_time.rb +11 -0
  181. data/db/migrate/20080705164959_create_tags_and_taggings.rb +28 -0
  182. data/db/migrate/20081011181519_create_versioned_events.rb +25 -0
  183. data/db/migrate/20081011193124_create_versioned_venues.rb +32 -0
  184. data/db/migrate/20081115190515_add_rrule_to_events.rb +15 -0
  185. data/db/migrate/20090912082129_create_versions.rb +18 -0
  186. data/db/migrate/20110219205156_add_closed_flag_to_venues.rb +9 -0
  187. data/db/migrate/20110220001008_add_wifi_flag_to_venues.rb +9 -0
  188. data/db/migrate/20110220011427_add_access_notes_to_venues.rb +9 -0
  189. data/db/migrate/20110220031117_add_events_count_to_venues.rb +8 -0
  190. data/db/migrate/20110604174521_add_venue_details_to_events.rb +9 -0
  191. data/db/migrate/20110717231316_acts_as_taggable_on_migration.rb +50 -0
  192. data/db/migrate/20120709092821_cleanup.rb +14 -0
  193. data/db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb +11 -0
  194. data/db/migrate/20150206085809_remove_updates.rb +13 -0
  195. data/db/migrate/20150207231355_add_locked_status_to_events.rb +5 -0
  196. data/db/production.sqlite3 +0 -0
  197. data/db/schema.rb +102 -0
  198. data/db/seeds.rb +16 -0
  199. data/db/test.sqlite3 +0 -0
  200. data/db/test2.sqlite3 +0 -0
  201. data/lib/calagator.rb +30 -0
  202. data/lib/calagator/blacklist_validator.rb +69 -0
  203. data/lib/calagator/decode_html_entities_hack.rb +33 -0
  204. data/lib/calagator/duplicate_checking.rb +133 -0
  205. data/lib/calagator/duplicate_checking/controller_actions.rb +38 -0
  206. data/lib/calagator/duplicate_checking/duplicate_finder.rb +83 -0
  207. data/lib/calagator/duplicate_checking/duplicate_squasher.rb +74 -0
  208. data/lib/calagator/engine.rb +30 -0
  209. data/lib/calagator/strip_whitespace.rb +19 -0
  210. data/lib/calagator/tag_model_extensions.rb +104 -0
  211. data/lib/calagator/url_prefixer.rb +9 -0
  212. data/lib/calagator/version.rb +3 -0
  213. data/lib/generators/calagator/install_generator.rb +39 -0
  214. data/lib/generators/calagator/templates/config/calagator.rb +26 -0
  215. data/lib/generators/calagator/templates/config/secrets.yml.sample +83 -0
  216. data/lib/secrets_reader.rb +76 -0
  217. data/lib/tasks/spec_db.rake +53 -0
  218. data/lib/tasks/sunspot_reindex_calagator.rake +6 -0
  219. data/lib/tasks/sunspot_solr_restart_enhancements.rake +19 -0
  220. data/lib/tasks/update_counter_caches.rake +13 -0
  221. data/lib/templates/erb/scaffold/_form.html.erb +11 -0
  222. data/lib/theme_reader.rb +17 -0
  223. data/lib/wait_for_solr.rb +25 -0
  224. data/spec/controllers/calagator/application_controller_spec.rb +47 -0
  225. data/spec/controllers/calagator/events_controller_spec.rb +794 -0
  226. data/spec/controllers/calagator/site_controller_spec.rb +59 -0
  227. data/spec/controllers/calagator/sources_controller_spec.rb +439 -0
  228. data/spec/controllers/calagator/venues_controller_spec.rb +319 -0
  229. data/spec/controllers/calagator/versions_controller_spec.rb +82 -0
  230. data/spec/controllers/squash_many_duplicates_examples.rb +49 -0
  231. data/spec/dummy/Gemfile +39 -0
  232. data/spec/dummy/Gemfile.lock +195 -0
  233. data/spec/dummy/README.rdoc +261 -0
  234. data/spec/dummy/Rakefile +7 -0
  235. data/spec/dummy/app/assets/images/rails.png +0 -0
  236. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  237. data/spec/dummy/app/assets/stylesheets/application.css +14 -0
  238. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  239. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  240. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  241. data/spec/dummy/config.ru +4 -0
  242. data/spec/dummy/config/application.rb +62 -0
  243. data/spec/dummy/config/boot.rb +6 -0
  244. data/spec/dummy/config/database.yml +25 -0
  245. data/spec/dummy/config/environment.rb +5 -0
  246. data/spec/dummy/config/environments/development.rb +37 -0
  247. data/spec/dummy/config/environments/production.rb +67 -0
  248. data/spec/dummy/config/environments/test.rb +37 -0
  249. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  250. data/spec/dummy/config/initializers/calagator.rb +26 -0
  251. data/spec/dummy/config/initializers/inflections.rb +15 -0
  252. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  253. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  254. data/spec/dummy/config/initializers/session_store.rb +8 -0
  255. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  256. data/spec/dummy/config/locales/en.yml +5 -0
  257. data/spec/dummy/config/routes.rb +59 -0
  258. data/spec/dummy/config/secrets.yml +83 -0
  259. data/spec/dummy/db/development.sqlite3 +0 -0
  260. data/spec/dummy/db/migrate/20150309023304_create_events.calagator.rb +18 -0
  261. data/spec/dummy/db/migrate/20150309023305_create_venues.calagator.rb +18 -0
  262. data/spec/dummy/db/migrate/20150309023306_create_sources.calagator.rb +17 -0
  263. data/spec/dummy/db/migrate/20150309023307_add_detailed_fields_to_venue.calagator.rb +20 -0
  264. data/spec/dummy/db/migrate/20150309023308_add_end_time_to_events.calagator.rb +10 -0
  265. data/spec/dummy/db/migrate/20150309023309_add_source_id_to_events.calagator.rb +10 -0
  266. data/spec/dummy/db/migrate/20150309023310_add_source_id_to_venues.calagator.rb +11 -0
  267. data/spec/dummy/db/migrate/20150309023311_add_duplicate_of_column_to_venues.calagator.rb +10 -0
  268. data/spec/dummy/db/migrate/20150309023312_add_duplicate_of_column_to_events.calagator.rb +10 -0
  269. data/spec/dummy/db/migrate/20150309023313_change_lat_long_type.calagator.rb +13 -0
  270. data/spec/dummy/db/migrate/20150309023314_add_source_reimport.calagator.rb +10 -0
  271. data/spec/dummy/db/migrate/20150309023315_change_end_time_to_duration.calagator.rb +12 -0
  272. data/spec/dummy/db/migrate/20150309023316_remove_format_type_from_source.calagator.rb +10 -0
  273. data/spec/dummy/db/migrate/20150309023317_create_updates.calagator.rb +16 -0
  274. data/spec/dummy/db/migrate/20150309023318_remove_next_update_from_source.calagator.rb +10 -0
  275. data/spec/dummy/db/migrate/20150309023319_change_duration_to_end_time.calagator.rb +12 -0
  276. data/spec/dummy/db/migrate/20150309023320_create_tags_and_taggings.calagator.rb +29 -0
  277. data/spec/dummy/db/migrate/20150309023321_create_versioned_events.calagator.rb +26 -0
  278. data/spec/dummy/db/migrate/20150309023322_create_versioned_venues.calagator.rb +33 -0
  279. data/spec/dummy/db/migrate/20150309023323_add_rrule_to_events.calagator.rb +16 -0
  280. data/spec/dummy/db/migrate/20150309023324_create_versions.calagator.rb +19 -0
  281. data/spec/dummy/db/migrate/20150309023325_add_closed_flag_to_venues.calagator.rb +10 -0
  282. data/spec/dummy/db/migrate/20150309023326_add_wifi_flag_to_venues.calagator.rb +10 -0
  283. data/spec/dummy/db/migrate/20150309023327_add_access_notes_to_venues.calagator.rb +10 -0
  284. data/spec/dummy/db/migrate/20150309023328_add_events_count_to_venues.calagator.rb +9 -0
  285. data/spec/dummy/db/migrate/20150309023329_add_venue_details_to_events.calagator.rb +10 -0
  286. data/spec/dummy/db/migrate/20150309023330_acts_as_taggable_on_migration.calagator.rb +51 -0
  287. data/spec/dummy/db/migrate/20150309023331_cleanup.calagator.rb +15 -0
  288. data/spec/dummy/db/migrate/20150309023332_specify_venues_latitude_and_longitude_precision.calagator.rb +12 -0
  289. data/spec/dummy/db/migrate/20150309023333_remove_updates.calagator.rb +14 -0
  290. data/spec/dummy/db/migrate/20150309023334_add_locked_status_to_events.calagator.rb +6 -0
  291. data/spec/dummy/db/schema.rb +95 -0
  292. data/spec/dummy/db/seeds.rb +7 -0
  293. data/spec/dummy/db/test.sqlite3 +0 -0
  294. data/spec/dummy/doc/README_FOR_APP +2 -0
  295. data/spec/dummy/log/development.log +273 -0
  296. data/spec/dummy/public/404.html +26 -0
  297. data/spec/dummy/public/422.html +26 -0
  298. data/spec/dummy/public/500.html +25 -0
  299. data/spec/dummy/public/favicon.ico +0 -0
  300. data/spec/dummy/public/robots.txt +5 -0
  301. data/spec/dummy/script/rails +6 -0
  302. data/spec/dummy/test/performance/browsing_test.rb +12 -0
  303. data/spec/dummy/test/test_helper.rb +13 -0
  304. data/spec/factories.rb +93 -0
  305. data/spec/features/add_event_spec.rb +99 -0
  306. data/spec/features/add_venue_spec.rb +34 -0
  307. data/spec/features/admin_auth_spec.rb +22 -0
  308. data/spec/features/admin_lock_event_spec.rb +41 -0
  309. data/spec/features/import_events_from_feed_spec.rb +43 -0
  310. data/spec/features/managing_event_spec.rb +111 -0
  311. data/spec/features/managing_venue_spec.rb +71 -0
  312. data/spec/features/search_event_spec.rb +27 -0
  313. data/spec/helpers/calagator/application_helper_spec.rb +82 -0
  314. data/spec/helpers/calagator/events_helper_spec.rb +172 -0
  315. data/spec/helpers/calagator/google_event_export_helper_spec.rb +70 -0
  316. data/spec/helpers/calagator/sources_helper_spec.rb +12 -0
  317. data/spec/helpers/calagator/tags_helper_spec.rb +85 -0
  318. data/spec/helpers/calagator/time_range_helper_spec.rb +59 -0
  319. data/spec/lib/calagator/blacklist_validator_spec.rb +65 -0
  320. data/spec/lib/calagator/decode_html_entities_hack_spec.rb +54 -0
  321. data/spec/lib/calagator/settings_spec.rb +20 -0
  322. data/spec/lib/calagator/url_prefixer_spec.rb +33 -0
  323. data/spec/lib/secrets_reader_spec.rb +65 -0
  324. data/spec/models/calagator/event/cloner_spec.rb +43 -0
  325. data/spec/models/calagator/event/overview_spec.rb +79 -0
  326. data/spec/models/calagator/event/search_spec.rb +103 -0
  327. data/spec/models/calagator/event_search_spec.rb +149 -0
  328. data/spec/models/calagator/event_spec.rb +859 -0
  329. data/spec/models/calagator/source/parser_facebook_spec.rb +73 -0
  330. data/spec/models/calagator/source/parser_hcal_spec.rb +69 -0
  331. data/spec/models/calagator/source/parser_ical_non_standard_spec.rb +91 -0
  332. data/spec/models/calagator/source/parser_ical_spec.rb +322 -0
  333. data/spec/models/calagator/source/parser_meetup_spec.rb +69 -0
  334. data/spec/models/calagator/source/parser_plancast_spec.rb +53 -0
  335. data/spec/models/calagator/source/parser_spec.rb +238 -0
  336. data/spec/models/calagator/source_spec.rb +135 -0
  337. data/spec/models/calagator/venue/search_spec.rb +92 -0
  338. data/spec/models/calagator/venue_search_spec.rb +124 -0
  339. data/spec/models/calagator/venue_spec.rb +346 -0
  340. data/spec/models/tag_spec.rb +35 -0
  341. data/spec/rails_helper.rb +39 -0
  342. data/spec/spec_helper.rb +140 -0
  343. data/spec/support/disable_geocoding.rb +1 -0
  344. data/spec/support/http_samples.rb +5 -0
  345. data/spec/support/samples/facebook.json +23 -0
  346. data/spec/support/samples/hcal_basic.xml +6 -0
  347. data/spec/support/samples/hcal_dup_event_dup_venue.xml +13 -0
  348. data/spec/support/samples/hcal_event_duplicates_fixture.xml +13 -0
  349. data/spec/support/samples/hcal_event_wo_lat_and_long.xml +14 -0
  350. data/spec/support/samples/hcal_multiple.xml +16 -0
  351. data/spec/support/samples/hcal_same_event_twice_with_different_venues.xml +12 -0
  352. data/spec/support/samples/hcal_single.xml +8 -0
  353. data/spec/support/samples/hcal_two_identical_events.xml +14 -0
  354. data/spec/support/samples/hcal_upcoming_v1.html +412 -0
  355. data/spec/support/samples/hcal_upcoming_v2.html +749 -0
  356. data/spec/support/samples/hcal_upcoming_v3.html +685 -0
  357. data/spec/support/samples/hcal_upcoming_v4.html +761 -0
  358. data/spec/support/samples/ical_apple.ics +22 -0
  359. data/spec/support/samples/ical_apple_v3.ics +37 -0
  360. data/spec/support/samples/ical_basic.ics +15 -0
  361. data/spec/support/samples/ical_basic_with_duration.ics +16 -0
  362. data/spec/support/samples/ical_event_with_squashed_venue.ics +12 -0
  363. data/spec/support/samples/ical_eventful_many.ics +504 -0
  364. data/spec/support/samples/ical_gmt.ics +12 -0
  365. data/spec/support/samples/ical_google.ics +786 -0
  366. data/spec/support/samples/ical_multiple_calendars.ics +111 -0
  367. data/spec/support/samples/ical_upcoming.ics +36 -0
  368. data/spec/support/samples/ical_upcoming_many.ics +682 -0
  369. data/spec/support/samples/ical_upcoming_v2.ics +43 -0
  370. data/spec/support/samples/ical_z.ics +10 -0
  371. data/spec/support/samples/meetup.ics +16 -0
  372. data/spec/support/samples/meetup.json +31 -0
  373. data/spec/support/samples/plancast.ics +59 -0
  374. data/spec/support/samples/plancast.json +51 -0
  375. data/spec/support/samples/plancast_with_missing_venue.json +39 -0
  376. data/spec/support/samples/upcoming_v1.xml +8 -0
  377. data/spec/support/samples/upcoming_v2_with_invalid_utc_dates.xml +8 -0
  378. data/spec/support/time_convenience_methods.rb +8 -0
  379. data/spec/support/time_zones.rb +12 -0
  380. data/spec/support/url_helpers.rb +5 -0
  381. data/spec/support/wait_for_ajax.rb +15 -0
  382. data/spec/support/webmock.rb +8 -0
  383. data/spec/travis_spec.rb +7 -0
  384. metadata +1194 -0
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <kml xmlns="http://earth.google.com/kml/2.2">
3
+ <Document>
4
+ <% for event in @events %>
5
+ <% next if event.venue.nil? %>
6
+ <% next if event.venue.latitude.nil? %>
7
+ <Placemark>
8
+ <name><%= escape_once(event.title) %></name>
9
+ <description><%= cdata_section("#{event.description}<p>Venue: #{event.venue.title}</p>") %></description>
10
+ <Point>
11
+ <coordinates><%= event.venue.longitude %>,<%= event.venue.latitude %></coordinates>
12
+ </Point>
13
+ <TimeSpan>
14
+ <begin><%= event.start_time.xmlschema %></begin>
15
+ <end><%= event.end_time.nil? ? (event.start_time+1.hours).xmlschema : event.end_time.xmlschema %></end>
16
+ </TimeSpan>
17
+ </Placemark>
18
+ <% end %>
19
+ </Document>
20
+ </kml>
@@ -0,0 +1,7 @@
1
+ <%= content_for :title, "Add an Event" %>
2
+
3
+ <div id="new_event_form">
4
+ <%= render :partial => 'calagator/site/appropriateness_message' %>
5
+
6
+ <%= render :partial => 'form', :locals => {:event => @event} %>
7
+ </div>
@@ -0,0 +1,25 @@
1
+ <%= content_for :title do %>
2
+ <%= @search.tag ? "Events tagged with: #{@search.tag}" : "Search Results for: #{@search.query}" %>
3
+ <% end %>
4
+
5
+ <% tabindex_on '#search_field' %>
6
+
7
+ <% content_for :discovery_insert do %>
8
+ <%= auto_discovery_link_tag(:atom, atom_feed_link(query: @search.query, tag: @search.tag), title: "Atom: Search Results for '#{@search.query}'" )%>
9
+ <% end -%>
10
+ <%= render partial: 'search_section', locals: { search: @search, section: :current } %>
11
+ <%= render partial: 'search_section', locals: { search: @search, section: :past } unless @search.current %>
12
+
13
+ <div id='list_filters' class='sidebar'>
14
+ <h3>Subscribe to</h3>
15
+ <ul>
16
+ <li><%= link_to "iCalendar feed", icalendar_feed_link(query: @search.query, tag: @search.tag) %></li>
17
+ <li><%= link_to "Atom feed", atom_feed_link(query: @search.query, tag: @search.tag) %></li>
18
+ <li><%= link_to "Google Calendar", google_events_subscription_link(query: @search.query, tag: @search.tag) %></li>
19
+ </ul>
20
+
21
+ <h3>Export to</h3>
22
+ <ul>
23
+ <li><%= link_to "iCalendar file", icalendar_export_link(query: @search.query, tag: @search.tag) %></li>
24
+ </ul>
25
+ </div>
@@ -0,0 +1,81 @@
1
+ <%= content_for :title, @event.title %>
2
+
3
+ <% tabindex_on '#search_field' %>
4
+
5
+ <% content_for :discovery_insert do %>
6
+ <link rel="alternate" type="application/x-wiki" title="Edit this page" href="<%= edit_event_url(@event) %>" />
7
+ <% end %>
8
+
9
+ <% content_for :open_graph_tags do %>
10
+ <meta property="og:title" content="<%= @event.title %>" />
11
+ <meta property="og:description" content="<%= @event.description %>" />
12
+ <meta property="og:site_name" content="<%= Calagator.name %>" />
13
+ <meta property="og:url" content="<%= event_url(@event) %>">
14
+ <% end %>
15
+
16
+ <% content_for :javascript_insert do %>
17
+ <%= javascript_include_tag 'mustache' %>
18
+ <script type='text/javascript'>
19
+ $(function() {
20
+ var plan = $('.external.plancast.plan:first');
21
+ if (plan.length > 0) {
22
+ var plan_id = plan.text().split('=')[1];
23
+ $.getJSON("http://api.plancast.com/02/plans/show.json?jsonp=?",
24
+ { extensions: "attendees",
25
+ plan_id: plan_id },
26
+ function(data) {
27
+ if (data.attendees.length > 0) {
28
+ $('.attendees').append(Mustache.to_html($('#attendees-template').html(), data));
29
+ }
30
+ }
31
+ );
32
+ }
33
+ })
34
+ </script>
35
+ <% end %>
36
+
37
+
38
+ <a href="#" id="event_sidebar_toggle" class="expander_toggle">Export or edit this event...</a>
39
+
40
+ <div id="event_sidebar" class='single_view_right sidebar'>
41
+ <h3 class='first'>Export to</h3>
42
+ <ul>
43
+ <li><%= link_to "Google Calendar", google_event_export_link(@event), :id => "google_calendar_export", :target => "_blank" %></li>
44
+ <li><%= link_to 'iCalendar file', "#{event_url(@event)}.ics" %></li>
45
+ <li>
46
+ <%= link_to_function 'hCalendar markup', '$("#hcal_field").toggle()' %>
47
+ <div id="hcal_field" style="display: none;">
48
+ <%= text_area_tag 'hCalendar',
49
+ h(
50
+ render :partial => 'hcal',
51
+ :locals => {
52
+ :event => @event,
53
+ :show_year => true
54
+ },
55
+ :formats => [:html]
56
+ ), :rows => 10, :cols => 80 %>
57
+ </div>
58
+ </li>
59
+ </ul>
60
+ <hr />
61
+ <div id='edit_link'>
62
+ <% if @event.locked %>
63
+ <p>This event is currently locked and cannot be edited. Please contact an admin for assistance.</p>
64
+ <% else %>
65
+ <p>
66
+ You can
67
+ <%= link_to "edit", edit_event_url(@event) %>,
68
+ <%= link_to "clone", clone_event_url(@event) %>,
69
+ or
70
+ <%= link_to "delete", event_url(@event), :method => :delete, :confirm => "Are you sure?" %>
71
+ this event.
72
+ </p>
73
+ <% end %>
74
+ </div>
75
+ <hr />
76
+ <div id='metadata'>
77
+ <%= datestamp(@event) %>
78
+ </div>
79
+ </div>
80
+
81
+ <%= render :partial => 'item', :locals => {:event => @event} %>
@@ -0,0 +1,15 @@
1
+ <%#
2
+ USAGE: Display message describing appropriate content for this site
3
+
4
+ This message is shown on the "Add event" and "Import event(s)" pages. The appropriateness text is stored in a theme-specific partial and will only be displayed if found.
5
+
6
+ The theme-specific partial is retrieved from `themes/YOUR_THEME/views/site/_appropriateness`. You can find the `default` theme's appropriateness message partial at `themes/default/views/site/_appropriateness.html.erb` if you'd like to see an example.
7
+ %>
8
+ <div class="appropriateness_message">
9
+ <%- begin -%>
10
+ <%= render :partial => 'site/appropriateness' %>
11
+ <%- rescue ActionView::MissingTemplate => e -%>
12
+ <%#- No theme-specific appropriatenes partial was found. -%>
13
+ Please only add or import events that are of interest to our community. The more information you provide about the event, the easier it will be for people to find and choose to attend it.
14
+ <%- end -%>
15
+ </div>
@@ -0,0 +1,3 @@
1
+ <p class="replace-this-text">
2
+ Describe your site in: <code>themes/YOUR_THEME/views/site/_description.html.erb</code>
3
+ </p>
@@ -0,0 +1,7 @@
1
+ <div class='copy_block'>
2
+ <h1 id='about'>About us</a></h1>
3
+
4
+ <p class="replace-this-text">
5
+ Describe your project in: <code>themes/YOUR_THEME/views/site/about.html.erb</code>
6
+ </p>
7
+ </div>
@@ -0,0 +1,15 @@
1
+ <div class='copy_block'>
2
+ <h1>You've found the missing link!</a></h1>
3
+
4
+ <p class="replace-this-text">
5
+ Unfortunately, the tag you clicked points to a site or service that no longer exists. Lucky for you, nothing ever disappears from the Internet!
6
+ </p>
7
+
8
+ <p>
9
+ Here's the most recent viewable version of the page you were looking for, courtesy of the <%= link_to 'Wayback Machine', 'https://archive.org/web/' %>:
10
+ </p>
11
+
12
+ <p>
13
+ <%= link_to params[:url], params[:url] %>
14
+ </p>
15
+ </div>
@@ -0,0 +1,4 @@
1
+ Export formats:
2
+ <ul>
3
+ <li><%= link_to "sqlite3", '/export.sqlite3' %></li>
4
+ </ul>
@@ -0,0 +1,40 @@
1
+ <% tabindex_on '#search_field' %>
2
+
3
+ <% cache(Calagator::CacheObserver.daily_key_for("site_index", request)) do %>
4
+ <a href="#" id="homepage_sidebar_toggle" class="expander_toggle">Hi there. Learn about this site and how to use it...</a>
5
+ <div id="homepage_sidebar">
6
+ <div id="project_description">
7
+ <%= render :partial => "description" %>
8
+ </div>
9
+
10
+ <% if @overview.tags.present? %>
11
+ <div id="tagcloud">
12
+ <h2>Popular tags</h2>
13
+ <% tag_cloud @overview.tags, %w(tagcloud_level_0 tagcloud_level_1 tagcloud_level_2 tagcloud_level_3 tagcloud_level_4) do |tag, css_class| %>
14
+ <%= link_to tag.name, search_events_path(tag: tag.name), class: css_class %>
15
+ <span class="spacer">&middot;</span>
16
+ <% end %>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+
21
+ <div id="whats_happening">
22
+ <h1>What's happening?</h1>
23
+ <h3><%= link_to("View All &raquo;".html_safe, events_path(:anchor => "event-#{@overview.more.id}")) if @overview.more %></h3>
24
+ <div class='coming_events' id='today'>
25
+ <h3>Today</h3>
26
+ <%= render 'calagator/events/list', :events => @overview.today, :dates => false %>
27
+ </div>
28
+ <div class='coming_events' id='tomorrow'>
29
+ <h3>Tomorrow</h3>
30
+ <%= render 'calagator/events/list', :events => @overview.tomorrow, :dates => false %>
31
+ </div>
32
+ <div id='next_two_weeks'>
33
+ <h3>Next two weeks</h3>
34
+ <%= render 'calagator/events/list', :events => @overview.later %>
35
+ </div>
36
+ <div>
37
+ <h3><%= link_to("View future events &raquo;".html_safe, events_path(:anchor => "event-#{@overview.more.id}")) if @overview.more %></h3>
38
+ </div>
39
+ </div>
40
+ <% end %>
@@ -0,0 +1,8 @@
1
+ xml.instruct!
2
+ xml.OpenSearchDescription('xmlns' => 'http://a9.com/-/spec/opensearch/1.1/', 'xmlns:moz' => 'http://www.mozilla.org/2006/browser/search/') do
3
+ xml.ShortName Calagator.title
4
+ xml.Description "Search #{Calagator.title}"
5
+ xml.InputEncoding "UTF-8"
6
+ # The sub call at the end of this line is because we want to use the rails URL helper, but don't want to urlencode the curly braces.
7
+ xml.Url('type' => 'text/html', 'method' => 'get', 'template' => search_events_url(:query => "searchTerms").sub('searchTerms', '{searchTerms}') )
8
+ end
@@ -0,0 +1,12 @@
1
+ <h1>Editing source</h1>
2
+
3
+ <%= semantic_form_for(@source) do |f| %>
4
+ <%= f.inputs do %>
5
+ <%= f.input :url %>
6
+ <%= f.input :reimport, :label => "Re-import?" %>
7
+ <% end %>
8
+ <%= f.actions :submit %>
9
+ <% end %>
10
+
11
+ <%= link_to 'Show', @source %> |
12
+ <%= link_to 'Back', sources_path %>
@@ -0,0 +1,10 @@
1
+ <p>Imported <%= @importer.events.size %> entries:</p>
2
+ <ul>
3
+ <% @importer.events.take(5).each do |event| %>
4
+ <li><%= link_to event.title, event_url(event) %></li>
5
+ <% end %>
6
+ <% if @importer.events.size > 5 %>
7
+ <li>And <%= @importer.events.size - 5 %> other events.</li>
8
+ <% end %>
9
+ </ul>
10
+
@@ -0,0 +1,22 @@
1
+ <h1>Listing sources</h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Manage</th>
6
+ <th>URL</th>
7
+ </tr>
8
+
9
+ <% for source in @sources %>
10
+ <tr class="<%= cycle 'odd', 'even' %>">
11
+ <td>
12
+ <%= link_to 'Show', source %> |
13
+ <%= link_to 'Destroy', source, :confirm => 'Are you sure?', :method => :delete %>
14
+ </td>
15
+ <td><%= source_url_link source %></td>
16
+ </tr>
17
+ <% end %>
18
+ </table>
19
+
20
+ <br />
21
+
22
+ <%= will_paginate @sources %>
@@ -0,0 +1,32 @@
1
+ <%= render :partial => 'calagator/site/appropriateness_message' %>
2
+
3
+ <%= semantic_form_for(@source, :html => {:id => 'source_form', :class => 'standard_form'}, :url => { :action => :import }) do |f| %>
4
+ <%= f.inputs :name => "Import a source" do %>
5
+ <li class='input'>
6
+ <p class='inline-hints'>
7
+ Import one or more events from a feed or web page in one of the following formats:
8
+ <%= Calagator::Source::Parser.labels.to_sentence %>
9
+ </p>
10
+ </li>
11
+ <%= f.input :url, :label => "URL" %>
12
+ <% focus_on '#source_url' %>
13
+
14
+ <%# FIXME Igal has disabled reimport UI because people are adding almost all new events with re-import, which means that they're almost all generating duplicates and I'm completely overwhelmed with having to use the horrible squashing UI to deal with them. %>
15
+ <% if false %>
16
+ <%= f.input :reimport, :label => "Re-import?", :hint => "(Regularly check this source and import new events automatically?)" %>
17
+ <% end %>
18
+ <% end %>
19
+
20
+ <%= f.actions do %>
21
+ <%= f.action :submit, :label => "Import" %>
22
+ <% end %>
23
+
24
+ <%= f.inputs do %>
25
+ <li class='input'>
26
+ <p class='inline-hints'>
27
+ <b>Bookmarklet</b>: Import pages faster by adding the following bookmark to your browser's bookmarks and click it when you're visiting a webpage that contains events that you'd like to import:
28
+ <%= link_to "Add to #{Calagator.title}", "javascript:d=document;q=(d.location.href);w=window;location.href='#{new_source_url}?url='+escape(q);" %>
29
+ </p>
30
+ </li>
31
+ <% end %>
32
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <% content_for :discovery_insert do %>
2
+ <link rel="alternate" type="application/x-wiki" title="Edit this page" href="<%= edit_source_url(@source) %>" />
3
+ <% end %>
4
+
5
+ <p>
6
+ <b>URL:</b>
7
+ <%= source_url_link @source %>
8
+ </p>
9
+
10
+ <p>
11
+ <b>Events:</b>
12
+ <%= render :partial => 'events/list', :locals => { :events => @source.events } %>
13
+ </p>
14
+
15
+ <%= link_to 'Back', sources_path %> |
16
+ <%= link_to 'Destroy', @source, :confirm => 'Are you sure?', :method => :delete %>
@@ -0,0 +1,57 @@
1
+ <%= semantic_form_for(venue, :html => {:id => 'venue_form', :class => 'standard_form', :novalidate => 'novalidate'}) do |f| %>
2
+ <%= f.inputs :name => "#{'Editing: ' unless venue.new_record?} <em>#{venue.title}</em>".html_safe do %>
3
+ <li class='input'>
4
+ <p class='inline-hints'>You can <%= venue.new_record? ? "enter" : "edit" %> the details of your venue below. The more information we have about your venue, the easier it will be for people to find it.</p>
5
+ </li>
6
+ <%= f.input :title, :label => "Venue Name" %>
7
+ <% focus_on '#venue_title' %>
8
+ <%= f.input :address, :label => "Full Address", :hint => "(one-line version of full address)" %>
9
+
10
+ <% unless venue.new_record? %>
11
+ <%= f.input :street_address %>
12
+ <%= f.input :locality, :label => "City" %>
13
+ <%= f.input :region, :label => "State" %>
14
+ <%= f.input :postal_code, :label => "Zip Code" %>
15
+ <%= f.input :country, :label => "Country", :as => :text %>
16
+ <%= f.input :latitude, :input_html => { :maxlength => 20 } %>
17
+ <%= f.input :longitude, :input_html => { :maxlength => 20 } %>
18
+ <% end %>
19
+
20
+ <%= f.input :url, :label => "Web Link" %>
21
+ <%= f.input :email %>
22
+ <%= f.input :telephone %>
23
+ <%= f.input :wifi, :label => "This venue has public wireless internet access" %>
24
+ <%= f.input :description,
25
+ :input_html => {:rows => 12},
26
+ :hint => "(Format text using #{link_to("Markdown", "http://www.simpleeditions.com/59001/markdown-an-introduction", :target => "_blank")} and HTML)".html_safe %>
27
+ <%= f.input :access_notes,
28
+ :input_html => {:rows => 4},
29
+ :hint => "(Details about how to access the space, and any accessibility restrictions that visitors may want to know about.)" %>
30
+
31
+ <%= f.input(:closed, :label => "This venue is no longer open for business") unless venue.new_record? %>
32
+
33
+ <%= f.input :tag_list, :required => false,
34
+ :label => 'Tags',
35
+ :hint => "(Tags are comma-separated keywords that make it easier to find your event and boost its position in searches. You can also use a tag like <code>plancast:plan=ABCD</code> to associate this with a particular <a href='http://plancast.com/'>Plancast</a> event, or a tag like <code>epdx:group=1234</code> to associate the event with a particular <a href='http://epdx.org/'>ePDX</a> group)".html_safe %>
36
+
37
+ <% unless venue.new_record? %>
38
+ <%= f.input :force_geocoding,
39
+ :label => venue.location ? "Re-geocode?" : "Geocode?",
40
+ :as => :boolean,
41
+ :hint => venue.location \
42
+ ? "([#{venue.latitude}, #{venue.longitude}] currently; if checked, we'll try again.)" \
43
+ : "(if checked, we'll try to determine a map position for this venue.)" %>
44
+ <% end %>
45
+
46
+ <li class='trap'>
47
+ <%= label 'trap', 'field', "Leave this field blank or we'll think you're a robot." %>
48
+ <%= text_field_tag 'trap_field', params[:trap_field] %>
49
+ </li>
50
+
51
+ <% if !params[:from_event].blank? %>
52
+ <%= hidden_field_tag(:from_event, params[:from_event]) %>
53
+ <% end %>
54
+ <% end %>
55
+
56
+ <%= f.actions :submit %>
57
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <div id="venues_subnav" class="subnav">
2
+ <ul class="clearfix">
3
+ <li><%= link_to 'Browse venues', venues_url,
4
+ :class => subnav_class_for("venues", "index") %>
5
+ </li>
6
+ <li><%= link_to 'Add a venue', new_venue_url,
7
+ :class => subnav_class_for("venues", "new") %>
8
+ </li>
9
+ </ul>
10
+ </div>
@@ -0,0 +1,65 @@
1
+ <%= content_for :title, "Duplicate Venue Squasher" %>
2
+
3
+ <div id="venues_duplicates">
4
+ <h1>Duplicate Venues</h1>
5
+
6
+ <div id='list_filters' class='sidebar'>
7
+ <h3>Show venues with a duplicate</h3>
8
+ <ul>
9
+ <li><%= link_to_unless_current "Title", url_for(:action => 'duplicates', :type => 'title') %></li>
10
+ <li><%= link_to_unless_current "Address", url_for(:action => 'duplicates', :type => 'address') %></li>
11
+ <li><%= link_to_unless_current "Street Address", url_for(:action => 'duplicates', :type => 'street_address') %></li>
12
+ <li><%= link_to_unless_current "Postal Code", url_for(:action => 'duplicates', :type => 'postal_code') %></li>
13
+ <li><%= link_to_unless_current "URL", url_for(:action => 'duplicates', :type => 'url') %></li>
14
+ <li><%= link_to_unless_current "Description", url_for(:action => 'duplicates', :type => 'description') %></li>
15
+ <li><%= link_to_unless_current "Geo. Coords", url_for(:action => 'duplicates', :type => 'latitude,longitude') %></li>
16
+ <li><%= link_to_unless_current "Email", url_for(:action => 'duplicates', :type => 'email') %></li>
17
+ <li><%= link_to_unless_current "Telephone", url_for(:action => 'duplicates', :type => 'telephone') %></li>
18
+ <li>-----</li>
19
+ <li><%= link_to_unless_current "Any Field", url_for(:action => 'duplicates', :type => 'any') %></li>
20
+ <li><%= link_to_unless_current "All Fields", url_for(:action => 'duplicates', :type => 'all') %></li>
21
+ </ul>
22
+ <h3>List venues</h3>
23
+ <ul>
24
+ <li><%= link_to_unless_current "All Venues", url_for(:action => 'duplicates', :type => 'na') %></li>
25
+ </ul>
26
+ </div>
27
+
28
+ <% unless @grouped_venues.empty? %>
29
+ <%= form_tag :action => "squash_many_duplicates", :method => :post %>
30
+ <div class="flash notice">
31
+ Select only one set of master and duplicate venues. If you select an unrelated venue, it will be merged into the same single master record.
32
+ </div>
33
+ <div class='list_items'>
34
+ <%= hidden_field_tag(:type, @type) %>
35
+ <table>
36
+ <% for value, group in @grouped_venues %>
37
+ <tr>
38
+ <th>Master?</th><th>Duplicate?</th><th># of Events</th><th>Venue<%= " (#{value})" unless value.blank? %></th>
39
+ </tr>
40
+ <% for venue in group %>
41
+ <tr class="<%= cycle('odd', 'even') %>">
42
+ <td><%= radio_button_tag("master_id", venue.id) %></td>
43
+ <td><%= check_box_tag("duplicate_id_#{venue.id}", venue.id) %></td>
44
+ <td><%= venue.events_count %></td>
45
+ <td>
46
+ <%= link_to(venue.title, venue_url(venue)) %>
47
+ <% if venue.has_full_address? %>
48
+ at <%= venue.full_address %>
49
+ <% end %>
50
+ </td>
51
+ </tr>
52
+ <% end %>
53
+ <% end %>
54
+ </table>
55
+ <ul>
56
+ <li>Checked items will be deleted.</li>
57
+ <li>Events associated with deleted venues will be replaced with the venues selected using radio buttons.</li>
58
+ </ul>
59
+ <%= submit_tag "Squash Duplicates" %>
60
+ </div>
61
+ </form>
62
+ <% else %>
63
+ No duplicates found
64
+ <% end %>
65
+ </div>