calagator 0.0.1.pre1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +23 -0
- data/README.md +75 -0
- data/Rakefile +12 -0
- data/app/assets/images/confused-alligator-sm.png +0 -0
- data/app/assets/images/disk.png +0 -0
- data/app/assets/images/edit.png +0 -0
- data/app/assets/images/external_sites/epdx.png +0 -0
- data/app/assets/images/external_sites/external.gif +0 -0
- data/app/assets/images/external_sites/facebook.png +0 -0
- data/app/assets/images/external_sites/foursquare.png +0 -0
- data/app/assets/images/external_sites/gowalla.png +0 -0
- data/app/assets/images/external_sites/lanyrd.png +0 -0
- data/app/assets/images/external_sites/meetup.png +0 -0
- data/app/assets/images/external_sites/plancast.png +0 -0
- data/app/assets/images/external_sites/shizzow.png +0 -0
- data/app/assets/images/external_sites/upcoming.png +0 -0
- data/app/assets/images/external_sites/yelp.png +0 -0
- data/app/assets/images/feed.png +0 -0
- data/app/assets/images/heart.png +0 -0
- data/app/assets/images/icon_ical.gif +0 -0
- data/app/assets/images/information.png +0 -0
- data/app/assets/images/nav_marker.gif +0 -0
- data/app/assets/images/nav_marker.png +0 -0
- data/app/assets/images/plus.png +0 -0
- data/app/assets/images/redx.png +0 -0
- data/app/assets/images/site-icon.png +0 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/images/star.png +0 -0
- data/app/assets/images/subnav_marker.gif +0 -0
- data/app/assets/images/subnav_marker.png +0 -0
- data/app/assets/images/tag_blue.png +0 -0
- data/app/assets/images/tag_icons/angular.png +0 -0
- data/app/assets/images/tag_icons/beer.png +0 -0
- data/app/assets/images/tag_icons/bitcoin.png +0 -0
- data/app/assets/images/tag_icons/free.png +0 -0
- data/app/assets/images/tag_icons/golang.png +0 -0
- data/app/assets/images/tag_icons/html.png +0 -0
- data/app/assets/images/tag_icons/javascript.png +0 -0
- data/app/assets/images/tag_icons/linux.png +0 -0
- data/app/assets/images/tag_icons/php.png +0 -0
- data/app/assets/images/tag_icons/pizza.png +0 -0
- data/app/assets/images/tag_icons/python.png +0 -0
- data/app/assets/images/tag_icons/rails.png +0 -0
- data/app/assets/images/tag_icons/ruby.png +0 -0
- data/app/assets/images/tag_icons/sass.png +0 -0
- data/app/assets/images/tag_icons/swift.png +0 -0
- data/app/assets/images/transmit_blue.png +0 -0
- data/app/assets/images/weekday_background.gif +0 -0
- data/app/assets/javascripts/calagator.js +18 -0
- data/app/assets/javascripts/calagator/forms.js +60 -0
- data/app/assets/stylesheets/calagator.scss +8 -0
- data/app/assets/stylesheets/calagator/common.scss +14 -0
- data/app/assets/stylesheets/calagator/datepicker.scss +177 -0
- data/app/assets/stylesheets/calagator/errors.css +110 -0
- data/app/assets/stylesheets/calagator/forms.scss +4 -0
- data/app/assets/stylesheets/calagator/reset.css +40 -0
- data/app/assets/stylesheets/calagator/theme.css +0 -0
- data/app/controllers/calagator/admin_controller.rb +27 -0
- data/app/controllers/calagator/application_controller.rb +82 -0
- data/app/controllers/calagator/events_controller.rb +171 -0
- data/app/controllers/calagator/site_controller.rb +35 -0
- data/app/controllers/calagator/sources_controller.rb +94 -0
- data/app/controllers/calagator/venues_controller.rb +133 -0
- data/app/controllers/calagator/versions_controller.rb +20 -0
- data/app/helpers/calagator/application_helper.rb +128 -0
- data/app/helpers/calagator/events_helper.rb +184 -0
- data/app/helpers/calagator/google_event_export_helper.rb +67 -0
- data/app/helpers/calagator/mapping_helper.rb +103 -0
- data/app/helpers/calagator/sources_helper.rb +10 -0
- data/app/helpers/calagator/tags_helper.rb +42 -0
- data/app/helpers/calagator/time_range_helper.rb +166 -0
- data/app/models/calagator/event.rb +246 -0
- data/app/models/calagator/event/cloner.rb +39 -0
- data/app/models/calagator/event/ical_renderer.rb +155 -0
- data/app/models/calagator/event/overview.rb +45 -0
- data/app/models/calagator/event/saver.rb +58 -0
- data/app/models/calagator/event/search.rb +72 -0
- data/app/models/calagator/event/search_engine.rb +28 -0
- data/app/models/calagator/event/search_engine/apache_sunspot.rb +106 -0
- data/app/models/calagator/event/search_engine/sql.rb +107 -0
- data/app/models/calagator/source.rb +115 -0
- data/app/models/calagator/source/importer.rb +46 -0
- data/app/models/calagator/source/parser.rb +128 -0
- data/app/models/calagator/source/parser/facebook.rb +67 -0
- data/app/models/calagator/source/parser/hcal.rb +108 -0
- data/app/models/calagator/source/parser/http_authentication_required_error.rb +6 -0
- data/app/models/calagator/source/parser/ical.rb +186 -0
- data/app/models/calagator/source/parser/meetup.rb +72 -0
- data/app/models/calagator/source/parser/not_found.rb +9 -0
- data/app/models/calagator/source/parser/plancast.rb +59 -0
- data/app/models/calagator/venue.rb +161 -0
- data/app/models/calagator/venue/geocoder.rb +51 -0
- data/app/models/calagator/venue/search.rb +63 -0
- data/app/models/calagator/venue/search_engine.rb +24 -0
- data/app/models/calagator/venue/search_engine/apache_sunspot.rb +85 -0
- data/app/models/calagator/venue/search_engine/sql.rb +68 -0
- data/app/models/event/search_engine/base.rb +0 -0
- data/app/observers/calagator/cache_observer.rb +37 -0
- data/app/views/calagator/admin/events.html.erb +28 -0
- data/app/views/calagator/admin/index.html.erb +8 -0
- data/app/views/calagator/events/_feed_item.html.erb +62 -0
- data/app/views/calagator/events/_form.html.erb +63 -0
- data/app/views/calagator/events/_gcal_reminder.html.erb +1 -0
- data/app/views/calagator/events/_hcal.html.erb +41 -0
- data/app/views/calagator/events/_item.html.erb +120 -0
- data/app/views/calagator/events/_list.html.erb +30 -0
- data/app/views/calagator/events/_list_item.html.erb +37 -0
- data/app/views/calagator/events/_search_section.html.erb +15 -0
- data/app/views/calagator/events/_subnav.html.erb +13 -0
- data/app/views/calagator/events/_table.html.erb +74 -0
- data/app/views/calagator/events/duplicates.html.erb +58 -0
- data/app/views/calagator/events/edit.html.erb +3 -0
- data/app/views/calagator/events/index.atom.builder +32 -0
- data/app/views/calagator/events/index.html.erb +51 -0
- data/app/views/calagator/events/index.kml.erb +20 -0
- data/app/views/calagator/events/new.html.erb +7 -0
- data/app/views/calagator/events/search.html.erb +25 -0
- data/app/views/calagator/events/show.html.erb +81 -0
- data/app/views/calagator/site/_appropriateness_message.html.erb +15 -0
- data/app/views/calagator/site/_description.html.erb +3 -0
- data/app/views/calagator/site/about.html.erb +7 -0
- data/app/views/calagator/site/defunct.html.erb +15 -0
- data/app/views/calagator/site/export.html.erb +4 -0
- data/app/views/calagator/site/index.html.erb +40 -0
- data/app/views/calagator/site/opensearch.xml.builder +8 -0
- data/app/views/calagator/sources/_subnav.html.erb +0 -0
- data/app/views/calagator/sources/edit.html.erb +12 -0
- data/app/views/calagator/sources/import.html.erb +10 -0
- data/app/views/calagator/sources/index.html.erb +22 -0
- data/app/views/calagator/sources/new.html.erb +32 -0
- data/app/views/calagator/sources/show.html.erb +16 -0
- data/app/views/calagator/venues/_form.html.erb +57 -0
- data/app/views/calagator/venues/_subnav.html.erb +10 -0
- data/app/views/calagator/venues/duplicates.html.erb +65 -0
- data/app/views/calagator/venues/edit.html.erb +3 -0
- data/app/views/calagator/venues/index.html.erb +93 -0
- data/app/views/calagator/venues/index.kml.erb +15 -0
- data/app/views/calagator/venues/map.html.erb +4 -0
- data/app/views/calagator/venues/new.html.erb +5 -0
- data/app/views/calagator/venues/show.html.erb +113 -0
- data/app/views/calagator/versions/_chooser.html.erb +28 -0
- data/app/views/calagator/versions/_edit_with_chooser.html.erb +16 -0
- data/app/views/layouts/calagator/application.html.erb +110 -0
- data/config/deploy/local.rb +37 -0
- data/config/deploy/lucca.rb +33 -0
- data/config/initializers/dates.rb +7 -0
- data/config/initializers/formtastic.rb +82 -0
- data/config/initializers/geokit.rb +74 -0
- data/config/initializers/ics_renderer.rb +6 -0
- data/config/initializers/load_tag_model_extensions.rb +3 -0
- data/config/initializers/mime_types.rb +9 -0
- data/config/initializers/observers.rb +1 -0
- data/config/initializers/search_engine.rb +4 -0
- data/config/initializers/set_default_url_host.rb +5 -0
- data/config/initializers/time_get_zone.rb +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +55 -0
- data/config/secrets.yml.blag +75 -0
- data/config/sunspot.yml +23 -0
- data/db/development.sqlite3 +0 -0
- data/db/development.sqlite3.bak +0 -0
- data/db/development.sqlite3.old +0 -0
- data/db/development~20111112@110950.sqlite3 +0 -0
- data/db/migrate/001_create_events.rb +17 -0
- data/db/migrate/002_create_venues.rb +17 -0
- data/db/migrate/003_create_sources.rb +16 -0
- data/db/migrate/004_add_detailed_fields_to_venue.rb +19 -0
- data/db/migrate/005_add_end_time_to_events.rb +9 -0
- data/db/migrate/006_add_source_id_to_events.rb +9 -0
- data/db/migrate/008_add_source_id_to_venues.rb +10 -0
- data/db/migrate/009_add_duplicate_of_column_to_venues.rb +9 -0
- data/db/migrate/010_add_duplicate_of_column_to_events.rb +9 -0
- data/db/migrate/011_change_lat_long_type.rb +12 -0
- data/db/migrate/012_add_source_reimport.rb +9 -0
- data/db/migrate/013_change_end_time_to_duration.rb +11 -0
- data/db/migrate/014_remove_format_type_from_source.rb +9 -0
- data/db/migrate/015_create_updates.rb +15 -0
- data/db/migrate/016_remove_next_update_from_source.rb +9 -0
- data/db/migrate/20080705163959_change_duration_to_end_time.rb +11 -0
- data/db/migrate/20080705164959_create_tags_and_taggings.rb +28 -0
- data/db/migrate/20081011181519_create_versioned_events.rb +25 -0
- data/db/migrate/20081011193124_create_versioned_venues.rb +32 -0
- data/db/migrate/20081115190515_add_rrule_to_events.rb +15 -0
- data/db/migrate/20090912082129_create_versions.rb +18 -0
- data/db/migrate/20110219205156_add_closed_flag_to_venues.rb +9 -0
- data/db/migrate/20110220001008_add_wifi_flag_to_venues.rb +9 -0
- data/db/migrate/20110220011427_add_access_notes_to_venues.rb +9 -0
- data/db/migrate/20110220031117_add_events_count_to_venues.rb +8 -0
- data/db/migrate/20110604174521_add_venue_details_to_events.rb +9 -0
- data/db/migrate/20110717231316_acts_as_taggable_on_migration.rb +50 -0
- data/db/migrate/20120709092821_cleanup.rb +14 -0
- data/db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb +11 -0
- data/db/migrate/20150206085809_remove_updates.rb +13 -0
- data/db/migrate/20150207231355_add_locked_status_to_events.rb +5 -0
- data/db/production.sqlite3 +0 -0
- data/db/schema.rb +102 -0
- data/db/seeds.rb +16 -0
- data/db/test.sqlite3 +0 -0
- data/db/test2.sqlite3 +0 -0
- data/lib/calagator.rb +30 -0
- data/lib/calagator/blacklist_validator.rb +69 -0
- data/lib/calagator/decode_html_entities_hack.rb +33 -0
- data/lib/calagator/duplicate_checking.rb +133 -0
- data/lib/calagator/duplicate_checking/controller_actions.rb +38 -0
- data/lib/calagator/duplicate_checking/duplicate_finder.rb +83 -0
- data/lib/calagator/duplicate_checking/duplicate_squasher.rb +74 -0
- data/lib/calagator/engine.rb +30 -0
- data/lib/calagator/strip_whitespace.rb +19 -0
- data/lib/calagator/tag_model_extensions.rb +104 -0
- data/lib/calagator/url_prefixer.rb +9 -0
- data/lib/calagator/version.rb +3 -0
- data/lib/generators/calagator/install_generator.rb +39 -0
- data/lib/generators/calagator/templates/config/calagator.rb +26 -0
- data/lib/generators/calagator/templates/config/secrets.yml.sample +83 -0
- data/lib/secrets_reader.rb +76 -0
- data/lib/tasks/spec_db.rake +53 -0
- data/lib/tasks/sunspot_reindex_calagator.rake +6 -0
- data/lib/tasks/sunspot_solr_restart_enhancements.rake +19 -0
- data/lib/tasks/update_counter_caches.rake +13 -0
- data/lib/templates/erb/scaffold/_form.html.erb +11 -0
- data/lib/theme_reader.rb +17 -0
- data/lib/wait_for_solr.rb +25 -0
- data/spec/controllers/calagator/application_controller_spec.rb +47 -0
- data/spec/controllers/calagator/events_controller_spec.rb +794 -0
- data/spec/controllers/calagator/site_controller_spec.rb +59 -0
- data/spec/controllers/calagator/sources_controller_spec.rb +439 -0
- data/spec/controllers/calagator/venues_controller_spec.rb +319 -0
- data/spec/controllers/calagator/versions_controller_spec.rb +82 -0
- data/spec/controllers/squash_many_duplicates_examples.rb +49 -0
- data/spec/dummy/Gemfile +39 -0
- data/spec/dummy/Gemfile.lock +195 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/dummy/app/assets/stylesheets/application.css +14 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +62 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/calagator.rb +26 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +59 -0
- data/spec/dummy/config/secrets.yml +83 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20150309023304_create_events.calagator.rb +18 -0
- data/spec/dummy/db/migrate/20150309023305_create_venues.calagator.rb +18 -0
- data/spec/dummy/db/migrate/20150309023306_create_sources.calagator.rb +17 -0
- data/spec/dummy/db/migrate/20150309023307_add_detailed_fields_to_venue.calagator.rb +20 -0
- data/spec/dummy/db/migrate/20150309023308_add_end_time_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023309_add_source_id_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023310_add_source_id_to_venues.calagator.rb +11 -0
- data/spec/dummy/db/migrate/20150309023311_add_duplicate_of_column_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023312_add_duplicate_of_column_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023313_change_lat_long_type.calagator.rb +13 -0
- data/spec/dummy/db/migrate/20150309023314_add_source_reimport.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023315_change_end_time_to_duration.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023316_remove_format_type_from_source.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023317_create_updates.calagator.rb +16 -0
- data/spec/dummy/db/migrate/20150309023318_remove_next_update_from_source.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023319_change_duration_to_end_time.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023320_create_tags_and_taggings.calagator.rb +29 -0
- data/spec/dummy/db/migrate/20150309023321_create_versioned_events.calagator.rb +26 -0
- data/spec/dummy/db/migrate/20150309023322_create_versioned_venues.calagator.rb +33 -0
- data/spec/dummy/db/migrate/20150309023323_add_rrule_to_events.calagator.rb +16 -0
- data/spec/dummy/db/migrate/20150309023324_create_versions.calagator.rb +19 -0
- data/spec/dummy/db/migrate/20150309023325_add_closed_flag_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023326_add_wifi_flag_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023327_add_access_notes_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023328_add_events_count_to_venues.calagator.rb +9 -0
- data/spec/dummy/db/migrate/20150309023329_add_venue_details_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023330_acts_as_taggable_on_migration.calagator.rb +51 -0
- data/spec/dummy/db/migrate/20150309023331_cleanup.calagator.rb +15 -0
- data/spec/dummy/db/migrate/20150309023332_specify_venues_latitude_and_longitude_precision.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023333_remove_updates.calagator.rb +14 -0
- data/spec/dummy/db/migrate/20150309023334_add_locked_status_to_events.calagator.rb +6 -0
- data/spec/dummy/db/schema.rb +95 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/doc/README_FOR_APP +2 -0
- data/spec/dummy/log/development.log +273 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/test/performance/browsing_test.rb +12 -0
- data/spec/dummy/test/test_helper.rb +13 -0
- data/spec/factories.rb +93 -0
- data/spec/features/add_event_spec.rb +99 -0
- data/spec/features/add_venue_spec.rb +34 -0
- data/spec/features/admin_auth_spec.rb +22 -0
- data/spec/features/admin_lock_event_spec.rb +41 -0
- data/spec/features/import_events_from_feed_spec.rb +43 -0
- data/spec/features/managing_event_spec.rb +111 -0
- data/spec/features/managing_venue_spec.rb +71 -0
- data/spec/features/search_event_spec.rb +27 -0
- data/spec/helpers/calagator/application_helper_spec.rb +82 -0
- data/spec/helpers/calagator/events_helper_spec.rb +172 -0
- data/spec/helpers/calagator/google_event_export_helper_spec.rb +70 -0
- data/spec/helpers/calagator/sources_helper_spec.rb +12 -0
- data/spec/helpers/calagator/tags_helper_spec.rb +85 -0
- data/spec/helpers/calagator/time_range_helper_spec.rb +59 -0
- data/spec/lib/calagator/blacklist_validator_spec.rb +65 -0
- data/spec/lib/calagator/decode_html_entities_hack_spec.rb +54 -0
- data/spec/lib/calagator/settings_spec.rb +20 -0
- data/spec/lib/calagator/url_prefixer_spec.rb +33 -0
- data/spec/lib/secrets_reader_spec.rb +65 -0
- data/spec/models/calagator/event/cloner_spec.rb +43 -0
- data/spec/models/calagator/event/overview_spec.rb +79 -0
- data/spec/models/calagator/event/search_spec.rb +103 -0
- data/spec/models/calagator/event_search_spec.rb +149 -0
- data/spec/models/calagator/event_spec.rb +859 -0
- data/spec/models/calagator/source/parser_facebook_spec.rb +73 -0
- data/spec/models/calagator/source/parser_hcal_spec.rb +69 -0
- data/spec/models/calagator/source/parser_ical_non_standard_spec.rb +91 -0
- data/spec/models/calagator/source/parser_ical_spec.rb +322 -0
- data/spec/models/calagator/source/parser_meetup_spec.rb +69 -0
- data/spec/models/calagator/source/parser_plancast_spec.rb +53 -0
- data/spec/models/calagator/source/parser_spec.rb +238 -0
- data/spec/models/calagator/source_spec.rb +135 -0
- data/spec/models/calagator/venue/search_spec.rb +92 -0
- data/spec/models/calagator/venue_search_spec.rb +124 -0
- data/spec/models/calagator/venue_spec.rb +346 -0
- data/spec/models/tag_spec.rb +35 -0
- data/spec/rails_helper.rb +39 -0
- data/spec/spec_helper.rb +140 -0
- data/spec/support/disable_geocoding.rb +1 -0
- data/spec/support/http_samples.rb +5 -0
- data/spec/support/samples/facebook.json +23 -0
- data/spec/support/samples/hcal_basic.xml +6 -0
- data/spec/support/samples/hcal_dup_event_dup_venue.xml +13 -0
- data/spec/support/samples/hcal_event_duplicates_fixture.xml +13 -0
- data/spec/support/samples/hcal_event_wo_lat_and_long.xml +14 -0
- data/spec/support/samples/hcal_multiple.xml +16 -0
- data/spec/support/samples/hcal_same_event_twice_with_different_venues.xml +12 -0
- data/spec/support/samples/hcal_single.xml +8 -0
- data/spec/support/samples/hcal_two_identical_events.xml +14 -0
- data/spec/support/samples/hcal_upcoming_v1.html +412 -0
- data/spec/support/samples/hcal_upcoming_v2.html +749 -0
- data/spec/support/samples/hcal_upcoming_v3.html +685 -0
- data/spec/support/samples/hcal_upcoming_v4.html +761 -0
- data/spec/support/samples/ical_apple.ics +22 -0
- data/spec/support/samples/ical_apple_v3.ics +37 -0
- data/spec/support/samples/ical_basic.ics +15 -0
- data/spec/support/samples/ical_basic_with_duration.ics +16 -0
- data/spec/support/samples/ical_event_with_squashed_venue.ics +12 -0
- data/spec/support/samples/ical_eventful_many.ics +504 -0
- data/spec/support/samples/ical_gmt.ics +12 -0
- data/spec/support/samples/ical_google.ics +786 -0
- data/spec/support/samples/ical_multiple_calendars.ics +111 -0
- data/spec/support/samples/ical_upcoming.ics +36 -0
- data/spec/support/samples/ical_upcoming_many.ics +682 -0
- data/spec/support/samples/ical_upcoming_v2.ics +43 -0
- data/spec/support/samples/ical_z.ics +10 -0
- data/spec/support/samples/meetup.ics +16 -0
- data/spec/support/samples/meetup.json +31 -0
- data/spec/support/samples/plancast.ics +59 -0
- data/spec/support/samples/plancast.json +51 -0
- data/spec/support/samples/plancast_with_missing_venue.json +39 -0
- data/spec/support/samples/upcoming_v1.xml +8 -0
- data/spec/support/samples/upcoming_v2_with_invalid_utc_dates.xml +8 -0
- data/spec/support/time_convenience_methods.rb +8 -0
- data/spec/support/time_zones.rb +12 -0
- data/spec/support/url_helpers.rb +5 -0
- data/spec/support/wait_for_ajax.rb +15 -0
- data/spec/support/webmock.rb +8 -0
- data/spec/travis_spec.rb +7 -0
- metadata +1194 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Return a new record with fields selectively copied from the original, and
|
|
2
|
+
# the start_time and end_time adjusted so that their date is set to today and
|
|
3
|
+
# their time-of-day is set to the original record's time-of-day.
|
|
4
|
+
module Calagator
|
|
5
|
+
|
|
6
|
+
class Event < ActiveRecord::Base
|
|
7
|
+
class Cloner < Struct.new(:event)
|
|
8
|
+
def self.clone(event)
|
|
9
|
+
new(event).clone
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
ATTRIBUTES = [:title, :description, :venue_id, :url, :tag_list, :venue_details]
|
|
13
|
+
|
|
14
|
+
def clone
|
|
15
|
+
clone = Event.new
|
|
16
|
+
ATTRIBUTES.each do |attribute|
|
|
17
|
+
clone.send "#{attribute}=", event.send(attribute)
|
|
18
|
+
end
|
|
19
|
+
if event.start_time
|
|
20
|
+
clone.start_time = clone_time_for_today(event.start_time)
|
|
21
|
+
end
|
|
22
|
+
if event.end_time
|
|
23
|
+
clone.end_time = clone_time_for_today(event.end_time)
|
|
24
|
+
end
|
|
25
|
+
clone
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Return a time that's today but has the time-of-day component from the
|
|
31
|
+
# +source+ time argument.
|
|
32
|
+
def clone_time_for_today(source)
|
|
33
|
+
today = Date.today
|
|
34
|
+
Time.zone.local(today.year, today.mon, today.day, source.hour, source.min, source.sec, source.usec)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Return an iCalendar string representing an Array of Event instances.
|
|
2
|
+
#
|
|
3
|
+
# Arguments:
|
|
4
|
+
# * :events - Event instance or array of them.
|
|
5
|
+
#
|
|
6
|
+
# Options:
|
|
7
|
+
# * :url_helper - Lambda that accepts an Event instance and generates a URL
|
|
8
|
+
# for it if it doesn't have a URL already.
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# ics1 = Event::IcalRenderer.render(myevent)
|
|
12
|
+
# ics2 = Event::IcalRenderer.render(myevents, url_helper: -> (event) { event_url(event) })
|
|
13
|
+
module Calagator
|
|
14
|
+
|
|
15
|
+
class Event < ActiveRecord::Base
|
|
16
|
+
class IcalRenderer
|
|
17
|
+
def self.render(events, opts={})
|
|
18
|
+
output = render_icalendar(events, opts)
|
|
19
|
+
output = add_name(output)
|
|
20
|
+
output = normalize_line_endings(output)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.render_icalendar(events, opts)
|
|
24
|
+
RiCal.Calendar do |calendar|
|
|
25
|
+
calendar.prodid = "-//Calagator//EN"
|
|
26
|
+
|
|
27
|
+
Array(events).each do |event|
|
|
28
|
+
calendar.event do |entry|
|
|
29
|
+
new(event, opts).add_event_to(entry)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end.export
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.add_name(output)
|
|
36
|
+
output.sub(/(CALSCALE:\w+)/i, "\\1\nX-WR-CALNAME:#{Calagator.title}\nMETHOD:PUBLISH")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.normalize_line_endings(output)
|
|
40
|
+
output.gsub(/\r?\n/,"\r\n")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_reader :event, :imported_from
|
|
44
|
+
|
|
45
|
+
def initialize(event, opts)
|
|
46
|
+
@event = event
|
|
47
|
+
@imported_from = opts[:url_helper].call(event).to_s if opts[:url_helper]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def add_event_to(entry)
|
|
51
|
+
fields.each do |field|
|
|
52
|
+
value = send(field) # value = summary
|
|
53
|
+
entry.send field, value if value # entry.summary summary if summary
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def fields
|
|
60
|
+
%w(summary description url location dtstart dtend created last_modified sequence dtstamp uid)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def summary
|
|
64
|
+
event.title || 'Untitled Event'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def description
|
|
68
|
+
parts = [
|
|
69
|
+
description_range,
|
|
70
|
+
description_description,
|
|
71
|
+
description_tags,
|
|
72
|
+
description_imported_from,
|
|
73
|
+
].compact
|
|
74
|
+
|
|
75
|
+
parts.join if parts.any?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def description_range
|
|
79
|
+
return unless multiday?
|
|
80
|
+
time_range = TimeRangeHelper.normalize_time(event, format: :text)
|
|
81
|
+
"This event runs from #{time_range}.\n\nDescription:\n"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def description_description
|
|
85
|
+
Loofah::Helpers.strip_tags(event.description) if event.description.present?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def description_tags
|
|
89
|
+
"\n\nTags: #{event.tag_list}" if event.tag_list.present?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def description_imported_from
|
|
93
|
+
"\n\nImported from: #{imported_from}" if imported_from
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def url
|
|
97
|
+
event.url if event.url.present?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def location
|
|
101
|
+
[event.venue_title, event.venue.full_address].compact.join(": ") if event.venue
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def dtstart
|
|
105
|
+
if multiday?
|
|
106
|
+
event.dates.first
|
|
107
|
+
else
|
|
108
|
+
event.start_time
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def dtend
|
|
113
|
+
if multiday?
|
|
114
|
+
event.dates.last + 1.day
|
|
115
|
+
else
|
|
116
|
+
event.end_time || event.start_time + 1.hour
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def created
|
|
121
|
+
event.created_at if event.created_at
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def last_modified
|
|
125
|
+
event.updated_at if event.updated_at
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def sequence
|
|
129
|
+
# Set the iCalendar SEQUENCE, which should be increased each time an
|
|
130
|
+
# event is updated. If an admin needs to forcefully increment the
|
|
131
|
+
# SEQUENCE for all events, they can edit the "config/secrets.yml"
|
|
132
|
+
# file and set the "icalendar_sequence_offset" value to something
|
|
133
|
+
# greater than 0.
|
|
134
|
+
(SECRETS.icalendar_sequence_offset || 0) + event.versions.count
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def dtstamp
|
|
138
|
+
# dtstamp and uid added because of a bug in Outlook;
|
|
139
|
+
# Outlook 2003 will not import an .ics file unless it has DTSTAMP, UID, and METHOD
|
|
140
|
+
# use created_at for DTSTAMP; if there's no created_at, use event.start_time;
|
|
141
|
+
event.created_at || event.start_time
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def uid
|
|
145
|
+
imported_from
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Treat any event with a duration of at least 20 hours as a multiday event.
|
|
149
|
+
def multiday?
|
|
150
|
+
event.dates.size > 1 && event.duration.seconds > 20.hours
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Calagator
|
|
2
|
+
|
|
3
|
+
class Event < ActiveRecord::Base
|
|
4
|
+
class Overview
|
|
5
|
+
def today
|
|
6
|
+
Event.non_duplicates.within_dates(today_date, tomorrow_date)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def tomorrow
|
|
10
|
+
Event.non_duplicates.within_dates(tomorrow_date, after_tomorrow_date)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def later
|
|
14
|
+
Event.non_duplicates.within_dates(after_tomorrow_date, future_cutoff_date)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def more
|
|
18
|
+
Event.after_date(future_cutoff_date).first
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def tags
|
|
22
|
+
@tags ||= Event.tag_counts_on(:tags, limit: 100, conditions: "tags_count >= 10").sort_by(&:name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def today_date
|
|
28
|
+
@today_date ||= Time.zone.now.beginning_of_day
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def tomorrow_date
|
|
32
|
+
today_date + 1.day
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def after_tomorrow_date
|
|
36
|
+
tomorrow_date + 1.day
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def future_cutoff_date
|
|
40
|
+
today_date + 2.weeks
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Calagator
|
|
2
|
+
|
|
3
|
+
class Event < ActiveRecord::Base
|
|
4
|
+
class Saver < Struct.new(:event, :params, :failure)
|
|
5
|
+
def save
|
|
6
|
+
event.attributes = params[:event]
|
|
7
|
+
event.venue = find_or_initialize_venue
|
|
8
|
+
event.start_time = [ params[:start_date], params[:start_time] ]
|
|
9
|
+
event.end_time = [ params[:end_date], params[:end_time] ]
|
|
10
|
+
event.tags.reload # Reload the #tags association because its members may have been modified when #tag_list was set above.
|
|
11
|
+
|
|
12
|
+
attempt_save?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def has_new_venue?
|
|
16
|
+
return unless event.venue
|
|
17
|
+
event.venue.previous_changes["id"] == [nil, event.venue.id] && params[:venue_name].present?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def find_or_initialize_venue
|
|
23
|
+
if params[:event] && params[:event][:venue_id].present?
|
|
24
|
+
Venue.find(params[:event][:venue_id]).progenitor
|
|
25
|
+
else
|
|
26
|
+
Venue.find_or_initialize_by_title(params[:venue_name]).progenitor
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def attempt_save?
|
|
31
|
+
!spam? && !preview? && event.save
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def spam?
|
|
35
|
+
evil_robot? || too_many_links?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def evil_robot?
|
|
39
|
+
if params[:trap_field].present?
|
|
40
|
+
self.failure = "<h3>Evil Robot</h3> We didn't save this event because we think you're an evil robot. If you're really not an evil robot, look at the form instructions more carefully. If this doesn't work please file a bug report and let us know."
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def too_many_links?
|
|
45
|
+
if event.description.present? && event.description.scan(/https?:\/\//i).size > 3
|
|
46
|
+
self.failure = "We allow a maximum of 3 links in a description. You have too many links."
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def preview?
|
|
51
|
+
if params[:preview]
|
|
52
|
+
event.valid?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Calagator
|
|
2
|
+
|
|
3
|
+
class Event < ActiveRecord::Base
|
|
4
|
+
class Search < Struct.new(:query, :tag, :order, :current)
|
|
5
|
+
def initialize attributes = {}
|
|
6
|
+
members.each do |key|
|
|
7
|
+
send "#{key}=", attributes[key]
|
|
8
|
+
end
|
|
9
|
+
self.order ||= "date"
|
|
10
|
+
validate!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Return +events+ grouped by currentness using a data structure like:
|
|
14
|
+
#
|
|
15
|
+
# {
|
|
16
|
+
# :current => [ my_current_event, my_other_current_event ],
|
|
17
|
+
# :past => [ my_past_event ],
|
|
18
|
+
# }
|
|
19
|
+
def grouped_events
|
|
20
|
+
grouped = events.group_by(&:current?)
|
|
21
|
+
grouped = { current: grouped[true] || [], past: grouped[false] || [] }
|
|
22
|
+
grouped[:current].reverse! if order.to_s == "date"
|
|
23
|
+
grouped[:past] = [] if current
|
|
24
|
+
grouped
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def current
|
|
28
|
+
["1", "true"].include?(super)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def events
|
|
32
|
+
@events ||= if tag
|
|
33
|
+
Event.search_tag(tag, order: order, current: current)
|
|
34
|
+
else
|
|
35
|
+
Event.search(query, order: order, skip_old: current)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def failure_message
|
|
40
|
+
@failure_message
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def hard_failure?
|
|
44
|
+
@hard_failure
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def validate!
|
|
50
|
+
unless %w(date name title venue score).include?(order) || order.blank?
|
|
51
|
+
@failure_message = "Unknown ordering option #{order.inspect}, sorting by date instead."
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if tag.present? && order == "score"
|
|
55
|
+
@failure_message = "You cannot sort tags by score"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if [query, tag].all?(&:blank?)
|
|
59
|
+
@failure_message = "You must enter a search query"
|
|
60
|
+
@hard_failure = true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if [query, tag].all?(&:present?)
|
|
64
|
+
# TODO make it possible to search by tag and query simultaneously
|
|
65
|
+
@failure_message = "You can't search by tag and query at the same time"
|
|
66
|
+
@hard_failure = true
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Calagator
|
|
2
|
+
|
|
3
|
+
class Event < ActiveRecord::Base
|
|
4
|
+
class SearchEngine
|
|
5
|
+
cattr_accessor(:kind) { :sql }
|
|
6
|
+
|
|
7
|
+
def self.search(*args)
|
|
8
|
+
search_engine.search(*args)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.use(kind)
|
|
12
|
+
self.kind = kind
|
|
13
|
+
search_engine.configure if search_engine.respond_to?(:configure)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.score?
|
|
17
|
+
search_engine.score?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private_class_method
|
|
21
|
+
|
|
22
|
+
def self.search_engine
|
|
23
|
+
kind == :sunspot ? ApacheSunspot : Sql
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module Calagator
|
|
2
|
+
|
|
3
|
+
class Event < ActiveRecord::Base
|
|
4
|
+
class SearchEngine
|
|
5
|
+
class ApacheSunspot < Struct.new(:query, :opts)
|
|
6
|
+
# Return an Array of non-duplicate Event instances matching the search +query+..
|
|
7
|
+
#
|
|
8
|
+
# Options:
|
|
9
|
+
# * :order => How to order the entries? Defaults to :score. Permitted values:
|
|
10
|
+
# * :score => Sort with most relevant matches first
|
|
11
|
+
# * :date => Sort by date
|
|
12
|
+
# * :name => Sort by event title
|
|
13
|
+
# * :title => same as :name
|
|
14
|
+
# * :venue => Sort by venue title
|
|
15
|
+
# * :limit => Maximum number of entries to return. Defaults to 50.
|
|
16
|
+
# * :skip_old => Return old entries? Defaults to false.
|
|
17
|
+
def self.search(*args)
|
|
18
|
+
new(*args).all
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.score?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.configure
|
|
26
|
+
Event.searchable do
|
|
27
|
+
text :title, :default_boost => 3
|
|
28
|
+
string :title
|
|
29
|
+
|
|
30
|
+
text :description
|
|
31
|
+
|
|
32
|
+
text :tag_list, :default_boost => 3
|
|
33
|
+
|
|
34
|
+
text :url
|
|
35
|
+
|
|
36
|
+
time :start_time
|
|
37
|
+
time :end_time
|
|
38
|
+
|
|
39
|
+
text :venue_title
|
|
40
|
+
string :venue_title
|
|
41
|
+
|
|
42
|
+
boolean(:duplicate) { |event| event.duplicate? }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.configured?
|
|
47
|
+
Event.respond_to?(:solr_search)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def initialize(*args)
|
|
51
|
+
super
|
|
52
|
+
self.class.configure unless self.class.configured?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def all
|
|
56
|
+
current_events + past_events
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def current_events
|
|
62
|
+
search(true)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def past_events
|
|
66
|
+
skip_old ? [] : search(false)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def search(current)
|
|
70
|
+
Event.solr_search do
|
|
71
|
+
keywords query
|
|
72
|
+
order_by *order
|
|
73
|
+
order_by :start_time, :desc
|
|
74
|
+
with :duplicate, false
|
|
75
|
+
data_accessor_for(Event).include = [:venue]
|
|
76
|
+
|
|
77
|
+
method = current ? :greater_than_or_equal_to : :less_than
|
|
78
|
+
with(:start_time).send(method, Date.yesterday.to_time)
|
|
79
|
+
end.results.take(limit)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def order
|
|
83
|
+
case opts[:order].try(:to_sym)
|
|
84
|
+
when :date
|
|
85
|
+
[:start_time, :desc]
|
|
86
|
+
when :venue, :location
|
|
87
|
+
[:venue_title, :asc]
|
|
88
|
+
when :name, :title
|
|
89
|
+
[:title, :asc]
|
|
90
|
+
else
|
|
91
|
+
[:score, :desc]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def skip_old
|
|
96
|
+
opts[:skip_old] == true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def limit
|
|
100
|
+
opts[:limit] || 50
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|