├── .gitignore ├── .rspec ├── .travis.yml ├── 19wu.sublime-project ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Capfile ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── avatar-default.png │ │ ├── event │ │ │ └── map.png │ │ ├── favicon.ico │ │ ├── feature-follow.png │ │ ├── feature-notification.png │ │ ├── feature-slug.png │ │ ├── logo-wordmark.png │ │ ├── sponsors │ │ │ ├── tui3.png │ │ │ ├── ucloud.png │ │ │ └── yunpian.png │ │ ├── spotlight-devices.png │ │ ├── spotlight-free.png │ │ └── spotlight-team.png │ ├── javascripts │ │ ├── angularjs.js.coffee │ │ ├── angularjs │ │ │ ├── admin │ │ │ │ └── order_fulfillments.js.coffee │ │ │ ├── collaborators.js.coffee │ │ │ ├── directives.js.coffee │ │ │ ├── filters.js.coffee │ │ │ ├── follows.js.coffee │ │ │ ├── order_refund_submit.js.coffee │ │ │ ├── orders.js.coffee │ │ │ ├── participants.js.coffee │ │ │ ├── services.js.coffee │ │ │ └── user_phones.js.coffee │ │ ├── application.js.coffee │ │ ├── bootstrap-custom.js │ │ ├── datetime.js.coffee │ │ ├── locales │ │ │ └── zh-CN.js.erb │ │ ├── map.js.coffee.erb │ │ └── upload.js.coffee │ └── stylesheets │ │ ├── _0.variables.css.scss │ │ ├── _1.base.css.scss │ │ ├── _2.layout.css.scss │ │ ├── _3.states.css.scss │ │ ├── _4.themes.css.scss │ │ ├── application.css.scss │ │ ├── bootstrap-custom.css.scss │ │ ├── mixins │ │ ├── .gitkeep │ │ └── _sticky-footer.css.scss │ │ └── modules │ │ ├── .gitkeep │ │ ├── _brand.css.scss │ │ ├── _event-map.css.scss │ │ ├── _event-order.css.scss │ │ ├── _event.css.scss │ │ ├── _form.css.scss │ │ ├── _home.css.scss │ │ ├── _icons.css.scss │ │ ├── _jumbotron.css.scss │ │ ├── _label.css.scss │ │ ├── _list.css.scss │ │ ├── _menu.css.scss │ │ ├── _nav-bar.css.scss │ │ ├── _social-count.css.scss │ │ ├── _table.css.scss │ │ ├── _topic.css.scss │ │ ├── _upload.css.scss │ │ ├── _user-bar.css.scss │ │ └── _user-profile.css.scss ├── constraints │ └── slug_constraint.rb ├── controllers │ ├── admin │ │ └── order_fulfillments_controller.rb │ ├── admin_orders_controller.rb │ ├── api │ │ └── events_controller.rb │ ├── application_controller.rb │ ├── autocomplete_controller.rb │ ├── collaborators_controller.rb │ ├── concerns │ │ ├── alipay_generatable.rb │ │ └── has_api_response.rb │ ├── event │ │ └── invoices_controller.rb │ ├── event_changes_controller.rb │ ├── event_orders_controller.rb │ ├── event_summaries_controller.rb │ ├── event_tickets_controller.rb │ ├── events_controller.rb │ ├── export_controller.rb │ ├── group_controller.rb │ ├── home_controller.rb │ ├── invitations_controller.rb │ ├── mockup_controller.rb │ ├── order_refunds_controller.rb │ ├── participants_controller.rb │ ├── photo_controller.rb │ ├── profiles_controller.rb │ ├── registrations_controller.rb │ ├── sessions_controller.rb │ ├── topic_reply_controller.rb │ ├── topics_controller.rb │ ├── user_orders_controller.rb │ ├── user_phones_controller.rb │ └── users_controller.rb ├── helpers │ ├── admin │ │ └── order_fulfillment_helper.rb │ ├── application_helper.rb │ ├── event_helper.rb │ ├── event_order_helper.rb │ ├── event_ticket_helper.rb │ ├── export_helper.rb │ ├── home_helper.rb │ ├── user_helper.rb │ └── user_orders_helper.rb ├── inputs │ ├── compound_datetime_input.rb │ ├── upload_photo_input.rb │ └── uploadable_input.rb ├── mailers │ ├── .gitkeep │ ├── event_mailer.rb │ ├── order_fulfillment_mailer.rb │ ├── order_mailer.rb │ └── user_mailer.rb ├── menus │ ├── event_menu.rb │ ├── event_orders_submenu.rb │ └── event_submenu.rb ├── middlewares │ └── fallback_url_redirector.rb ├── models │ ├── .gitkeep │ ├── ability.rb │ ├── compound_datetime.rb │ ├── content_filter.rb │ ├── event.rb │ ├── event_change.rb │ ├── event_observer.rb │ ├── event_order.rb │ ├── event_order_fulfillment.rb │ ├── event_order_item.rb │ ├── event_order_observer.rb │ ├── event_order_participant.rb │ ├── event_order_refund.rb │ ├── event_order_refund_observer.rb │ ├── event_order_shipping_address.rb │ ├── event_order_status_transition.rb │ ├── event_summary.rb │ ├── event_ticket.rb │ ├── fallback_url.rb │ ├── follow.rb │ ├── group.rb │ ├── group_collaborator.rb │ ├── group_topic.rb │ ├── group_topic_reply.rb │ ├── photo.rb │ ├── profile.rb │ ├── refund_batch.rb │ ├── sequence.rb │ ├── settings.rb │ └── user.rb ├── uploaders │ └── photo_uploader.rb └── views │ ├── admin │ └── order_fulfillments │ │ ├── create.json.jbuilder │ │ └── index.html.slim │ ├── admin_orders │ └── index.html.slim │ ├── api │ └── events │ │ └── participants.rabl │ ├── application │ ├── 403.html.slim │ ├── _clippy.html.slim │ ├── _flashes.html.slim │ ├── _markdown_guide.html.slim │ ├── _signed_in_nav_bar.html.slim │ ├── _signed_in_user_bar.html.slim │ └── _signed_out_user_bar.html.slim │ ├── collaborators │ └── index.html.slim │ ├── devise │ ├── confirmations │ │ └── new.html.slim │ ├── invitations │ │ ├── edit.html.slim │ │ └── new.html.slim │ ├── mailer │ │ ├── confirmation_instructions.html.slim │ │ ├── confirmation_instructions.text.erb │ │ ├── confirmation_instructions.zh-CN.html.slim │ │ ├── confirmation_instructions.zh-CN.text.erb │ │ ├── invitation_instructions.html.slim │ │ ├── reset_password_instructions.html.slim │ │ ├── reset_password_instructions.text.erb │ │ ├── reset_password_instructions.zh-CN.html.slim │ │ └── reset_password_instructions.zh-CN.text.erb │ ├── passwords │ │ ├── edit.html.slim │ │ └── new.html.slim │ ├── registrations │ │ ├── edit.html.slim │ │ └── new.html.slim │ ├── sessions │ │ └── new.html.slim │ ├── shared │ │ └── _links.slim │ └── unlocks │ │ └── new.html.slim │ ├── event │ └── invoices │ │ └── index.xlsx.axlsx │ ├── event_changes │ ├── _submenu.html.slim │ ├── index.html.slim │ └── new.html.slim │ ├── event_mailer │ └── change_email.zh-CN.html.slim │ ├── event_orders │ ├── _submenu.html.slim │ ├── create.json.jbuilder │ └── index.html.slim │ ├── event_summaries │ ├── _form.html.slim │ └── new.html.slim │ ├── event_tickets │ ├── _form.html.slim │ ├── edit.html.slim │ ├── index.html.slim │ └── new.html.slim │ ├── events │ ├── _form.html.slim │ ├── _header.html.slim │ ├── _menu.html.slim │ ├── edit.html.slim │ ├── followers.html.slim │ ├── index.html.slim │ ├── new.html.slim │ ├── ordered.html.slim │ └── show.html.slim │ ├── export │ └── index.html.slim │ ├── home │ ├── _menu.html.slim │ ├── index.html.slim │ └── page.html.slim │ ├── invitations │ ├── index.html.slim │ └── upgrade.html.slim │ ├── layouts │ ├── application.html.slim │ └── settings.html.slim │ ├── order_fulfillment_mailer │ ├── notify_organizer_fulfilled.html.slim │ └── notify_user_fulfilled.html.slim │ ├── order_mailer │ ├── notify_organizer_created.html.slim │ ├── notify_organizer_paid.html.slim │ ├── notify_organizer_refunded.html.slim │ ├── notify_support_refund.html.slim │ ├── notify_user_checkin_code.html.slim │ ├── notify_user_created.html.slim │ ├── notify_user_paid.html.slim │ └── notify_user_refunded.html.slim │ ├── order_refunds │ ├── _refunds.html.slim │ ├── index.html.slim │ └── submit.json.jbuilder │ ├── participants │ ├── _submenu.html.slim │ ├── checkin.html.slim │ ├── export.xlsx.axlsx │ ├── index.html.slim │ └── update.json.jbuilder │ ├── profiles │ └── show.html.slim │ ├── registrations │ └── create.json.jbuilder │ ├── sessions │ └── create.json.jbuilder │ ├── topics │ ├── _index.html.slim │ ├── new.html.slim │ └── show.html.slim │ ├── user_mailer │ ├── invited_email.html.slim │ ├── notify_email.zh-CN.html.slim │ ├── reminder_email.zh-CN.html.slim │ ├── welcome_email.html.slim │ ├── welcome_email.text.erb │ ├── welcome_email.zh-CN.html.slim │ └── welcome_email.zh-CN.text.erb │ ├── user_orders │ ├── _order_item.html.slim │ ├── alipay_done.html.slim │ └── index.html.slim │ ├── user_phones │ └── edit.html.slim │ └── users │ ├── cohort.html.slim │ └── show.html.slim ├── bin ├── bundle ├── delayed_job ├── magic_encoding_find ├── magic_encoding_fix ├── rails └── rake ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml.example.mysql2 ├── database.yml.example.pg ├── database.yml.example.sqlite3 ├── deploy.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── 0_jruby_patches.rb │ ├── alipay.rb │ ├── backtrace_silencers.rb │ ├── carrierwave.rb │ ├── delayed_job_config.rb │ ├── devise.rb │ ├── html_pipeline.rb │ ├── inflections.rb │ ├── load_lib_files.rb │ ├── middlewares.rb │ ├── mime_types.rb │ ├── ransack.rb │ ├── secret_token.rb │ ├── session_store.rb │ ├── setup_locale.rb │ ├── setup_mail.rb │ ├── simple_form.rb │ ├── sms.rb │ └── wrap_parameters.rb ├── locales │ ├── activerecord.en.yml │ ├── activerecord.zh-CN.yml │ ├── cancan.en.yml │ ├── cancan.zh-CN.yml │ ├── devise.en.yml │ ├── devise.zh-CN.yml │ ├── devise_invitable.en.yml │ ├── devise_invitable.zh-CN.yml │ ├── en.yml │ ├── rails.zh-CN.yml │ ├── simple_form.en.yml │ ├── simple_form.zh-CN.yml │ └── zh-CN.yml ├── nginx ├── routes.rb ├── schedule.rb ├── settings.yml.example └── unicorn.rb ├── custom_plan.rb ├── db ├── migrate │ ├── 20121226132049_devise_create_users.rb │ ├── 20121227135540_add_login_to_users.rb │ ├── 20121231000051_create_events.rb │ ├── 20130104101554_add_confirmable_to_devise.rb │ ├── 20130105124957_add_user_id_to_events.rb │ ├── 20130106024948_create_delayed_jobs.rb │ ├── 20130106071318_create_profiles.rb │ ├── 20130107053540_add_location_guide_to_events.rb │ ├── 20130107102539_create_photos.rb │ ├── 20130110074522_create_event_participants.rb │ ├── 20130124031857_create_groups.rb │ ├── 20130125081842_add_joined_to_event_participants.rb │ ├── 20130225091732_devise_invitable_add_to_users.rb │ ├── 20130226085502_add_admin_flag_to_users.rb │ ├── 20130226122259_add_invite_reason_to_users.rb │ ├── 20130319094925_acts_as_follower_migration.rb │ ├── 20130328114637_participant_automatic_follow_event.rb │ ├── 20130403135737_create_fallback_urls.rb │ ├── 20130526080632_create_event_summaries.rb │ ├── 20130527075012_create_group_collaborators.rb │ ├── 20130617104446_create_event_changes.rb │ ├── 20130702125422_move_phone_to_users.rb │ ├── 20130704141739_create_group_topics.rb │ ├── 20130706130132_create_group_topic_replies.rb │ ├── 20130716040406_add_replies_count_to_group_topics.rb │ ├── 20130717073411_recreate_delayed_jobs_1.rb │ ├── 20130807005443_create_event_tickets.rb │ ├── 20130807133515_create_event_orders.rb │ ├── 20130808003107_create_event_order_items.rb │ ├── 20130809010150_change_price_to_price_in_cents.rb │ ├── 20130811124504_create_event_order_shipping_addresses.rb │ ├── 20130812033933_add_default_value_to_events.rb │ ├── 20130812043238_add_canceled_at_to_event_orders.rb │ ├── 20130814191134_remove_canceled_at_from_event_orders.rb │ ├── 20130817025041_create_event_order_participants.rb │ ├── 20130817121436_move_event_participant_to_order_participant.rb │ ├── 20130818175826_create_event_order_status_transitions.rb │ ├── 20130825132343_create_sequences.rb │ ├── 20130825132914_add_number_to_order.rb │ ├── 20130916141824_add_paid_amount_and_refunded_amount_to_order.rb │ ├── 20130916145041_create_refund_batches.rb │ ├── 20130916145506_create_event_order_refunds.rb │ ├── 20130921140156_add_unit_price_to_event_order_item.rb │ ├── 20130925031818_revert_ticket_quantity_of_request_refund_order.rb │ ├── 20131031070813_create_event_order_fulfillments.rb │ ├── 20131125011434_send_checkin_code_sms_for_free_event.rb │ ├── 20150128225959_change_trade_no_to_event_order.rb │ ├── 20150501061606_add_status_to_event_tickets.rb │ └── 20150516142649_add_require_invoice_to_event_orders.rb ├── schema.rb └── seeds.rb ├── doc └── README_FOR_APP ├── lib ├── assets │ └── .gitkeep ├── core_ext │ └── hash │ │ └── angularjs.rb ├── fancy_url │ └── fancy_url.rb ├── jruby_patches │ ├── escape_utils.rb │ └── github │ │ └── markdown.rb ├── tasks │ ├── .gitkeep │ ├── assets.rake │ ├── check.rake │ ├── schema_ext.rake │ ├── setup.rake │ ├── stats.rake │ └── travis.rake └── templates │ └── slim │ └── scaffold │ └── _form.html.slim ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico ├── flash │ └── clippy.swf └── robots.txt ├── spec ├── controllers │ ├── admin │ │ └── order_fulfillments_controller_spec.rb │ ├── admin_orders_controller_spec.rb │ ├── application_controller_spec.rb │ ├── autocomplete_controller_spec.rb │ ├── collaborators_controller_spec.rb │ ├── event │ │ └── invoices_controller_spec.rb │ ├── event_changes_controller_spec.rb │ ├── event_orders_controller_spec.rb │ ├── event_summaries_controller_spec.rb │ ├── event_tickets_controller_spec.rb │ ├── events_controller_spec.rb │ ├── exprot_controller_spec.rb │ ├── group_controller_spec.rb │ ├── home_controller_spec.rb │ ├── order_refunds_controller_spec.rb │ ├── participants_controller_spec.rb │ ├── photo_controller_spec.rb │ ├── profiles_controller_spec.rb │ ├── sessions_controller_spec.rb │ ├── topic_reply_controller_spec.rb │ ├── topics_controller_spec.rb │ ├── user_orders_controller_spec.rb │ ├── user_phones_controller_spec.rb │ └── users_controller_spec.rb ├── factories │ ├── data │ │ └── event │ │ │ ├── map.png │ │ │ └── map_big.png │ ├── event_changes.rb │ ├── event_order_fulfillments.rb │ ├── event_order_items.rb │ ├── event_order_participants.rb │ ├── event_order_refunds.rb │ ├── event_order_shipping_addresses.rb │ ├── event_order_status_transitions.rb │ ├── event_orders.rb │ ├── event_participants.rb │ ├── event_summaries.rb │ ├── event_tickets.rb │ ├── events.rb │ ├── fallback_urls.rb │ ├── group_collaborators.rb │ ├── group_topic_replies.rb │ ├── group_topics.rb │ ├── groups.rb │ ├── profiles.rb │ ├── refund_batches.rb │ ├── sequences.rb │ └── users.rb ├── features │ ├── admin │ │ └── order_fulfillments_spec.rb │ ├── event_changes_spec.rb │ ├── event_orders_spec.rb │ ├── event_page_spec.rb │ ├── event_participants_spec.rb │ ├── event_summary_spec.rb │ ├── event_tickets_spec.rb │ ├── fallback_url_spec.rb │ ├── follower_page_spec.rb │ ├── group_collaborators_spec.rb │ ├── group_topics_spec.rb │ ├── landing_page_spec.rb │ ├── loc_spec.rb │ ├── magic_encoding_spec.rb │ ├── new_event_spec.rb │ ├── order_refunds_spec.rb │ ├── profile_spec.rb │ ├── user_orders_spec.rb │ ├── user_page_spec.rb │ ├── user_phones_spec.rb │ └── user_registration_spec.rb ├── helpers │ ├── application_helper_spec.rb │ ├── event_helper_spec.rb │ ├── event_order_helper_spec.rb │ ├── export_helper_spec.rb │ └── home_helper_spec.rb ├── javascripts │ ├── angular │ │ ├── filters.js.coffee │ │ └── services.js.coffee │ ├── events │ │ ├── collaborators.js.coffee │ │ ├── follows.js.coffee │ │ └── orders.js.coffee │ ├── helpers │ │ ├── factory.js.coffee │ │ └── html_sandbox_helpers.js.coffee │ ├── orders │ │ └── refund_submit.js.coffee │ ├── specs.js.coffee │ ├── support │ │ └── jasmine.yml │ └── users │ │ └── phones.js.coffee ├── lib │ └── core_ext │ │ └── hash │ │ └── angularjs_spec.rb ├── mailers │ ├── order_fulfillment_mailer_spec.rb │ ├── order_mailer_spec.rb │ └── user_mailer_spec.rb ├── models │ ├── ability_spec.rb │ ├── compound_datetime_spec.rb │ ├── content_filter_spec.rb │ ├── event_change_spec.rb │ ├── event_observer_spec.rb │ ├── event_order_fulfillment_spec.rb │ ├── event_order_item_spec.rb │ ├── event_order_participant_spec.rb │ ├── event_order_refund_observer_spec.rb │ ├── event_order_refund_spec.rb │ ├── event_order_shipping_address_spec.rb │ ├── event_order_spec.rb │ ├── event_spec.rb │ ├── event_summary_spec.rb │ ├── event_ticket_spec.rb │ ├── fallback_url_spec.rb │ ├── group_collaborator_spec.rb │ ├── group_spec.rb │ ├── group_topic_reply_spec.rb │ ├── group_topic_spec.rb │ ├── photo_spec.rb │ ├── refund_batch_spec.rb │ ├── sequence_spec.rb │ └── user_spec.rb ├── spec_helper.rb ├── support │ ├── delayed_job.rb │ ├── devise.rb │ ├── javascript.rb │ └── stub_gravatar_url.rb ├── uploaders │ └── photo_uploader_spec.rb └── views │ └── events │ └── show.html.slim_spec.rb ├── vendor ├── assets │ ├── javascripts │ │ ├── .gitkeep │ │ ├── angular-strap.min.js │ │ ├── jquery.ba-throttle-debounce.min.js │ │ └── jquery.textarea.caret.js │ └── stylesheets │ │ └── .gitkeep └── plugins │ └── .gitkeep └── zeus.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/*.log 15 | chromedriver.log 16 | /tmp 17 | # carrierwave upload 18 | public/uploads 19 | 20 | .sass-cache/ 21 | 22 | # Secret file 23 | config/database.yml 24 | config/settings.yml 25 | 26 | # pl#1 windows 7 compatible 27 | *.sublime-workspace 28 | .idea/ 29 | .yardoc/ 30 | doc/ 31 | public/assets/ 32 | .tags* 33 | .gemtags 34 | 35 | # Ignore rvmrc configuration file 36 | .rvmrc 37 | 38 | # Ignore *.swp file 39 | *.swp 40 | 41 | # Ignore .DS_Store file 42 | *.DS_Store 43 | 44 | # SimpleCov 45 | coverage 46 | 47 | # jasmine-rails 48 | spec/tmp 49 | 50 | # selenium 51 | libpeerconnection.log 52 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # http://about.travis-ci.org/docs/user/build-configuration/ 2 | language: ruby 3 | 4 | bundler_args: '--without production development -j8' 5 | 6 | script: bundle exec rake travis:script 7 | 8 | before_install: 9 | - "phantomjs --version" 10 | - "cat /etc/timezone" 11 | - "grep -i processor /proc/cpuinfo | wc -l" 12 | - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" 13 | - "gem install bundler --pre" 14 | - "bundle -v" 15 | 16 | before_script: 17 | - "bundle exec rake travis:before_script" 18 | 19 | env: 20 | matrix: 21 | - DATABASE=pg 22 | - DATABASE=mysql2 23 | #- DATABASE=sqlite3 24 | 25 | rvm: 26 | #- 1.9.3 27 | - 2.0.0 28 | #- ruby-head # will cause timeout 29 | -------------------------------------------------------------------------------- /19wu.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | "folder_exclude_patterns": [".bundle",".idea",".yardoc","tmp","images","public/assets"], 7 | "file_exclude_patterns": [".gitkeep","*.sublime-workspace","*.sqlite3*",".tags*",".gemtags"] 8 | } 9 | ], 10 | "settings": 11 | { 12 | "tab_size": 2 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 变更记录 2 | 3 | https://github.com/19wu/19wu/pulls?direction=desc&page=1&sort=created&state=closed 4 | -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | # capify . 2 | load 'deploy' 3 | # Uncomment if you are using Rails' asset pipeline 4 | load 'deploy/assets' 5 | 6 | # load delayed_job gem recipes 7 | Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) } 8 | 9 | load 'config/deploy' # remove this line to skip loading any of the default tasks 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2012 saberma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec rails s -p $PORT 2 | worker: bundle exec bin/delayed_job run 3 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require 'tasks/state_machine' 6 | require File.expand_path('../config/application', __FILE__) 7 | 8 | NineteenWu::Application.load_tasks 9 | -------------------------------------------------------------------------------- /app/assets/images/avatar-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/avatar-default.png -------------------------------------------------------------------------------- /app/assets/images/event/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/event/map.png -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/feature-follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/feature-follow.png -------------------------------------------------------------------------------- /app/assets/images/feature-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/feature-notification.png -------------------------------------------------------------------------------- /app/assets/images/feature-slug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/feature-slug.png -------------------------------------------------------------------------------- /app/assets/images/logo-wordmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/logo-wordmark.png -------------------------------------------------------------------------------- /app/assets/images/sponsors/tui3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/sponsors/tui3.png -------------------------------------------------------------------------------- /app/assets/images/sponsors/ucloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/sponsors/ucloud.png -------------------------------------------------------------------------------- /app/assets/images/sponsors/yunpian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/sponsors/yunpian.png -------------------------------------------------------------------------------- /app/assets/images/spotlight-devices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/spotlight-devices.png -------------------------------------------------------------------------------- /app/assets/images/spotlight-free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/spotlight-free.png -------------------------------------------------------------------------------- /app/assets/images/spotlight-team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/images/spotlight-team.png -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs.js.coffee: -------------------------------------------------------------------------------- 1 | #= require angular 2 | #= require angular-resource 3 | #= require angular-strap.min 4 | #= require_self 5 | #= require_tree ./angularjs 6 | 7 | @app = angular.module('19wu', ['$strap.directives']) 8 | @app.config(["$httpProvider", (provider) -> 9 | provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') 10 | ]) 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/admin/order_fulfillments.js.coffee: -------------------------------------------------------------------------------- 1 | @OrderFulfillmentsCtrl = ['$scope', '$http', ($scope, $http) -> 2 | $scope.submit = (index) -> 3 | order = $scope.orders[index] 4 | request = $http.post("/admin/fulfillments", order_id: order.id, fulfillment: { tracking_number: order.fulfillment_tracking_number }) 5 | request.success (data) -> 6 | order['fulfillment'] = data 7 | request.error (data) -> 8 | alert data['errors'] 9 | ] 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/collaborators.js.coffee: -------------------------------------------------------------------------------- 1 | @CollaboratorsCtrl = ['$scope', '$http', ($scope, $http) -> 2 | $scope.error = false 3 | $scope.load = (query, callback) -> # TODO: cancellable 4 | $http.get("/autocomplete/users?q=#{query}").success (data) -> callback(data) 5 | $scope.add = -> # TODO: only real login can be add 6 | unless $scope.exists($scope.login) 7 | $http.post("/events/#{$scope.event.id}/collaborators", login: $scope.login).success (data) -> 8 | $scope.items.push data 9 | $scope.login = '' 10 | $scope.remove = (index) -> 11 | item = $scope.items[index] 12 | $http.delete("/events/#{$scope.event.id}/collaborators/#{item.id}").success (data) -> 13 | $scope.items.splice(index, 1) 14 | $scope.exists = (login) -> 15 | $scope.error = false 16 | $scope.error = true for item in $scope.items when item.login is login 17 | $scope.error 18 | ] 19 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/directives.js.coffee: -------------------------------------------------------------------------------- 1 | @app.directive 'bsPopoverCustom', () -> 2 | (scope, element, attrs) -> 3 | attrs.$observe 'target', (value) -> 4 | options = {} 5 | if value 6 | $target = $(value) 7 | options = 8 | html: true 9 | title: $target.find('.popover-title').html() 10 | content: $target.find('.popover-content').html() 11 | element.popover options 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/filters.js.coffee: -------------------------------------------------------------------------------- 1 | @app.filter 'money', () -> 2 | (input) -> 3 | if parseFloat(input) == 0 4 | '免费' 5 | else 6 | input 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/follows.js.coffee: -------------------------------------------------------------------------------- 1 | @FollowsCtrl = ['$scope', '$http', '$location', ($scope, $http, $location) -> 2 | $scope.init = (data) -> 3 | [$scope.count, $scope.labels, $scope.followed] = data 4 | $scope.disabled = !$scope.user? 5 | $scope.title = "新活动发布时会给您发送邮件通知" 6 | if $scope.disabled 7 | $scope.title = "您需要登录后才能关注活动" 8 | $scope.href = "/users/sign_in" 9 | $scope.follow = -> 10 | return if $scope.disabled 11 | $scope.followed = !$scope.followed 12 | action = if $scope.followed then 'follow' else 'unfollow' 13 | $http.post("/events/#{$scope.event.id}/#{action}").success (data) -> $scope.count = data.count 14 | ] 15 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/order_refund_submit.js.coffee: -------------------------------------------------------------------------------- 1 | @OrderRefundSubmitCtrl = ['$scope', '$http', ($scope, $http) -> 2 | $scope.amount = null 3 | $scope.reason = null 4 | 5 | $scope.show = (index) -> 6 | order = $scope.orders[index] 7 | order.submit_refund_form = !order.submit_refund_form 8 | if order.submit_refund_form 9 | order.amount = $scope.amount unless order.amount 10 | order.reason = $scope.reason unless order.reason 11 | $scope.submit = (index) -> 12 | order = $scope.orders[index] 13 | request = $http.post("/events/#{$scope.event.id}/orders/#{order.id}/refund/submit", refund: { amount: order.amount, reason: order.reason }) 14 | request.success (data) -> 15 | order['refund'] = data 16 | $scope.amount = data['amount'] 17 | $scope.reason = data['reason'] 18 | request.error (data) -> 19 | alert data['errors'] 20 | ] 21 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/participants.js.coffee: -------------------------------------------------------------------------------- 1 | @ParticipantsCtrl = ['$scope', '$http', ($scope, $http) -> 2 | $scope.checkin = -> 3 | return unless $scope.code 4 | $scope.wait = true 5 | $scope.error = $scope.data = null 6 | request = $http.post("/events/#{$scope.event.id}/participants", code: $scope.code) 7 | request.success (data) -> 8 | $scope.code = '' 9 | $scope.data = data 10 | $scope.wait = false # TODO: use always method 11 | request.error (data) -> 12 | $scope.error = data['errors'].join(',') 13 | $scope.wait = false 14 | ] 15 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/services.js.coffee: -------------------------------------------------------------------------------- 1 | @app.factory 'participants', ['$rootScope', '$http', ($rootScope, $http) -> 2 | data = [] 3 | load = -> 4 | $http.get("/api/events/#{$rootScope.event.id}/participants").success (users) -> 5 | data.length = 0 6 | $.each users, (index, item) -> 7 | data.push item 8 | load() 9 | data: data, reload: -> load() 10 | ] 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs/user_phones.js.coffee: -------------------------------------------------------------------------------- 1 | @UserPhonesCtrl = ['$scope', '$http', '$timeout', ($scope, $http, $timeout) -> 2 | LIMIT_TIME = $scope.timeleft = 60 3 | $scope.showed = $scope.wait = false 4 | $scope.send_code = -> 5 | if $scope.phone 6 | $http.post("/user_phone/send_code", phone: $scope.phone).success (data) -> 7 | $scope.showed = $scope.wait = true 8 | $scope.timeleft = LIMIT_TIME 9 | $scope.tick() 10 | $scope.tick = -> 11 | $scope.timeleft-- 12 | if $scope.timeleft > 0 13 | $timeout($scope.tick, 1000) 14 | else 15 | $scope.wait = false 16 | ] 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require jquery_ujs 3 | #= require jquery-fileupload/basic 4 | #= require jquery.textarea.caret 5 | #= require jquery.ba-throttle-debounce.min 6 | #= require rails-timeago 7 | #= require china_city/jquery.china_city 8 | #= require locales/jquery.timeago.zh-CN.js 9 | #= require bootstrap-custom 10 | #= require bootstrap-datepicker/core 11 | #= require bootstrap-timepicker 12 | #= require angularjs 13 | #= require datetime 14 | #= require upload 15 | #= require map 16 | #= require_self 17 | $ -> 18 | $("a[data-toggle='tooltip']").tooltip() 19 | $("a[rel='popover']").each -> 20 | $target = $($(this).data('target')) 21 | $(this).popover 22 | html: true 23 | title: $target.find('.popover-title').html() 24 | content: $target.find('.popover-content').html() 25 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap-custom.js: -------------------------------------------------------------------------------- 1 | // http://git.io/dOz4cQ 2 | //= require bootstrap-alert 3 | //= require bootstrap-modal 4 | //= require bootstrap-tab 5 | //= require bootstrap-tooltip 6 | //= require bootstrap-popover 7 | //= require bootstrap-typeahead 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/locales/zh-CN.js.erb: -------------------------------------------------------------------------------- 1 | //= require bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.js 2 | //= require_self 3 | var I18n = window.I18n = { 4 | locale: 'zh-CN', 5 | date: { 6 | weekstart: <%= I18n.backend.translate('zh-CN', 'date.weekstart').inspect %>, 7 | formats: { 8 | datepicker: <%= I18n.backend.translate('zh-CN', 'date.formats.datepicker').inspect %> 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_0.variables.css.scss: -------------------------------------------------------------------------------- 1 | /* Site settings */ 2 | 3 | $grayBackground: hsl(0, 0%, 97%); 4 | $borderColor: hsl(0, 0, 88); 5 | 6 | $hrBorder: hsl(0, 0%, 82%); 7 | 8 | /* Bootstrap overrides */ 9 | // always reuse these variables http://git.io/OLWa8Q 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_1.base.css.scss: -------------------------------------------------------------------------------- 1 | h3 { 2 | @extend h5; 3 | line-height: $baseLineHeight; 4 | } 5 | 6 | .space { 7 | padding-right: $baseFontSize / 2; 8 | } 9 | 10 | .margin-right { 11 | margin-right: $baseFontSize / 2; 12 | } 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_3.states.css.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/stylesheets/_3.states.css.scss -------------------------------------------------------------------------------- /app/assets/stylesheets/_4.themes.css.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/stylesheets/_4.themes.css.scss -------------------------------------------------------------------------------- /app/assets/stylesheets/mixins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/stylesheets/mixins/.gitkeep -------------------------------------------------------------------------------- /app/assets/stylesheets/mixins/_sticky-footer.css.scss: -------------------------------------------------------------------------------- 1 | /* Sticky Footer 2 | * http://www.cssstickyfooter.com/using-sticky-footer-code.html 3 | */ 4 | @mixin sticky_footer { 5 | html, body { 6 | height: 100%; 7 | } 8 | #wrap { 9 | min-height: 100%; 10 | } 11 | #main { 12 | overflow: auto; 13 | padding-bottom: 145px; 14 | padding-top: 64px; 15 | } 16 | #header { 17 | min-height: 48px; 18 | margin-bottom: -64px; 19 | } 20 | #footer { 21 | position: relative; 22 | clear: both; 23 | min-height: 65px; 24 | margin-top: -145px; 25 | } 26 | /*Opera Fix*/ 27 | body:before { 28 | content:""; 29 | height:100%; 30 | float:left; 31 | width:0; 32 | margin-top:-32767px; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/stylesheets/modules/.gitkeep -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_brand.css.scss: -------------------------------------------------------------------------------- 1 | .brand { 2 | line-height: 24px; 3 | margin-right: -15px; 4 | } 5 | 6 | .logo-wordmark { 7 | padding-top: 4px; 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_event-map.css.scss: -------------------------------------------------------------------------------- 1 | div[id^=mapModal] { 2 | max-width: 800px; 3 | width: auto; 4 | } 5 | 6 | .event-map img:hover { 7 | cursor: pointer; 8 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_event-order.css.scss: -------------------------------------------------------------------------------- 1 | // event show 2 | .orders-stats { 3 | @extend .unstyled, .inline; 4 | text-align: center; 5 | margin-bottom: 0; 6 | 7 | .orders-stat { 8 | padding: 0 60px; 9 | border-right: 1px solid $hrBorder; 10 | &.last { 11 | border-right: 0; 12 | } 13 | } 14 | } 15 | 16 | .tickets-list { 17 | margin-bottom: 0; 18 | } 19 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_home.css.scss: -------------------------------------------------------------------------------- 1 | .home-nav { 2 | @extend .nav, .nav-tabs; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_icons.css.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/assets/stylesheets/modules/_icons.css.scss -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_jumbotron.css.scss: -------------------------------------------------------------------------------- 1 | .jumbotron { 2 | background: $grayBackground; 3 | padding: 3 * $baseLineHeight 0; 4 | margin-bottom: 2 * $baseLineHeight; 5 | border-bottom: 1px solid $borderColor; 6 | .form-full-width { 7 | margin: 0; 8 | } 9 | } 10 | 11 | .slogan-title { 12 | font-size: $baseFontSize * 4; 13 | line-height: $baseFontSize * 4; 14 | margin-bottom: 1.5 * $baseLineHeight; 15 | } 16 | .slogan-description { 17 | @extend .muted; 18 | font-size: $baseFontSize * 1.5; 19 | line-height: $baseLineHeight * 2; 20 | } 21 | 22 | @media (max-width: 767px) { 23 | .jumbotron { 24 | padding-left: 20px; 25 | padding-right: 20px; 26 | margin-left: -20px; 27 | margin-right: -20px; 28 | } 29 | } 30 | 31 | .features { 32 | h3, h4 { 33 | font-weight: 300; 34 | } 35 | h3 { 36 | font-size: $baseFontSize * 1.75; 37 | line-height: $baseLineHeight * 2; 38 | } 39 | h4 { 40 | margin-top: $baseLineHeight; 41 | } 42 | img { 43 | border: 1px solid $borderColor; 44 | } 45 | hr { 46 | margin: $baseLineHeight * 4 $gridColumnWidth + $gridGutterWidth; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_label.css.scss: -------------------------------------------------------------------------------- 1 | .amount { 2 | margin-left: 4px; 3 | } 4 | 5 | .alpha { 6 | @extend .label; 7 | margin-left: 10px; 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_list.css.scss: -------------------------------------------------------------------------------- 1 | ul, ol { 2 | &.horizontal { 3 | @extend .unstyled; 4 | @include clearfix; 5 | > li { 6 | float: left; 7 | } 8 | } 9 | &.unstyled > li { 10 | margin-bottom: $baseLineHeight / 4; 11 | } 12 | } 13 | 14 | .list { 15 | .item { 16 | margin-bottom: $baseLineHeight / 2; 17 | } 18 | .avatar-wrapper { 19 | line-height: 0; 20 | float: left; 21 | margin-top: 4px; 22 | margin-right: 10px; 23 | padding: 1px; 24 | border: 1px solid $grayBackground; 25 | } 26 | .gravatar { 27 | width: 50px; 28 | height: 50px; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_menu.css.scss: -------------------------------------------------------------------------------- 1 | .menu .tooltip-inner { 2 | width: 156px; 3 | } 4 | 5 | .submenu { 6 | @extend .well; 7 | padding: 20px 0; 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_nav-bar.css.scss: -------------------------------------------------------------------------------- 1 | .navbar .nav { 2 | > li { 3 | > a { 4 | padding: 14px 15px 14px; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_social-count.css.scss: -------------------------------------------------------------------------------- 1 | @mixin gap($color) { 2 | content: ""; 3 | display: block; 4 | width: 0; 5 | height: 0; 6 | border: 6px solid $color; 7 | border-color: transparent $color transparent transparent; 8 | position: absolute; 9 | right: 100%; 10 | top: 50%; 11 | margin-top: -6px; 12 | } 13 | @mixin social-count($background-color, $border-color) { 14 | position: relative; 15 | display: inline-block; 16 | padding: 0 7px 0; 17 | margin-left: 8px; 18 | font-size: $baseFontSize; 19 | font-weight: bold; 20 | line-height: 28px; 21 | color: $grayDark; 22 | vertical-align: middle; 23 | background-color: $background-color; 24 | border: 1px solid $btnBorder; 25 | border-radius: 3px; 26 | &:before { 27 | @include gap($btnBorder); 28 | margin-right: 0; 29 | } 30 | &:after { 31 | @include gap($background-color); 32 | margin-right: -1px; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_table.css.scss: -------------------------------------------------------------------------------- 1 | .table-operate { 2 | a { 3 | margin-right: 10px; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_topic.css.scss: -------------------------------------------------------------------------------- 1 | // topic index item 2 | .topics-nav { 3 | @extend .nav, .nav-tabs; 4 | margin: $baseLineHeight*4 0 $baseLineHeight/2 0; 5 | } 6 | 7 | .topic-item { 8 | @extend .media; 9 | margin-bottom: 0 !important; 10 | padding: $baseLineHeight/2 0; 11 | border-bottom: 1px solid #DDD 12 | } 13 | .topic-item-avatar { 14 | @extend .pull-left; 15 | margin-right: 8px; 16 | } 17 | .topic-item-title { 18 | @extend .media-heading; 19 | margin-bottom: 3px; 20 | } 21 | .topic-item-info { 22 | @extend .muted; 23 | font-size: 12px; 24 | } 25 | .replies-count { 26 | @extend .pull-right, .badge, .badge-info; 27 | margin: 10px 10px 0 0; 28 | } 29 | 30 | 31 | //topic show 32 | .topic-body { 33 | margin-bottom: $baseLineHeight * 2; 34 | } 35 | 36 | //reply 37 | .reply-form { 38 | margin-top: $baseLineHeight * 2; 39 | } 40 | .reply-item { 41 | @extend .topic-item; 42 | } 43 | .reply-item-avatar { 44 | @extend .topic-item-avatar; 45 | } 46 | .reply-body { 47 | margin-left: 56px; 48 | margin-top: 3px; 49 | } 50 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_user-bar.css.scss: -------------------------------------------------------------------------------- 1 | .user-bar { 2 | @extend .horizontal; 3 | margin: 0; 4 | li { 5 | margin-left: 10px; 6 | line-height: $navbarHeight; 7 | > .btn { 8 | margin-top: 0; 9 | } 10 | } 11 | .gravatar{ 12 | margin-right: 5px; 13 | } 14 | } 15 | 16 | .signed-in { 17 | .user-bar a { 18 | color: $grayDarker; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/modules/_user-profile.css.scss: -------------------------------------------------------------------------------- 1 | .user-title, .user-title small { 2 | font-size: $baseFontSize * 1.5; 3 | line-height: $baseLineHeight * 1.5; 4 | } 5 | .user-title { 6 | max-width: 100%; 7 | word-break: break-all; 8 | margin: 0 0 4px 0; 9 | > small { 10 | margin-left: 4px; 11 | } 12 | } 13 | 14 | ul.user-info > li { 15 | padding-left: 0; 16 | padding-right: 14px; 17 | } 18 | 19 | .user-body { 20 | margin-top: $baseLineHeight; 21 | color: $gray; 22 | } 23 | -------------------------------------------------------------------------------- /app/constraints/slug_constraint.rb: -------------------------------------------------------------------------------- 1 | class SlugConstraint 2 | def self.matches?(request) 3 | Group.exists? :slug => request.path_parameters[:slug] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/admin/order_fulfillments_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::OrderFulfillmentsController < ApplicationController 2 | include HasApiResponse 3 | before_filter :authenticate_user! 4 | before_filter :authorize_order! 5 | 6 | def index 7 | @orders = EventOrder.paid.where(require_invoice: true).includes(:items).order('paid_amount_in_cents asc, id asc') 8 | end 9 | 10 | def create 11 | order = EventOrder.find(params[:order_id]) 12 | @fulfillment = order.create_fulfillment!(params.fetch(:fulfillment).permit(:tracking_number)) 13 | end 14 | 15 | private 16 | def authorize_order! 17 | authorize! :manage_fulfillment, EventOrder 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/admin_orders_controller.rb: -------------------------------------------------------------------------------- 1 | class AdminOrdersController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :authorize_order! 4 | 5 | def index 6 | @orders = [] 7 | @orders = EventOrder.where(number: params[:number]) unless params[:number].blank? 8 | end 9 | 10 | def pay 11 | @order = EventOrder.find params[:id] 12 | @order.pay! if @order.pending? 13 | redirect_to admin_orders_path(number: @order.number) 14 | end 15 | 16 | private 17 | def authorize_order! 18 | authorize! :confirm_pay, EventOrder 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/api/events_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | class EventsController < ApplicationController 3 | def participants 4 | @event = Event.find(params[:id]) 5 | @users = @event.ordered_users.recent(10) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/autocomplete_controller.rb: -------------------------------------------------------------------------------- 1 | class AutocompleteController < ApplicationController 2 | before_filter :authenticate_user! 3 | def users 4 | @users = User.where("login like ?", "#{params[:q]}%").select('login').limit(20) 5 | render json: @users.map(&:login) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/collaborators_controller.rb: -------------------------------------------------------------------------------- 1 | class CollaboratorsController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :authorize_group! 4 | set_tab :collaborators 5 | 6 | def index 7 | @collaborators = @group.collaborators.map do |collaborator| 8 | {id: collaborator.id, login: collaborator.user.login, avatar_url: collaborator.user.gravatar_url(size: 50)} 9 | end 10 | end 11 | 12 | def create 13 | user = User.where(login: params[:login]).first! 14 | collaborator = @group.collaborators.create user_id: user.id 15 | render json: {id: collaborator.id, login: user.login, avatar_url: user.gravatar_url(size: 50)} 16 | end 17 | 18 | def destroy 19 | collaborator = @group.collaborators.find(params[:id]) 20 | collaborator.destroy 21 | render nothing: true 22 | end 23 | 24 | private 25 | def authorize_group! 26 | @event = Event.find(params[:event_id]) 27 | @group = @event.group 28 | authorize! :update, @group 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/concerns/alipay_generatable.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | module AlipayGeneratable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | hide_action :generate_pay_link_by_order if respond_to?(:hide_action) 7 | helper_method :generate_pay_link_by_order 8 | end 9 | 10 | def generate_pay_link_by_order(order) 11 | event = order.event 12 | options = { 13 | :out_trade_no => order.number, 14 | :subject => "#{event.title} 门票", 15 | :logistics_type => 'DIRECT', 16 | :logistics_fee => '0', 17 | :logistics_payment => 'SELLER_PAY', 18 | :price => order.price, 19 | :quantity => 1, 20 | :discount => 0, 21 | :return_url => alipay_done_user_order_url(order), # localhost isn't work http://bit.ly/1cwKbsw 22 | :notify_url => alipay_notify_user_order_url(order) 23 | } 24 | Alipay::Service.create_direct_pay_by_user_url(options) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/concerns/has_api_response.rb: -------------------------------------------------------------------------------- 1 | # TODO: handle authentication error 2 | module HasApiResponse 3 | extend ActiveSupport::Concern 4 | 5 | # rails http response status code to symbol mapping 6 | # http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option 7 | 8 | included do 9 | rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid_error 10 | end 11 | 12 | protected 13 | def render_record_invalid_error(exception) 14 | payload = { 15 | result: 'error', 16 | type: 'record_invalid', 17 | errors: exception.record.errors.full_messages 18 | } 19 | 20 | render json: payload, status: :not_acceptable 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/event/invoices_controller.rb: -------------------------------------------------------------------------------- 1 | class Event::InvoicesController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :authorize_event! 4 | 5 | def index 6 | @orders = @event.orders.paid.where(require_invoice: true).includes(:items).order('paid_amount_in_cents asc, id asc') 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/event_changes_controller.rb: -------------------------------------------------------------------------------- 1 | class EventChangesController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :authorize_event! 4 | set_tab :change 5 | set_tab :new_change, :sidebar, only: [:new] 6 | set_tab :changes , :sidebar, only: [:index] 7 | 8 | def index 9 | @changes = @event.updates 10 | end 11 | 12 | def new 13 | @change = @event.updates.build 14 | end 15 | 16 | def create 17 | @change = @event.updates.build(event_change_params) 18 | if @change.save 19 | redirect_to event_changes_path(@event) 20 | else 21 | render :new 22 | end 23 | end 24 | 25 | private 26 | 27 | def event_change_params 28 | params.require(:event_change).permit :content 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/event_summaries_controller.rb: -------------------------------------------------------------------------------- 1 | class EventSummariesController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :authorize_event! 4 | set_tab :event_summary, only: :new 5 | 6 | def new 7 | @summary = @event.event_summary || @event.build_event_summary 8 | render :new 9 | end 10 | 11 | def create 12 | @summary = @event.build_event_summary(event_summary_params) 13 | 14 | if @summary.save 15 | redirect_to event_path(@event) 16 | else 17 | render :new 18 | end 19 | end 20 | 21 | def update 22 | @summary = @event.event_summary 23 | 24 | if @summary.update_attributes(event_summary_params) 25 | redirect_to event_path(@event) 26 | else 27 | render :new 28 | end 29 | end 30 | 31 | private 32 | 33 | def event_summary_params 34 | params.require(:event_summary).permit :content 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/controllers/export_controller.rb: -------------------------------------------------------------------------------- 1 | class ExportController < ApplicationController 2 | prepend_before_filter :authenticate_user! 3 | set_tab :export 4 | 5 | def index 6 | @event = Event.find(params[:event_id]) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/group_controller.rb: -------------------------------------------------------------------------------- 1 | class GroupController < ApplicationController 2 | def event 3 | group = Group.where(slug: params[:slug]).first! 4 | @event = group.events.latest.first 5 | render 'events/show' 6 | end 7 | 8 | def followers 9 | group = Group.where(slug: params[:slug]).first! 10 | @event = group.events.latest.first 11 | render 'events/followers' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | respond_to do |format| 4 | format.html # index.html.erb 5 | format.json { render json: @events } 6 | end 7 | end 8 | 9 | def content_preview 10 | render :nothing => true, :status => 200, 11 | :json => {:result => ContentFilter.refine(params[:content]) } 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/mockup_controller.rb: -------------------------------------------------------------------------------- 1 | class MockupController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/participants_controller.rb: -------------------------------------------------------------------------------- 1 | class ParticipantsController < ApplicationController 2 | include HasApiResponse 3 | before_filter :authenticate_user! 4 | before_filter :authorize_event! 5 | set_tab :check_in 6 | set_tab :participants_checkin, :sidebar, only: [:checkin] 7 | set_tab :participants_index , :sidebar, only: [:index] 8 | 9 | rescue_from ActiveRecord::RecordNotFound, with: :render_record_no_found_error 10 | 11 | def index 12 | @participants = @event.participants.joins(:user).order('id DESC').includes(:user => :profile) 13 | end 14 | 15 | def export 16 | @orders = @event.orders.where(status: :paid).includes(:participant).order('event_order_participants.checkin_code') 17 | end 18 | 19 | def checkin 20 | end 21 | 22 | def update 23 | @participant = @event.participants.where(checkin_code: params[:code]).first! 24 | @participant.checkin! 25 | end 26 | 27 | private 28 | def render_record_no_found_error(exception) 29 | render json: { errors: [I18n.t('errors.messages.event_order_participant.invalid_code')] }, status: :not_found 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/photo_controller.rb: -------------------------------------------------------------------------------- 1 | class PhotoController < ApplicationController 2 | before_filter :authenticate_user! 3 | 4 | def create 5 | files = params[:files].map do |file| 6 | photo = current_user.photos.create :image => file 7 | image_url = photo.image_url 8 | image_name = File.basename(image_url, File.extname(image_url)) 9 | { :name => image_name, :url => photo.image_url(:normal) } 10 | end 11 | render json: {files: files} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/profiles_controller.rb: -------------------------------------------------------------------------------- 1 | class ProfilesController < ApplicationController 2 | before_filter :authenticate_user! 3 | before_filter :find_profile 4 | layout 'settings' 5 | 6 | def show 7 | end 8 | 9 | def update 10 | @profile.assign_attributes(profile_params) 11 | 12 | if @profile.save 13 | redirect_to profile_path, :notice => I18n.t('flash.profiles.updated') 14 | else 15 | render :show 16 | end 17 | end 18 | 19 | private 20 | def find_profile 21 | @profile = current_user.profile 22 | end 23 | 24 | def profile_params 25 | params.require(:profile).permit :bio, :name, :website 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | include HasApiResponse 3 | layout 'settings', :only => [:edit, :update] 4 | 5 | def create 6 | if request.xhr? 7 | build_resource(sign_up_params) 8 | resource.save! 9 | sign_up(resource_name, resource) 10 | else 11 | super 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Devise::SessionsController 2 | def create 3 | if request.xhr? 4 | self.resource = warden.authenticate!(auth_options) 5 | sign_in(resource_name, resource) 6 | else 7 | super 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/topic_reply_controller.rb: -------------------------------------------------------------------------------- 1 | class TopicReplyController < ApplicationController 2 | before_filter :authenticate_user! 3 | 4 | def create 5 | @event = Event.find(params[:event_id]) 6 | @topic = @event.group.topics.find(params[:topic_id]) 7 | @reply = @topic.replies.build topic_reply_params 8 | @reply.user = current_user 9 | @reply.save 10 | redirect_to event_topic_path(@event, @topic) 11 | end 12 | 13 | private 14 | 15 | def topic_reply_params 16 | params.require(:group_topic_reply).permit :body 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/topics_controller.rb: -------------------------------------------------------------------------------- 1 | class TopicsController < ApplicationController 2 | before_filter :authenticate_user!, only: [:new, :create] 3 | before_filter :find_resource 4 | 5 | def new 6 | @topic = @group.topics.build 7 | end 8 | 9 | def create 10 | @topic = @group.topics.build group_topic_params 11 | @topic.user = current_user 12 | if @topic.save 13 | redirect_to event_path(@event) 14 | else 15 | render :new 16 | end 17 | end 18 | 19 | def show 20 | @topic = @group.topics.find(params[:id]) 21 | @replies = @topic.replies 22 | @reply = GroupTopicReply.new 23 | end 24 | 25 | private 26 | def find_resource 27 | @event = Event.find(params[:event_id]) 28 | @group = @event.group 29 | end 30 | 31 | def group_topic_params 32 | params.require(:group_topic).permit :body, :title 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | def show 3 | @user = User.friendly.find(params[:id]) 4 | @profile = @user.profile 5 | end 6 | 7 | def cohort 8 | non_guests = User.where("invitation_accepted_at IS NOT NULL").all 9 | @period = "weeks" 10 | activation_conditions = ["user_id IN (?)", non_guests] 11 | @cohorts = CohortMe.analyze(period: @period, 12 | activation_class: Event, 13 | activation_conditions: activation_conditions) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/helpers/admin/order_fulfillment_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin::OrderFulfillmentHelper 2 | def init_event_order_fulfillments(orders = @orders) 3 | orders = Jbuilder.new do |json| 4 | json.array! orders do |order| 5 | json.(order, :id, :number, :paid_amount) 6 | json.user order.user, :login, :email 7 | json.address order.shipping_address, :invoice_title, :info if order.shipping_address 8 | json.fulfillment order.fulfillment, :tracking_number if order.fulfillment 9 | items = order.items.select(&:require_invoice) 10 | json.items items do |item| 11 | json.(item, :price, :unit_price, :quantity, :name, :require_invoice) 12 | end 13 | end 14 | end.attributes! # http://git.io/DQj7LQ 15 | options = { orders: orders } 16 | options.to_ng_init 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/event_order_helper.rb: -------------------------------------------------------------------------------- 1 | module EventOrderHelper 2 | def stats_tickets_quantity(orders) 3 | orders.map(&:quantity).reduce(&:+) || 0 4 | end 5 | 6 | def stats_tickets_price(orders) 7 | (orders.map(&:paid_amount_in_cents).reduce(&:+) || 0) / 100.0 8 | end 9 | 10 | def init_event_orders(orders = @orders) 11 | orders = Jbuilder.new do |json| 12 | json.array! orders do |order| 13 | refunding = order.refunds.refunding 14 | json.(order, :id, :number, :price, :paid_amount, :paid_amount_in_cents) 15 | json.refund refunding, :amount, :reason if refunding 16 | json.user order.user, :login, :email if order.user 17 | json.items order.items, :price, :unit_price, :quantity, :name 18 | end 19 | end.attributes! # http://git.io/DQj7LQ 20 | options = { orders: orders } 21 | options.to_ng_init 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/helpers/event_ticket_helper.rb: -------------------------------------------------------------------------------- 1 | module EventTicketHelper 2 | def init_tickets(event) 3 | tickets = event.tickets.opened.select([:id, :name, :price_in_cents, :require_invoice, :description]).to_a 4 | tickets.map! do |ticket| 5 | item = ticket.as_json(methods: 'price') 6 | item[:quantity] = 0 7 | item 8 | end 9 | options = { tickets: tickets } 10 | options.to_ng_init 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | 3 | def active?(current, menu) 4 | current == menu && :active 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /app/helpers/user_helper.rb: -------------------------------------------------------------------------------- 1 | module UserHelper 2 | def init_user 3 | options = { 4 | 'user.id' => current_user.id, 5 | 'user.name' => current_user.name, 6 | 'user.phone' => current_user.phone 7 | } 8 | options.to_ng_init 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/user_orders_helper.rb: -------------------------------------------------------------------------------- 1 | module UserOrdersHelper 2 | def pay_link(order) 3 | if order.can_pay? 4 | link_to t('views.my_orders.pay'), pay_user_order_path(order), class: 'btn btn-info' 5 | end 6 | end 7 | 8 | def cancel_link(order) 9 | if order.can_cancel? 10 | link_to t('views.my_orders.cancel'), cancel_user_order_path(order), 11 | data: {confirm: t('confirmations.my_orders.cancel')}, 12 | class: 'btn', 13 | method: 'put' 14 | end 15 | end 16 | 17 | def request_refund_link(order) 18 | if !order.free? && order.paid? && order.can_request_refund? 19 | link_to t('views.my_orders.request_refund'), request_refund_user_order_path(order), 20 | data: {confirm: t('confirmations.my_orders.refund')}, 21 | class: 'btn', 22 | method: 'put' 23 | end 24 | end 25 | 26 | def operations(order) 27 | [pay_link(order), cancel_link(order), request_refund_link(order)].compact.join(' | ').html_safe 28 | end 29 | 30 | def orders_filtered? 31 | params[:event_id].present? 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/inputs/upload_photo_input.rb: -------------------------------------------------------------------------------- 1 | class UploadPhotoInput < SimpleForm::Inputs::Base 2 | 3 | def input 4 | template.tag(:input, :name => 'files[]', :type => 'file', :'data-url' => '/photos', :multiple => true, :class => 'fileupload') 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/mailers/event_mailer.rb: -------------------------------------------------------------------------------- 1 | class EventMailer < ActionMailer::Base 2 | default from: Settings.email[:from] 3 | 4 | def change_email(change, user) 5 | @change = change 6 | @event = change.event 7 | @user = user 8 | mail(to: user.email_with_login, subject: I18n.t('email.event.change.subject', title: @event.title)) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/mailers/order_fulfillment_mailer.rb: -------------------------------------------------------------------------------- 1 | class OrderFulfillmentMailer < ActionMailer::Base 2 | default from: Settings.email[:from] 3 | 4 | def notify_user_fulfilled(fulfillment) 5 | @fulfillment = fulfillment 6 | @order = @fulfillment.order 7 | @event = @order.event 8 | @user = @order.user 9 | mail(to: @user.email_with_login, subject: I18n.t('email.order.user.fulfilled.subject', title: @event.title, number: @order.number)) 10 | end 11 | 12 | def notify_organizer_fulfilled(fulfillment) 13 | @fulfillment = fulfillment 14 | @order = @fulfillment.order 15 | @event = @order.event 16 | @user = @order.user 17 | mail(to: @event.user.email_with_login, subject: I18n.t('email.order.organizer.fulfilled.subject', title: @event.title, number: @order.number)) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | helper :event 3 | default from: Settings.email[:from] 4 | 5 | def welcome_email(user) 6 | mail(:to => user.email, :subject => I18n.t('email.welcome.subject')) 7 | end 8 | 9 | def invited_email(user) 10 | @user = user 11 | mail(:to => user.email, :subject => I18n.t('email.invited.subject', :login => user.login)) 12 | end 13 | 14 | def notify_email(user, event) 15 | @user = user 16 | @event = event 17 | mail(:to => user.email, :subject => I18n.t('email.notify.subject', :title => event.title)) 18 | end 19 | 20 | def reminder_email(user, event) 21 | @user = user 22 | @event = event 23 | mail(:to => user.email, :subject => I18n.t('email.reminder.subject')) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/menus/event_menu.rb: -------------------------------------------------------------------------------- 1 | class EventMenu < TabsOnRails::Tabs::Builder 2 | def open_tabs(options = {}) 3 | @context.tag("ul", {class: 'nav'}.merge(options), open = true) 4 | end 5 | 6 | def tab_for(tab, name, options, item_options = {}) 7 | item_options[:class] = (current_tab?(tab) ? 'active' : '') 8 | @context.content_tag(:li, item_options) do 9 | @context.link_to(name, options) 10 | end + @context.tag("li", class: 'divider-vertical') 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/menus/event_orders_submenu.rb: -------------------------------------------------------------------------------- 1 | class EventOrdersSubmenu < TabsOnRails::Tabs::Builder 2 | def open_tabs(options = {}) 3 | @context.tag("ul", {class: ['nav', 'nav-list']}.merge(options), open = true) 4 | end 5 | 6 | def tab_for(tab, name, path, current_path, item_options = {}) 7 | item_options[:class] = (path == current_path ? 'active' : '') 8 | @context.content_tag(:li, item_options) do 9 | @context.link_to(name, path) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/menus/event_submenu.rb: -------------------------------------------------------------------------------- 1 | class EventSubmenu < TabsOnRails::Tabs::Builder 2 | def open_tabs(options = {}) 3 | @context.tag("ul", {class: ['nav', 'nav-list']}.merge(options), open = true) 4 | end 5 | 6 | def tab_for(tab, name, options, item_options = {}) 7 | item_options[:class] = (current_tab?(tab) ? 'active' : '') 8 | @context.content_tag(:li, item_options) do 9 | @context.link_to(name, options) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/middlewares/fallback_url_redirector.rb: -------------------------------------------------------------------------------- 1 | class FallbackUrlRedirector 2 | def initialize(app) 3 | @app = app 4 | end 5 | 6 | def call(env) 7 | status, headers, body = 8 | begin 9 | @app.call(env) 10 | rescue ActiveRecord::RecordNotFound 11 | if path = find_path(env) 12 | return [301, { 'Location' => path }, ["Redirecting you to #{path}"]] 13 | end 14 | raise 15 | end 16 | [status, headers, body] 17 | end 18 | 19 | 20 | private 21 | def find_path(env) 22 | path = env['PATH_INFO'].sub(/^\//, '') 23 | (path.present? && url = FallbackUrl.find_by_origin(path)) ? url.change_to : nil 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | user ||= User.new # guest user (not logged in) 6 | can :create, Event if user.invitation_accepted? or user.collaborator? 7 | can :update, Event do |event| 8 | event.user_id == user.id or event.group.collaborator?(user) 9 | end 10 | can :update, Group, user_id: user.id 11 | can :manage, :all if user.admin? 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/content_filter.rb: -------------------------------------------------------------------------------- 1 | require 'html/pipeline' 2 | 3 | class ContentFilter 4 | 5 | def self.refine(content) 6 | filters = HasHtmlPipeline.registered_html_pipelines[:markdown] 7 | filters.call(content)[:output].to_s 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/models/event_change.rb: -------------------------------------------------------------------------------- 1 | class EventChange < ActiveRecord::Base 2 | belongs_to :event 3 | validates :content, length: { maximum: 100 } 4 | 5 | after_create do 6 | event.ordered_users.each do |user| 7 | EventMailer.delay.change_email(self, user) 8 | end 9 | phones = event.ordered_users.with_phone.map(&:phone).sort 10 | # 活动通知的内容无法通过短信格式备案,暂时不发送 11 | #ChinaSMS.delay.to phones, I18n.t('sms.event.change', content: content) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/event_observer.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class EventObserver < ActiveRecord::Observer 3 | def before_create(event) 4 | save_group event 5 | end 6 | 7 | def after_create(event) 8 | notify_followers event 9 | event.tickets.create name: '门票', price: 0 10 | end 11 | 12 | def before_update(event) 13 | if event.slug 14 | group = event.group 15 | if group and group.slug != event.slug and group.events.size <= 1 16 | group.destroy 17 | fallback_url = FallbackUrl.find_or_initialize_by origin: group.slug 18 | fallback_url.change_to = event.slug 19 | fallback_url.save 20 | end 21 | save_group event 22 | end 23 | end 24 | 25 | def after_find(event) 26 | event.slug ||= event.group.try(:slug) 27 | end 28 | 29 | private 30 | 31 | def save_group(event) 32 | group = event.user.groups.where(:slug => event.slug).first_or_create! 33 | event.group = group 34 | end 35 | 36 | def notify_followers(event) 37 | event.group.followers.each do |follower| 38 | UserMailer.delay.notify_email follower, event 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/models/event_order_fulfillment.rb: -------------------------------------------------------------------------------- 1 | class EventOrderFulfillment < ActiveRecord::Base 2 | belongs_to :order, class_name: 'EventOrder' 3 | validates :tracking_number, presence: true 4 | validates :tracking_number, length: { is: 12 }, allow_blank: true 5 | 6 | after_create do 7 | OrderFulfillmentMailer.delay.notify_user_fulfilled(self) 8 | OrderFulfillmentMailer.delay.notify_organizer_fulfilled(self) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/event_order_observer.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class EventOrderObserver < ActiveRecord::Observer 3 | def after_pay(order, transition) 4 | order.create_participant 5 | order.increment! :paid_amount_in_cents, order.price_in_cents # convenient for refunding 6 | OrderMailer.delay.notify_user_paid(order) 7 | OrderMailer.delay.notify_organizer_paid(order) 8 | end 9 | 10 | def after_cancel(order, transition) 11 | order.event.increment! :tickets_quantity, order.quantity if order.event.tickets_quantity 12 | end 13 | 14 | def after_close(order, transition) 15 | order.event.increment! :tickets_quantity, order.quantity if order.event.tickets_quantity 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/event_order_refund_observer.rb: -------------------------------------------------------------------------------- 1 | class EventOrderRefundObserver< ActiveRecord::Observer 2 | def after_refund(refund, transition) 3 | order = refund.order 4 | order.decrement! :paid_amount_in_cents, refund.amount_in_cents 5 | order.cancel if order.paid_amount_in_cents <= 0 6 | OrderMailer.delay.notify_organizer_refunded(refund) 7 | OrderMailer.delay.notify_user_refunded(refund) 8 | end 9 | 10 | def after_submit(refund, transition) 11 | OrderMailer.delay.notify_support_refund(refund) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/event_order_shipping_address.rb: -------------------------------------------------------------------------------- 1 | class EventOrderShippingAddress < ActiveRecord::Base 2 | belongs_to :order 3 | validates :invoice_title, :province, :city, :district, :address, :name, :phone, presence: true 4 | 5 | def info 6 | "#{full_address} #{name} #{phone}" 7 | end 8 | 9 | def full_address 10 | "#{ChinaCity.get(province)}#{ChinaCity.get(city)}#{ChinaCity.get(district)} #{address}" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/event_order_status_transition.rb: -------------------------------------------------------------------------------- 1 | class EventOrderStatusTransition < ActiveRecord::Base 2 | belongs_to :event_order 3 | end 4 | -------------------------------------------------------------------------------- /app/models/event_summary.rb: -------------------------------------------------------------------------------- 1 | class EventSummary < ActiveRecord::Base 2 | extend HasHtmlPipeline 3 | 4 | belongs_to :event 5 | 6 | has_html_pipeline :content, :markdown 7 | 8 | validates_presence_of :content 9 | validates :content, :length => { :minimum => 10 } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/event_ticket.rb: -------------------------------------------------------------------------------- 1 | class EventTicket < ActiveRecord::Base 2 | belongs_to :event 3 | priceable :price 4 | validates :name, :price, presence: true 5 | validates :name, :description, length: { maximum: 255 } 6 | attr_writer :tickets_quantity 7 | delegate :tickets_quantity, to: :event 8 | 9 | scope :opened, -> { where(status: 'opened') } 10 | 11 | state_machine :status, :initial => :opened do 12 | event :close do 13 | transition :opened => :closed 14 | end 15 | 16 | event :reopen do 17 | transition :closed => :opened 18 | end 19 | end 20 | 21 | before_save do 22 | event.update_column :tickets_quantity, @tickets_quantity if @tickets_quantity.present? 23 | end 24 | 25 | after_destroy do 26 | event.update_column :tickets_quantity, nil if event.tickets.count.zero? 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/fallback_url.rb: -------------------------------------------------------------------------------- 1 | class FallbackUrl < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/models/follow.rb: -------------------------------------------------------------------------------- 1 | class Follow < ActiveRecord::Base 2 | 3 | extend ActsAsFollower::FollowerLib 4 | extend ActsAsFollower::FollowScopes 5 | 6 | # NOTE: Follows belong to the "followable" interface, and also to followers 7 | belongs_to :followable, :polymorphic => true 8 | belongs_to :follower, :polymorphic => true 9 | 10 | def block! 11 | self.update_attribute(:blocked, true) 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/models/group.rb: -------------------------------------------------------------------------------- 1 | class Group < ActiveRecord::Base 2 | belongs_to :user 3 | has_many :events 4 | has_many :collaborators, class_name: "GroupCollaborator" 5 | has_many :topics, class_name: "GroupTopic" 6 | acts_as_followable 7 | 8 | def collaborator?(user) 9 | self.collaborators.exists?(user_id: user.id) 10 | end 11 | 12 | def last_event_with_summary 13 | events.latest.each do |event| 14 | return event unless event.event_summary.nil? 15 | end 16 | nil 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/group_collaborator.rb: -------------------------------------------------------------------------------- 1 | class GroupCollaborator < ActiveRecord::Base 2 | belongs_to :group 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /app/models/group_topic.rb: -------------------------------------------------------------------------------- 1 | class GroupTopic < ActiveRecord::Base 2 | extend HasHtmlPipeline 3 | belongs_to :group 4 | belongs_to :user 5 | has_many :replies, class_name: "GroupTopicReply" 6 | validates :title, :body, presence: true 7 | has_html_pipeline :body, :markdown 8 | end 9 | -------------------------------------------------------------------------------- /app/models/group_topic_reply.rb: -------------------------------------------------------------------------------- 1 | class GroupTopicReply < ActiveRecord::Base 2 | extend HasHtmlPipeline 3 | belongs_to :topic, class_name: 'GroupTopic', counter_cache: 'replies_count', foreign_key: 'group_topic_id' 4 | belongs_to :user 5 | validates :body, presence: true 6 | has_html_pipeline :body, :markdown 7 | end 8 | -------------------------------------------------------------------------------- /app/models/photo.rb: -------------------------------------------------------------------------------- 1 | class Photo < ActiveRecord::Base 2 | mount_uploader :image, PhotoUploader 3 | end 4 | -------------------------------------------------------------------------------- /app/models/profile.rb: -------------------------------------------------------------------------------- 1 | class Profile < ActiveRecord::Base 2 | extend HasHtmlPipeline 3 | 4 | belongs_to :user 5 | validates :name, length: { maximum: 20 } 6 | 7 | has_html_pipeline :bio, :profile_markdown 8 | end 9 | -------------------------------------------------------------------------------- /app/models/refund_batch.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class RefundBatch < ActiveRecord::Base 3 | has_many :refunds, class_name: 'EventOrderRefund', dependent: :nullify 4 | validates :batch_no, presence: true 5 | 6 | before_validation do 7 | self.batch_no ||= Alipay::Utils.generate_batch_no # 生成批次号,避免重复退款 8 | end 9 | 10 | def refund_link 11 | data = self.refunds.map do |refund| 12 | { 13 | trade_no: refund.order.trade_no, 14 | amount: refund.amount, 15 | reason: refund.reason 16 | } 17 | end 18 | options = { 19 | batch_no: self.batch_no, 20 | data: data, 21 | notify_url: Rails.application.routes.url_helpers.refunds_alipay_notify_url(host: Settings.host) 22 | } 23 | logger.info data 24 | Alipay::Service.create_refund_url(options) 25 | end 26 | 27 | state_machine :status, :initial => :pending do 28 | state :pending, :completed 29 | 30 | event :complete do 31 | transition :pending => :completed 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/models/sequence.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class Sequence < ActiveRecord::Base 3 | def self.get 4 | date = Time.zone.today 5 | transaction do 6 | sequence = self.lock.where(date: date).first_or_create number: (Rails.env.production? ? 1000 : 0) # 生产环境从1000起计,避免与支付宝集成时 outer_trade_no 冲突 7 | sequence.increment! :number 8 | "#{date.to_s(:number)}#{sequence.number.to_s.rjust(4, '0')}" # TODO: exceed? 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/settings.rb: -------------------------------------------------------------------------------- 1 | # Static settings loaded from the YAML file 2 | class Settings < Settingslogic 3 | source Rails.root.join('config/settings.yml') 4 | namespace Rails.env 5 | 6 | # '19wu ' => 'support@19wu.com' 7 | def self.raw_email(email) 8 | email.gsub(/(.*<|>)/, '') 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/views/admin/order_fulfillments/create.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(@fulfillment, :id, :tracking_number) 2 | -------------------------------------------------------------------------------- /app/views/api/events/participants.rabl: -------------------------------------------------------------------------------- 1 | collection @users, object_root: false 2 | attributes :id, :login, :created_at 3 | 4 | child :profile do 5 | attributes :name, :website, :bio_html 6 | end 7 | 8 | node(:avatar_url) {|u| u.gravatar_url(size: 48) } 9 | -------------------------------------------------------------------------------- /app/views/application/403.html.slim: -------------------------------------------------------------------------------- 1 | = I18n.t("unauthorized.default") 2 | -------------------------------------------------------------------------------- /app/views/application/_clippy.html.slim: -------------------------------------------------------------------------------- 1 | - bgcolor ||= '#FFFFFF' 2 | - text = CGI::escape(text) 3 | object#clippy classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="110" height="14" 4 | param name="movie" value="/flash/clippy.swf" 5 | param name="allowScriptAccess" value="always" 6 | param name="quality" value="high" 7 | param name="scale" value="noscale" 8 | param name="FlashVars" value='text=#{text}' 9 | param name="bgcolor" value="#{bgcolor}" 10 | embed[src="/flash/clippy.swf" 11 | width="110" 12 | height="14" 13 | name="clippy" 14 | quality="high" 15 | allowScriptAccess="always" 16 | type="application/x-shockwave-flash" 17 | pluginspage="http://www.macromedia.com/go/getflashplayer" 18 | FlashVars='text=#{text}' 19 | bgcolor="#{bgcolor}"] 20 | -------------------------------------------------------------------------------- /app/views/application/_flashes.html.slim: -------------------------------------------------------------------------------- 1 | #flash 2 | - flash.each do |key, value| 3 | .alert title=key.to_s.humanize class=flash_class(key) 4 | = render_close_icon 5 | div = value 6 | -------------------------------------------------------------------------------- /app/views/application/_signed_in_nav_bar.html.slim: -------------------------------------------------------------------------------- 1 | ul.nav 2 | - if can? :invite, User 3 | li = link_to t('labels.invitations'), invitations_path 4 | - if can? :refund, EventOrder 5 | li = link_to t('activerecord.models.event_order_refund'), refunds_path 6 | - if can? :admin, EventOrder 7 | li = link_to t('labels.admin_orders'), admin_orders_path 8 | li = link_to t('labels.invoice_orders'), admin_fulfillments_path 9 | li = link_to t('labels.my_orders'), user_orders_path 10 | -------------------------------------------------------------------------------- /app/views/application/_signed_in_user_bar.html.slim: -------------------------------------------------------------------------------- 1 | ul.user-bar.pull-right ng-init=init_user 2 | li 3 | = link_to user_path(current_user), :title => t('devise.navigation.home') do 4 | = image_tag current_user.gravatar_url(:size => 20), :class => 'gravatar' 5 | = current_user.login 6 | li 7 | = link_to account_path, :title => t('views.profiles.show.title') do 8 | i.icon-wrench 9 | li 10 | = link_to destroy_user_session_path, :title => t('devise.navigation.sign_out'), :method => :delete do 11 | i.icon-off 12 | -------------------------------------------------------------------------------- /app/views/application/_signed_out_user_bar.html.slim: -------------------------------------------------------------------------------- 1 | ul.user-bar.pull-right 2 | - unless ['home', 'registrations'].include?(controller_name) 3 | li= link_to t('labels.sign_up'), new_user_registration_path, :class => "btn btn-success" 4 | - if controller_name != 'sessions' 5 | li= link_to t('labels.sign_in'), new_user_session_path, :class => "btn" 6 | - else 7 | li= link_to t('devise.views.links.resend_confirmation'), new_confirmation_path(resource_name), :class => "btn btn-link" 8 | -------------------------------------------------------------------------------- /app/views/collaborators/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .collaborators-list ng-controller='CollaboratorsCtrl' ng-init='items = #{@collaborators.to_json}' ng-cloak="" 3 | .list 4 | .item.clearfix ng-repeat="item in items" 5 | .avatar-wrapper 6 | img.gravatar ng-src="{{item.avatar_url}}" 7 | a ng-href="/{{item.login}}" {{item.login}} 8 | a.text-error href='javascript:void(0)' ng-click="remove($index)" (删除) 9 | form.form-inline ng-submit="add()" 10 | input.span3 bs-typeahead='load' ng-model='login' type='text' placeholder='输入他的用户名...' 11 | button.btn type='submit' = t('helpers.submit.add') 12 | p.alert.alert-error ng-show='error' 此用户已经是协办者 13 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| 4 | fieldset 5 | legend 6 | = t('devise.views.confirmations.new.title') 7 | small.pull-right 8 | = link_to t('devise.views.links.forget_pass'), new_password_path(resource_name) 9 | 10 | .form-inputs 11 | = f.input :email 12 | .control-group 13 | = f.submit t('devise.views.confirmations.new.submit'), :class => 'btn btn-primary' 14 | -------------------------------------------------------------------------------- /app/views/devise/invitations/edit.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :patch, :class => 'form-full-width' } do |f| 4 | fieldset 5 | legend = t 'devise.invitations.edit.header' 6 | = devise_error_messages! 7 | = f.hidden_field :invitation_token 8 | = f.input :login 9 | = f.input :password 10 | = f.input :password_confirmation 11 | .control-group 12 | = f.submit t("devise.invitations.edit.submit_button"), :class => ['btn', 'btn-info'] 13 | -------------------------------------------------------------------------------- /app/views/devise/invitations/new.html.slim: -------------------------------------------------------------------------------- 1 | h2= t "devise.invitations.new.header" 2 | = simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| 3 | = devise_error_messages! 4 | - resource.class.invite_key_fields.each do |field| 5 | = f.input field 6 | = f.submit t("devise.invitations.new.submit_button") 7 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | Welcome #{@resource.login}! 3 | p You can confirm your account email through the link below: 4 | p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) 5 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.text.erb: -------------------------------------------------------------------------------- 1 | Welcome <%= @resource.login %>! 2 | 3 | You can confirm your account email through the link below: 4 | 5 | [Confirm my account]( <%= confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %> ) 6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | #{@resource.login},欢迎! 3 | p 您可以通过下面的链接来激活帐号: 4 | p= link_to '激活帐号', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) 5 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.zh-CN.text.erb: -------------------------------------------------------------------------------- 1 | <%= @resource.login %>,欢迎! 2 | 3 | 您可以通过下面的链接来激活帐号: 4 | 5 | [激活帐号]( <%= confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %> ) 6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/invitation_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 非常感谢您申请注册19屋,以下为注册邀请链接 2 | 3 | p = link_to accept_invitation_url(@resource, :invitation_token => @resource.invitation_token), accept_invitation_url(@resource, :invitation_token => @resource.invitation_token), title: '接受邀请' 4 | 5 | p 19屋是一个活动平台,专注于为组织者提供便利的活动发布、推广、售票等免费服务。现在您可以在19屋进行发布活动,签到等操作。 6 | 7 | p 8 | |如需帮助,请发邮件到 9 | = link_to 'mahb45@gmail.com', 'mailto:mahb45@gmail.com' 10 | 11 | p 19屋 saberma 12 | br 13 | 14 | p --- 15 | a href='http://19wu.com' http://19wu.com 16 | br 17 | a href='https://github.com/19wu/19wu' https://github.com/19wu/19wu 18 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | Hello #{@resource.login}! 3 | p Someone has requested a link to change your password, and you can do this through the link below. 4 | p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) 5 | p If you didn't request this, please ignore this email. 6 | p Your password won't change until you access the link above and create a new one. 7 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.text.erb: -------------------------------------------------------------------------------- 1 | Hello <%= @resource.login %>! 2 | 3 | Someone has requested a link to change your password, and you can do this through the link below. 4 | 5 | [Change my password] ( <%= edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %> ) 6 | 7 | If you didn't request this, please ignore this email. 8 | 9 | Your password won't change until you access the link above and create a new one. 10 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | #{@resource.email} 您好, 3 | p 您可以通过下面的链接修改帐号密码: 4 | p= link_to '修改密码', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) 5 | p 如果您没有提出修改密码的请求,请忽略些邮件。 6 | p 除非点击上面的链接并设置新的密码,您的密码不会被更改。 7 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.zh-CN.text.erb: -------------------------------------------------------------------------------- 1 | <%= @resource.email %> 您好, 2 | 3 | 您可以通过下面的链接修改帐号密码: 4 | 5 | [修改密码] ( <%= edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %> ) 6 | 7 | 如果您没有提出修改密码的请求,请忽略些邮件。 8 | 9 | 除非点击上面的链接并设置新的密码,您的密码不会被更改。 10 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :patch }) do |f| 4 | 5 | fieldset 6 | legend 7 | = t('devise.views.passwords.edit.title') 8 | 9 | .form-inputs 10 | = f.hidden_field :reset_password_token 11 | = f.input :password 12 | = f.input :password_confirmation 13 | 14 | .control-group 15 | = f.submit t('devise.views.passwords.edit.submit'), :class => 'btn btn-primary' 16 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| 4 | fieldset 5 | legend 6 | = t('devise.views.passwords.new.title') 7 | small.pull-right 8 | = link_to t('devise.views.links.resend_confirmation'), new_confirmation_path(resource_name) 9 | 10 | .form-inputs 11 | = f.input :email 12 | .control-group 13 | = f.submit t('devise.views.passwords.new.submit'), :class => 'btn btn-primary' 14 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.slim: -------------------------------------------------------------------------------- 1 | = simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :patch, :class => 'form-horizontal' }) do |f| 2 | 3 | fieldset 4 | legend= t('devise.views.registrations.edit.title') 5 | 6 | .form-inputs 7 | = f.input :login 8 | = f.input :email 9 | = f.input :phone 10 | = f.input :password, :hint => t('devise.views.registrations.edit.change_pass_tip'), :input_html => { :autocomplete => 'off' } 11 | = f.input :password_confirmation 12 | 13 | = f.input :current_password, :hint => t('devise.views.registrations.edit.change_pass_confirm') 14 | 15 | .form-actions 16 | = f.button :submit, :class => 'btn-primary' 17 | 18 | .well 19 | h4 = t('devise.views.registrations.edit.cancel_account') 20 | p = t('devise.views.registrations.edit.cancel_account_warning') 21 | p.center = link_to t('devise.views.registrations.edit.cancel_account'), registration_path(resource_name), :confirm => t('devise.views.registrations.edit.cancel_account_confirm'), :method => :delete, :class => 'btn btn-danger' 22 | 23 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => 'form-full-width'}) do |f| 4 | fieldset 5 | legend = t('devise.views.registrations.new.title') 6 | = f.input :login, :label => false, :placeholder => resource.class.human_attribute_name(:login) 7 | = f.input :email, :label => false, :placeholder => resource.class.human_attribute_name(:email) 8 | = f.input :password, :label => false, :placeholder => resource.class.human_attribute_name(:password) 9 | .control-group 10 | = f.submit t('labels.sign_up'), :class => 'btn btn-success' 11 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => 'form-full-width' }) do |f| 4 | fieldset 5 | legend = t('devise.views.sessions.new.title') 6 | = render_flashes 7 | = f.label :email 8 | = f.email_field :email 9 | = f.label :password, render_password_label_with_forget_link(f.object) 10 | = f.password_field :password 11 | .control-group 12 | - if devise_mapping.rememberable? 13 | = f.hidden_field :remember_me, :value => true 14 | = f.submit t('devise.views.sessions.new.submit'), :class => ['btn', 'btn-info'] 15 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.slim: -------------------------------------------------------------------------------- 1 | ul.unstyled 2 | - if controller_name != 'sessions' 3 | li= link_to t('devise.views.links.sign_in'), new_session_path(resource_name) 4 | - if devise_mapping.registerable? && controller_name != 'registrations' 5 | li= link_to t('devise.views.links.sign_up'), new_registration_path(resource_name) 6 | - if devise_mapping.recoverable? && controller_name != 'passwords' 7 | li= link_to t('devise.views.links.forget_pass'), new_password_path(resource_name) 8 | - if devise_mapping.confirmable? && controller_name != 'confirmations' 9 | li= link_to t('devise.views.links.resend_confirmation'), new_confirmation_path(resource_name) 10 | - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' 11 | li= link_to t('devise.views.links.resend_unlock'), new_unlock_path(resource_name) 12 | - if devise_mapping.omniauthable? 13 | - resource_class.omniauth_providers.each do |provider| 14 | li= link_to t('devise.views.links.omni_auth', :provider => provider.to_s.titleize), omniauth_authorize_path(resource_name, provider) 15 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.slim: -------------------------------------------------------------------------------- 1 | h2 Resend unlock instructions 2 | = form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| 3 | = devise_error_messages! 4 | div 5 | = f.label :email 6 | br/ 7 | = f.email_field :email 8 | div= f.submit "Resend unlock instructions" 9 | = render "devise/shared/links" 10 | -------------------------------------------------------------------------------- /app/views/event/invoices/index.xlsx.axlsx: -------------------------------------------------------------------------------- 1 | wb = xlsx_package.workbook 2 | 3 | header_style = wb.styles.add_style alignment: { horizontal: :center }, sz: 16, b: true 4 | body_style = wb.styles.add_style alignment: { horizontal: :right, wrap_text: true } 5 | string_style = wb.styles.add_style alignment: { horizontal: :right }, num_fmt: 12 6 | 7 | wb.add_worksheet(name: @event.title) do |sheet| 8 | sheet.add_row ['订单编号', '抬头', '金额(元)', '商品', '收货地址', '姓名', '电话'], style: header_style 9 | @orders.each do |order| 10 | address = order.shipping_address 11 | items = order.items.map do |item| 12 | "#{item.quantity} x #{item.name}" 13 | end.join(", ") 14 | sheet.add_row [order.number, address.invoice_title, order.paid_amount, items, address.full_address, address.name, address.phone], style: body_style 15 | end 16 | sheet["A2:A#{@orders.size+1}"].each { |c| c.style = string_style } unless @orders.empty? # 订单号改为字符串格式 17 | end 18 | -------------------------------------------------------------------------------- /app/views/event_changes/_submenu.html.slim: -------------------------------------------------------------------------------- 1 | .span3 2 | .submenu 3 | = tabs_tag namespace: :sidebar, builder: EventSubmenu do |tab| 4 | = tab.new_change '发布通知', new_event_change_path(@event) 5 | = tab.changes '变更记录', event_changes_path(@event) 6 | -------------------------------------------------------------------------------- /app/views/event_changes/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | = render 'event_changes/submenu' 4 | .span9 5 | .changes-list 6 | table.table.table-striped 7 | thead 8 | tr 9 | td width='140px' 时间 10 | td 变更内容 11 | tbody 12 | - @changes.each do |change| 13 | tr 14 | td = change.created_at.to_s(:db) 15 | td = change.content 16 | -------------------------------------------------------------------------------- /app/views/event_changes/new.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | = render 'event_changes/submenu' 4 | .span9 5 | = simple_form_for @change, html: {name: 'form', novalidate: ''} do |f| 6 | = f.label :content 7 | p.muted 可以输入活动时间、地点等变更信息 8 | textarea.span5 rows="7" cols="40" name="event_change[content]" ng-init="content='(#{@event.title})'" ng-maxlength="100" ng-model="content" 9 | /p.muted 10 | |变更内容将以邮件、短信方式发送,剩余字数: 11 | span.label.label-important ng-cloak="" 12 | |{{100 - content.length}} 13 | |,50字/条短信; 14 | p.muted 15 | |报名用户共有 16 | span.label = @event.ordered_users.size 17 | | 人,将通过邮件通知他们 18 | /|,其中 19 | /span.label = @event.ordered_users.with_phone.size 20 | /| 人登记了手机号码,将收到短信通知。 21 | p.alert.alert-error ng-show="form.$invalid" ng-cloak="" 变更内容 不能超过 100 个字 22 | .form-actions 23 | = f.button :submit, class: 'btn-info', 'ng-disabled' => "form.$invalid" 24 | -------------------------------------------------------------------------------- /app/views/event_mailer/change_email.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p #{@event.title} 变更通知: 4 | 5 | p #{@change.content} 6 | -------------------------------------------------------------------------------- /app/views/event_orders/_submenu.html.slim: -------------------------------------------------------------------------------- 1 | .span3 2 | .submenu 3 | = tabs_tag namespace: :sidebar, builder: EventOrdersSubmenu do |tab| 4 | - [:pending, :paid, :canceled, :closed, :refund_pending].each do |status| 5 | = tab.send status, t("menus.submenu.orders.#{status}"), filter_event_orders_path(@event, status), current_path 6 | a.btn.btn-default href=event_invoices_path(@event, format: 'xlsx') role="button" 导出发票信息 7 | -------------------------------------------------------------------------------- /app/views/event_orders/create.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.result 'ok' 2 | 3 | json.(@order, :id, :number, :status) 4 | 5 | if @order.pending? 6 | json.link generate_pay_link_by_order(@order) 7 | end 8 | -------------------------------------------------------------------------------- /app/views/event_summaries/_form.html.slim: -------------------------------------------------------------------------------- 1 | = f.error_notification 2 | 3 | .form-inputs 4 | = f.input :content, :input_html => { :rows => 10, :class => 'span7' }, :as => 'uploadable' 5 | .form-actions 6 | = f.button :submit, :class => 'btn-info' 7 | a.btn href=root_path = t('labels.cancel') 8 | 9 | = render 'markdown_guide' -------------------------------------------------------------------------------- /app/views/event_summaries/new.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | = simple_form_for @summary, :url => create_event_summary_path(@event), :html => { :class => "form-horizontal event-form" } do |f| 4 | = render 'form', :f => f 5 | -------------------------------------------------------------------------------- /app/views/event_tickets/_form.html.slim: -------------------------------------------------------------------------------- 1 | = f.error_notification 2 | .form-inputs 3 | = f.input :name, input_html: { class: 'span4' } 4 | = f.input :price, input_html: { class: 'span4' } 5 | = f.input :require_invoice 6 | = f.input :description, as: :text, input_html: { rows: 3, class: 'span4' } 7 | = f.input :tickets_quantity, input_html: { class: 'span4' } 8 | .form-actions 9 | = f.button :submit, class: 'btn-info' 10 | -------------------------------------------------------------------------------- /app/views/event_tickets/edit.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | .span12 4 | = simple_form_for @ticket, url: event_ticket_path(@event, @ticket), html: {class: "form-horizontal", name: 'form', novalidate: ''} do |f| 5 | = render 'form', f: f 6 | -------------------------------------------------------------------------------- /app/views/event_tickets/new.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | .span12 4 | = simple_form_for @ticket, html: {class: "form-horizontal", name: 'form', novalidate: ''} do |f| 5 | = render 'form', :f => f 6 | -------------------------------------------------------------------------------- /app/views/events/_header.html.slim: -------------------------------------------------------------------------------- 1 | article.event ng-init="event.id=#{@event.id}" 2 | h1 = link_to @event.title, event_path(@event) 3 | = render 'events/menu' 4 | -------------------------------------------------------------------------------- /app/views/events/_menu.html.slim: -------------------------------------------------------------------------------- 1 | - if can? :update, @event 2 | .menu.navbar 3 | .navbar-inner 4 | ul.nav.pull-right 5 | li 6 | a data-toggle='tooltip' title='只有活动管理员能看到此菜单' 7 | i.icon-th-list 8 | = tabs_tag builder: EventMenu do |tab| 9 | = tab.edit t('views.edit'), edit_event_path(@event) 10 | = tab.ticket t('views.ticket'), event_tickets_path(@event) 11 | = tab.order t('views.sell'), filter_event_orders_path(@event, status: :paid) 12 | = tab.export t('views.export.export_link'), event_export_index_path(@event) 13 | = tab.change t('views.change'), new_event_change_path(@event) 14 | = tab.check_in t('views.check_in'), checkin_event_participants_path(@event) 15 | = tab.event_summary t('views.summary'), new_event_summary_path(@event) 16 | - if can? :update, @event.group 17 | = tab.collaborators t('views.groups.collaborators'), event_collaborators_path(@event) 18 | -------------------------------------------------------------------------------- /app/views/events/edit.html.slim: -------------------------------------------------------------------------------- 1 | article.event 2 | h1 = link_to @event.title, event_path(@event), "ng-model" => "title", "ng-bind"=>"title" 3 | = render 'menu' 4 | = simple_form_for @event, :html => { :class => "form-horizontal event-form" } do |f| 5 | = f.error_notification 6 | = render 'form', :f => f 7 | -------------------------------------------------------------------------------- /app/views/events/followers.html.slim: -------------------------------------------------------------------------------- 1 | article.event-follower 2 | header 3 | a.event-follower-title href=group_event_path(@event) =@event.title 4 | h2 = t('.title', :scope => 'views') 5 | 6 | ul.follower-list 7 | - @event.group.followers.each do |user| 8 | li.follower-item.row-fluid id="group_event_#{user.id}" 9 | a ng-href="/#{user.login}" 10 | .avatar-wrapper = image_tag user.gravatar_url(:size => 30), :class => 'gravatar', :title => "#{user.login}" 11 | a.follower-url ng-href="/#{user.login}" 12 | span.follower-login-name = user.login 13 | - if user.profile.name 14 | span.follower-name 15 | | ( 16 | = user.profile.name 17 | | ) 18 | -------------------------------------------------------------------------------- /app/views/events/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'home/menu', :current => :events 2 | 3 | .list 4 | - @events.each do |event| 5 | .item 6 | .avatar-wrapper = image_tag event.user.gravatar_url(:size => 50), :class => 'gravatar' 7 | = link_to event.title, edit_event_path(event) 8 | .event-item-time 9 | = time_merge(event) 10 | .event-item-location 11 | = event.location 12 | -------------------------------------------------------------------------------- /app/views/events/new.html.slim: -------------------------------------------------------------------------------- 1 | fieldset 2 | legend = t('.title', :scope => 'views') 3 | = simple_form_for @event, :html => { :class => "form-horizontal event-form" } do |f| 4 | = f.error_notification 5 | .controls 6 | ul.unstyled 7 | - @source_events.each do |event| 8 | li 9 | span.space = "[#{event.start_time.to_date.to_s(:db)}]" 10 | span.space = link_to event.title, group_event_path(event) 11 | = link_to t('views.events.new.copy'), new_event_path(from: event.id) 12 | = render 'form', :f => f 13 | -------------------------------------------------------------------------------- /app/views/events/ordered.html.slim: -------------------------------------------------------------------------------- 1 | = render 'home/menu', :current => :ordered_events 2 | 3 | .list 4 | - @events.each do |event| 5 | .item 6 | .avatar-wrapper = image_tag event.user.gravatar_url(:size => 50), :class => 'gravatar' 7 | = link_to event.title, group_event_path(event) 8 | .event-item-time 9 | = time_merge(event) 10 | .event-item-location 11 | = event.location 12 | -------------------------------------------------------------------------------- /app/views/export/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .tabbable.tabs-left 3 | ul.nav.nav-tabs#export-format 4 | li.active 5 | = link_to '#markdown', {'data-toggle' =>"tab"} do 6 | | Markdown 7 | span = render 'application/clippy', text: markdown_format_content(@event) 8 | .tab-content#export-content 9 | #markdown.tab-pane.active 10 | pre.pre-scrollable style="max-height: 480px;" 11 | = markdown_format_content(@event) 12 | -------------------------------------------------------------------------------- /app/views/home/_menu.html.slim: -------------------------------------------------------------------------------- 1 | = link_to t('labels.launch_event'), (can?(:create, Event) ? new_event_path : upgrade_invitation_path), :class => "btn btn-info new-event-btn pull-right" 2 | ul.home-nav 3 | li class=active?(current, :activities) 4 | = link_to t('home.menu.activities'), root_path 5 | li class=active?(current, :events) 6 | = link_to events_path do 7 | = t('home.menu.created_events') 8 | span.label.amount = current_user.events.size 9 | li class=active?(current, :ordered_events) 10 | = link_to ordered_events_path do 11 | = t('home.menu.ordered_events') 12 | span.label.amount = current_user.ordered_events.uniq.size 13 | -------------------------------------------------------------------------------- /app/views/home/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'menu', :current => :activities 2 | 3 | div = t('home.body.empty') 4 | -------------------------------------------------------------------------------- /app/views/invitations/index.html.slim: -------------------------------------------------------------------------------- 1 | h2 = t('labels.invitations') 2 | table.table.table-striped.table-bordered 3 | thead 4 | tr 5 | th Email 6 | th Reason 7 | th Invited At 8 | th Upgrade? 9 | th 10 | tbody 11 | - @users.each do |user| 12 | tr 13 | td = user.email 14 | td = user.invite_reason 15 | td = user.invitation_sent_at.try(:to_s, :db) 16 | td = :Yes if user.login 17 | td 18 | - if user.invited_by_id? 19 | | Invited! 20 | - else 21 | = link_to t('labels.invite_button'), (user.login ? upgrade_invite_invitation_path(user) : mail_invitation_path(user)), :method => :patch 22 | -------------------------------------------------------------------------------- /app/views/invitations/upgrade.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .span4.offset4.well 3 | = simple_form_for resource, :as => resource_name, :url => user_invitation_path(resource_name), :html => {:method => :post, :class => 'form-full-width'} do |f| 4 | legend = t('labels.need_upgrade_invitation') 5 | = f.input :invite_reason, :as => :text, :label => false, :input_html => { :rows => 3} 6 | .help-block = t('views.users.upgrade_invitation') 7 | = f.submit t('labels.apply_sign_up'), :class => 'btn btn-success btn-large' 8 | -------------------------------------------------------------------------------- /app/views/layouts/settings.html.slim: -------------------------------------------------------------------------------- 1 | - content_for :content do 2 | .row 3 | aside.span2 4 | .tabbable.tabs-left 5 | ul.nav.nav-tabs 6 | = render_settings_tab(t('layout.account'), account_path, 'registrations') 7 | = render_settings_tab(t('layout.profile'), profile_path, 'profiles') 8 | = render_settings_tab(t('layout.user_phone'), edit_user_phone_path, 'user_phones') 9 | 10 | .span10#settings-main = yield 11 | 12 | = render :template => 'layouts/application' 13 | -------------------------------------------------------------------------------- /app/views/order_fulfillment_mailer/notify_organizer_fulfilled.html.slim: -------------------------------------------------------------------------------- 1 | p #{@event.title} 的管理员, 您好: 2 | 3 | p 19屋已经向 #{@user.login} 快递发票。 4 | 5 | p 如需查询快递进度,请前往 http://www.sf-express.com/ 6 | 7 | p 顺丰快递单号:#{@fulfillment.tracking_number} 8 | 9 | p 订单号:#{@order.number} 10 | 11 | p 订单包含以下门票: 12 | 13 | - @order.items.each do |item| 14 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 15 | p 订单总额 :#{@order.price} 元 16 | -------------------------------------------------------------------------------- /app/views/order_fulfillment_mailer/notify_user_fulfilled.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p 19屋已经向您快递发票。 4 | 5 | p 如需查询快递进度,请前往 http://www.sf-express.com/ 6 | 7 | p 顺丰快递单号:#{@fulfillment.tracking_number} 8 | 9 | p 订单号:#{@order.number} 10 | 11 | p 订单包含以下门票: 12 | 13 | - @order.items.each do |item| 14 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 15 | p 订单总额 :#{@order.price} 元 16 | 17 | p 谢谢。 18 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_organizer_created.html.slim: -------------------------------------------------------------------------------- 1 | p #{@event.title} 的管理员, 您好: 2 | 3 | p #{@user.login} 刚刚新增了订单。 4 | 5 | p 订单号:#{@order.number} 6 | 7 | p 订单包含以下门票: 8 | 9 | - @order.items.each do |item| 10 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 11 | p 订单总额 :#{@order.price} 元 12 | 13 | - if @order.require_invoice 14 | div 订单门票可以提供发票。 15 | div 发票抬头:#{@order.shipping_address.invoice_title} 16 | div 收货人信息:#{@order.shipping_address.info} 17 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_organizer_paid.html.slim: -------------------------------------------------------------------------------- 1 | p #{@event.title} 的管理员, 您好: 2 | 3 | p #{@user.login} 刚刚成功支付款项:#{@order.price} 元。 4 | 5 | - if @order.pay_with_bank_transfer? 6 | p 支付方式:银行汇款。 7 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_organizer_refunded.html.slim: -------------------------------------------------------------------------------- 1 | p #{@event.title} 的管理员, 您好: 2 | 3 | p 19屋已经向 #{@user.login} 退款 #{@refund.amount} 元。 4 | 5 | p 退款原因:#{@refund.reason} 6 | 7 | p 订单号:#{@order.number} 8 | 9 | p 订单包含以下门票: 10 | 11 | - @order.items.each do |item| 12 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 13 | p 订单总额 :#{@order.price} 元 14 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_support_refund.html.slim: -------------------------------------------------------------------------------- 1 | p 19屋管理员,您好: 2 | 3 | p #{@event.title} 活动主办方刚刚申请退款: 4 | 5 | p 订单号:#{@order.number} 6 | 7 | p 退款金额:#{@refund.amount} 元 8 | 9 | p 退款原因:#{@refund.reason} 10 | 11 | p 请尽快进行退款操作,谢谢。 12 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_user_checkin_code.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p #{@event.title} 签到码:#{@participant.checkin_code}。 4 | 5 | p 请您于#{I18n.localize(@event.start_time, format: :short)}前到达现场并出示签到码。 6 | 7 | p 谢谢。 8 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_user_created.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p 您在#{@event.title}提交订单成功。 4 | 5 | p 订单号:#{@order.number} 6 | 7 | p 订单包含以下门票: 8 | 9 | - @order.items.each do |item| 10 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 11 | p 订单总额 :#{@order.price} 元 12 | 13 | - if @order.require_invoice 14 | div 您提交的订单门票可以提供发票。 15 | div 发票抬头:#{@order.shipping_address.invoice_title} 16 | div 收货人信息:#{@order.shipping_address.info} 17 | 18 | - unless @order.free? 19 | p 20 | |如果您尚未付款,请您尽快付款。您可以点击 21 | = link_to '这里', user_orders_url(event_id: @event.id) 22 | |查看订单,重新支付 23 | 24 | p 谢谢。 25 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_user_paid.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p 我们已收到您支付的款项 #{@order.price} 元。 4 | 5 | p 订单号:#{@order.number} 6 | 7 | - if @order.pay_with_bank_transfer? 8 | p 支付方式:银行汇款。 9 | 10 | p 谢谢。 11 | -------------------------------------------------------------------------------- /app/views/order_mailer/notify_user_refunded.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p #{@event.title} 已经向您退款 #{@refund.amount} 元。 4 | 5 | p 退款原因:#{@refund.reason} 6 | 7 | p 订单号:#{@order.number} 8 | 9 | p 订单包含以下门票: 10 | 11 | - @order.items.each do |item| 12 | div #{item.quantity} x #{item.name} 单价:#{item.unit_price} 元 13 | p 订单总额 :#{@order.price} 元 14 | 15 | p 谢谢。 16 | -------------------------------------------------------------------------------- /app/views/order_refunds/_refunds.html.slim: -------------------------------------------------------------------------------- 1 | table.table.table-striped.table-bordered 2 | thead 3 | tr 4 | th = t('views.my_orders.event') 5 | th = t('views.my_orders.items') 6 | th = t('views.my_orders.paid_amount') 7 | th = t('views.my_orders.trade_no') 8 | th.text-error = t('activerecord.attributes.event_order_refund.amount') 9 | th = t('activerecord.attributes.event_order_refund.reason') 10 | tbody 11 | - refunds.each do |refund| 12 | tr 13 | td = link_to refund.order.event.title, refund.order.event, target: '_blank' 14 | td 15 | ul.tickets-list = render partial: 'user_orders/order_item', collection: refund.order.items, as: 'item' 16 | td = refund.order.paid_amount 17 | td = refund.order.number 18 | td = refund.amount 19 | td = refund.reason 20 | -------------------------------------------------------------------------------- /app/views/order_refunds/index.html.slim: -------------------------------------------------------------------------------- 1 | h2 = t('activerecord.models.event_order_refund') 2 | - @refund_batches.each do |batch| 3 | .actions = link_to '退款', batch.refund_link, :class => "btn btn-link pull-right", :target => '_blank' 4 | =render 'order_refunds/refunds', refunds: batch.refunds 5 | 6 | - unless @refunds.empty? 7 | .actions = link_to '获取退款链接', refunds_archive_path, :class => "btn btn-link pull-right" 8 | p.muted 获取后会生成退款批次号,此号包含当前日期,支付宝要求当天进行退款 9 | =render 'order_refunds/refunds', refunds: @refunds 10 | -------------------------------------------------------------------------------- /app/views/order_refunds/submit.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(@refund, :id, :amount, :reason) 2 | -------------------------------------------------------------------------------- /app/views/participants/_submenu.html.slim: -------------------------------------------------------------------------------- 1 | .span3 2 | .submenu 3 | = tabs_tag namespace: :sidebar, builder: EventSubmenu do |tab| 4 | = tab.participants_checkin t('menus.submenu.participants.checkin'), checkin_event_participants_path(@event) 5 | = tab.participants_index t('menus.submenu.participants.index'), event_participants_path(@event) 6 | -------------------------------------------------------------------------------- /app/views/participants/checkin.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | = render 'participants/submenu' 4 | .span9 ng-controller='ParticipantsCtrl' 5 | form.form-search.checkin-form 6 | input.input-medium.margin-right name="code" ng-model='code' type="text" placeholder="请输入6位数字签到码" 7 | button.btn.btn-info type="submit" ng-click='checkin()' 签到 8 | p.alert.alert-success ng-show='data' ng-cloak="" 9 | |{{data.checkin_code}} 签到成功,门票总数:{{data.quantity}} 张 10 | ul 11 | li ng-repeat='item in data.items' ng-cloak="" 12 | |{{item.quantity}} x {{item.name}} 13 | p.alert.alert-error ng-show='error' ng-cloak="" 14 | |{{error}} 15 | p.alert ng-show='wait' ng-cloak="" 正在提交签到码... 16 | -------------------------------------------------------------------------------- /app/views/participants/export.xlsx.axlsx: -------------------------------------------------------------------------------- 1 | wb = xlsx_package.workbook 2 | 3 | header_style = wb.styles.add_style alignment: { horizontal: :center }, sz: 16, b: true 4 | body_style = wb.styles.add_style alignment: { horizontal: :right, wrap_text: true } 5 | string_style = wb.styles.add_style alignment: { horizontal: :right }, num_fmt: 12 6 | 7 | wb.add_worksheet(name: @event.title) do |sheet| 8 | sheet.add_row ['打勾', '签到码', '门票', '姓名', 'Email', '金额(元)', '订单号', '订单日期'], style: header_style 9 | @orders.each do |order| 10 | user = order.user 11 | items = order.items.map do |item| 12 | "#{item.quantity} x #{item.name}" 13 | end.join(", ") 14 | sheet.add_row ['', order.participant.checkin_code, items, user.name, user.email, order.paid_amount, order.number.to_s, order.created_at.to_date.to_s(:db)], style: body_style 15 | end 16 | sheet["G2:G#{@orders.size+1}"].each { |c| c.style = string_style } unless @orders.empty? # 订单号改为字符串格式 17 | end 18 | -------------------------------------------------------------------------------- /app/views/participants/index.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | = render 'participants/submenu' 4 | .span9 5 | = link_to '导出xls', export_event_participants_path(@event, format: 'xlsx'), class: 'btn btn-info pull-right' 6 | table.table 7 | thead 8 | th 签到码 9 | th 门票 10 | th 姓名 11 | th 手机 12 | th 下单时间 13 | th 状态 14 | tbody 15 | - @participants.each do |participant| 16 | tr 17 | td 18 | = participant.checkin_code 19 | span (总票数:#{participant.order.quantity}) 20 | td 21 | - participant.order.items.each do |item| 22 | div #{item.quantity} x #{item.name} 23 | td = participant.user.name 24 | td = participant.user.phone 25 | td = participant.created_at.to_s(:db) 26 | td = participant.joined? ? '已签到' : '未签到' 27 | -------------------------------------------------------------------------------- /app/views/participants/update.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.result 'ok' 2 | 3 | json.(@participant, :id, :checkin_code) 4 | 5 | json.quantity @participant.order.quantity 6 | json.items @participant.order.items, :quantity, :name 7 | -------------------------------------------------------------------------------- /app/views/profiles/show.html.slim: -------------------------------------------------------------------------------- 1 | = simple_form_for @profile, :url => profile_path, :html => { :class => 'form-horizontal', :method => :patch } do |f| 2 | fieldset 3 | legend = t('.title', :scope => 'views') 4 | .form-inputs 5 | = f.input :name 6 | = f.input :website 7 | = f.input :bio, :input_html => { :rows => 12 } 8 | 9 | .form-actions 10 | = f.button :submit, :class => 'btn-primary' 11 | -------------------------------------------------------------------------------- /app/views/registrations/create.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(@user, :id, :name, :phone) 2 | 3 | json.token form_authenticity_token 4 | -------------------------------------------------------------------------------- /app/views/sessions/create.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(@user, :id, :name, :phone) 2 | 3 | json.token form_authenticity_token 4 | -------------------------------------------------------------------------------- /app/views/topics/_index.html.slim: -------------------------------------------------------------------------------- 1 | div 2 | = link_to t('views.events.topics.new.button'), new_event_topic_path(@event), class: "btn pull-right span1" 3 | ul.topics-nav 4 | li.active 5 | a href="#" = t('views.events.topics.title') 6 | ul.media-list.unstyled 7 | = content_tag_for('li', @event.group.topics, class: 'topic-item') do |topic| 8 | a.topic-item-avatar href=user_path(topic.user) 9 | img.media-object src=topic.user.gravatar_url(size: 48) 10 | .media-body 11 | span.replies-count = topic.replies.size 12 | .topic-item-title = link_to topic.title, event_topic_path(@event, topic) 13 | .topic-item-info 14 | = link_to topic.user.login, user_path(topic.user), class: 'muted' 15 | | • 16 | = timeago_tag topic.created_at 17 | -------------------------------------------------------------------------------- /app/views/topics/new.html.slim: -------------------------------------------------------------------------------- 1 | = render 'events/header' 2 | .row 3 | .span8 4 | = simple_form_for @topic, url: event_topics_path(@event), html: { class: "form-horizontal" } do |f| 5 | = f.error_notification 6 | .form-inputs 7 | = f.input :title, input_html: { class: 'span5' } 8 | = f.input :body , input_html: { class: 'span5', rows: 18 }, as: 'uploadable' 9 | .form-actions 10 | = f.button :submit, class: 'btn-info' 11 | .span4 12 | -------------------------------------------------------------------------------- /app/views/user_mailer/invited_email.html.slim: -------------------------------------------------------------------------------- 1 | P 您好,#{@user.login}, 2 | 3 | p 您的申请已经通过,现在可以发起活动了。 4 | 5 | p 6 | |您可以点击 7 | = link_to '这里', root_url 8 | |访问19屋 9 | -------------------------------------------------------------------------------- /app/views/user_mailer/notify_email.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | p 您好,#{@user.login} 2 | 3 | p 以下为您所关注的最新活动: 4 | 5 | p 6 | #{@event.title} 7 | |时间:#{time_merge(@event)} 8 | 9 | p 10 | |详情请点击 11 | = link_to slug_event_url(@event.group.slug), slug_event_url(@event.group.slug) 12 | -------------------------------------------------------------------------------- /app/views/user_mailer/reminder_email.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | p #{@user.login}, 您好: 2 | 3 | p 4 | |您报名参加的 #{@event.title} 将于明天(#{@event.start_time.strftime("%Y年%m月%d日")}) #{@event.start_time.strftime("%p")} #{@event.start_time.strftime("%I:%M")} 开始,请您准时参加。 5 | -------------------------------------------------------------------------------- /app/views/user_mailer/welcome_email.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | meta content="text/html; charset=UTF-8" http-equiv="Content-Type" 5 | body 6 | h1 Welcome to 19wu 7 | p 8 | | If you have any problems, please contact 9 | a href="mailto:support@19wu.com" 10 | | administrator 11 | -------------------------------------------------------------------------------- /app/views/user_mailer/welcome_email.text.erb: -------------------------------------------------------------------------------- 1 | Welcome to 19wu. 2 | 3 | If you have any problems, please contact 4 | [administrator](support@19wu.com) 5 | -------------------------------------------------------------------------------- /app/views/user_mailer/welcome_email.zh-CN.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | meta content="text/html; charset=UTF-8" http-equiv="Content-Type" 5 | body 6 | h1 欢迎加入19屋, 7 | p 8 | | 在使用中如果有什么问题,请联系 9 | a href="mailto:support@19wu.com" 10 | |管理员 11 | -------------------------------------------------------------------------------- /app/views/user_mailer/welcome_email.zh-CN.text.erb: -------------------------------------------------------------------------------- 1 | 欢迎加入19屋, 2 | 3 | 在使用中如果有什么问题,请联系 4 | [管理员](support@19wu.com) 5 | -------------------------------------------------------------------------------- /app/views/user_orders/_order_item.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = "#{item.ticket.try(:name)}(#{item.price}元)" 3 | | x 4 | = item.quantity 5 | -------------------------------------------------------------------------------- /app/views/user_orders/alipay_done.html.slim: -------------------------------------------------------------------------------- 1 | p.text-success 谢谢,您的订单已经完成支付。 2 | -------------------------------------------------------------------------------- /app/views/user_orders/index.html.slim: -------------------------------------------------------------------------------- 1 | h2 = t('labels.my_orders') 2 | 3 | - if orders_filtered? 4 | p.alert.alert-info 5 | = t('views.my_orders.orders_filtered') 6 | = ', ' 7 | = link_to t('views.my_orders.view_all_orders'), user_orders_path 8 | 9 | table.table.table-striped.table-bordered 10 | thead 11 | tr 12 | th = t('views.my_orders.event') 13 | th = t('views.my_orders.items') 14 | th = t('views.my_orders.price') 15 | th = t('views.my_orders.trade_no') 16 | th = t('views.my_orders.status') 17 | th = t('views.my_orders.checkin_code') 18 | th = t('views.my_orders.operations') 19 | tbody 20 | - @orders.each do |order| 21 | tr 22 | td = link_to order.event.title, order.event, target: '_blank' 23 | td 24 | ul.tickets-list = render partial: 'user_orders/order_item', collection: order.items, as: 'item' 25 | td = order.price 26 | td = order.number 27 | td = order.status_name 28 | td = order.participant.try(:checkin_code) 29 | td 30 | = operations(order) 31 | -------------------------------------------------------------------------------- /app/views/users/cohort.html.slim: -------------------------------------------------------------------------------- 1 | table.table.table-bordered 2 | thead 3 | tr 4 | th 5 | - @cohorts.size.times do |i| 6 | th #{i} #{@period} 7 | 8 | - @cohorts.each_with_index do |row, index| 9 | - start = row[1][:count][0] 10 | tr 11 | td 12 | = row[0] 13 | br 14 | #{start} users 15 | 16 | - (@cohorts.size).times do |i| 17 | td 18 | - if @cohorts.size - index > (i + 1) 19 | = "#{((row[1][:count][i].to_f/start.to_f) * 100.00).round(0)}%" 20 | - else 21 | | -- 22 | -------------------------------------------------------------------------------- /app/views/users/show.html.slim: -------------------------------------------------------------------------------- 1 | = content_tag_for :article, @user do 2 | 3 | header 4 | .row 5 | .span2 = image_tag @user.gravatar_url(:size => 160), :class => 'gravatar' 6 | .span10 7 | h1.user-title 8 | = @user.login 9 | -if @profile.name.present? 10 | small = @profile.name 11 | ul.user-info.unstyled.inline.muted 12 | -if @profile.website.present? 13 | li = @profile.website 14 | li #{l @user.created_at.to_date} 加入 15 | 16 | .user-body = raw @user.profile.bio_html 17 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/delayed_job: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) 4 | require 'delayed/command' 5 | Delayed::Command.new(ARGV).daemonize 6 | -------------------------------------------------------------------------------- /bin/magic_encoding_find: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def magic_encoding_find 4 | non_ascii_regexp = /[^\0-\177]/ 5 | magic_encoding_regexp = /\s*#.*(?:-\*-\s*)?(?:en)?coding\s*:\s*([-a-z0-9_]*)\s*(;|-\*-|$)/ 6 | 7 | files = [] 8 | Dir.chdir(File.expand_path('../..', __FILE__)) do 9 | Dir['app/**/*.rb', 'lib/**/*.rb', 'spec/**/*.rb'].each do |f| 10 | content = File.read(f) 11 | if content =~ non_ascii_regexp && 12 | content.lines.take(2).none? { |l| l =~ magic_encoding_regexp } 13 | files << f 14 | end 15 | end 16 | end 17 | 18 | files 19 | end 20 | 21 | if $0 == __FILE__ 22 | puts magic_encoding_find.join("\n") 23 | end 24 | -------------------------------------------------------------------------------- /bin/magic_encoding_fix: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | load File.expand_path('../magic_encoding_find', __FILE__) 4 | 5 | encoding_line = "# -*- coding: utf-8 -*-\n" 6 | files = magic_encoding_find 7 | 8 | Dir.chdir(File.expand_path('../..', __FILE__)) do 9 | files.each do |f| 10 | puts "Fix file #{f}" 11 | lines = File.read(f).lines.to_a 12 | if lines.first =~ /^\s*#!/ 13 | lines[1, 0] = encoding_line 14 | else 15 | lines.unshift encoding_line 16 | end 17 | File.open(f, 'w') do |io| 18 | io.write lines.join 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run NineteenWu::Application 5 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /config/database.yml.example.mysql2: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: mysql2 3 | encoding: utf8 4 | host: localhost 5 | port: 3306 6 | username: root 7 | password: 8 | 9 | development: 10 | <<: *defaults 11 | database: nineteen_wu_development 12 | 13 | test: 14 | <<: *defaults 15 | database: nineteen_wu_test 16 | 17 | production: 18 | <<: *defaults 19 | database: nineteen_wu_production 20 | -------------------------------------------------------------------------------- /config/database.yml.example.pg: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: postgresql 3 | encoding: unicode 4 | host: localhost 5 | pool: 5 6 | username: postgres 7 | password: 8 | 9 | development: 10 | <<: *defaults 11 | database: nineteen_wu_development 12 | 13 | test: 14 | <<: *defaults 15 | database: nineteen_wu_test 16 | 17 | production: 18 | <<: *defaults 19 | database: nineteen_wu_production 20 | -------------------------------------------------------------------------------- /config/database.yml.example.sqlite3: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: sqlite3 3 | pool: 5 4 | timeout: 5000 5 | 6 | development: 7 | <<: *defaults 8 | database: db/development.sqlite3 9 | 10 | test: 11 | <<: *defaults 12 | database: db/test.sqlite3 13 | 14 | production: 15 | <<: *defaults 16 | database: db/production.sqlite3 17 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | NineteenWu::Application.initialize! 6 | -------------------------------------------------------------------------------- /config/initializers/0_jruby_patches.rb: -------------------------------------------------------------------------------- 1 | # Should load early to handle jruby compatible issues 2 | 3 | if RUBY_PLATFORM == 'java' 4 | $:.unshift Rails.root.join('lib', 'jruby_patches') 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/alipay.rb: -------------------------------------------------------------------------------- 1 | Alipay.pid = Settings.alipay.pid 2 | Alipay.key = Settings.alipay.key 3 | Alipay.seller_email = Settings.alipay.seller_email 4 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/carrierwave.rb: -------------------------------------------------------------------------------- 1 | CarrierWave.configure do |config| 2 | config.asset_host = "http://#{Settings.host}" 3 | if Rails.env.test? # http://git.io/XhkwBw 4 | config.storage = :file 5 | config.enable_processing = false 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/delayed_job_config.rb: -------------------------------------------------------------------------------- 1 | Delayed::Worker.destroy_failed_jobs = false 2 | Delayed::Worker.sleep_delay = 60 3 | Delayed::Worker.sleep_delay = 10 if Rails.env.development? 4 | Delayed::Worker.max_attempts = 3 5 | Delayed::Worker.max_run_time = 5.minutes 6 | Delayed::Worker.read_ahead = 10 7 | Delayed::Worker.delay_jobs = !(Rails.env.test? || ENV['DISABLE_DELAYED_JOB']) 8 | -------------------------------------------------------------------------------- /config/initializers/html_pipeline.rb: -------------------------------------------------------------------------------- 1 | HasHtmlPipeline.configure do |config| 2 | context = {} 3 | 4 | markdown = config.register(:markdown, 5 | [ 6 | HTML::Pipeline::MarkdownFilter, 7 | HTML::Pipeline::SanitizationFilter 8 | ], 9 | context) 10 | 11 | config.register(:profile_markdown, 12 | markdown.filters, 13 | context) 14 | end 15 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /config/initializers/load_lib_files.rb: -------------------------------------------------------------------------------- 1 | require File.join(Rails.root, "lib/fancy_url/fancy_url.rb") 2 | require File.join(Rails.root, "lib/core_ext/hash/angularjs.rb") 3 | -------------------------------------------------------------------------------- /config/initializers/middlewares.rb: -------------------------------------------------------------------------------- 1 | NineteenWu::Application.config.middleware.use :FallbackUrlRedirector 2 | 3 | if Rails.env.production? 4 | NineteenWu::Application.config.middleware.use ExceptionNotification::Rack, 5 | :email => { 6 | :email_prefix => "[19wu] ", 7 | :sender_address => Settings.email.from, 8 | :exception_recipients => Settings.email.exception_recipients 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/ransack.rb: -------------------------------------------------------------------------------- 1 | Ransack.configure do |config| 2 | config.add_predicate 'gteq_price', # Name your predicate 3 | # What non-compound ARel predicate will it use? (eq, matches, etc) 4 | :arel_predicate => 'gteq', 5 | # Format incoming values as you see fit. (Default: Don't do formatting) 6 | :formatter => proc {|v| (v.to_f * 100).to_i }, 7 | # Validate a value. An "invalid" value won't be used in a search. 8 | # Below is default. 9 | :validator => proc {|v| v.present?}, 10 | # Should compounds be created? Will use the compound (any/all) version 11 | # of the arel_predicate to create a corresponding any/all version of 12 | # your predicate. (Default: true) 13 | :compounds => false, 14 | # Force a specific column type for type-casting of supplied values. 15 | # (Default: use type from DB column) 16 | :type => :float # change to float 17 | end 18 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | 8 | if Settings[:secret_token] && Settings[:secret_token].size >= 30 9 | NineteenWu::Application.config.secret_token = Settings[:secret_token] 10 | else 11 | NineteenWu::Application.config.secret_token = 'c51d3acad27f10e29da85fa41c5d6b35ad65e187776fbc721a52c13365de0c07bd4d61a867a21dc0cb11277061754e8550146d2a79cedf07b4ce489e820a6d1c' 12 | end 13 | 14 | if Settings[:secret_key_base] && Settings[:secret_key_base].size >= 30 15 | NineteenWu::Application.config.secret_key_base = Settings[:secret_key_base] 16 | else 17 | NineteenWu::Application.config.secret_key_base = '4784f0b0633af49448defc29599d796782e4d12390b5d5790ade74c71e4f2be508a128983dcfe7d53e41ec20d032faa190fceba4502fb20b83789657921e515d' 18 | end 19 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | NineteenWu::Application.config.session_store :cookie_store, key: '_nineteen_wu_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # NineteenWu::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/initializers/setup_locale.rb: -------------------------------------------------------------------------------- 1 | NineteenWu::Application.configure do |app| 2 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 3 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 4 | config.time_zone = Settings.time_zone 5 | 6 | # Because of https://github.com/rails/rails/issues/8711 7 | # 8 | # time_zone changed here is not applied 9 | require 'active_support/core_ext/time/zones' 10 | zone_default = Time.find_zone!(app.config.time_zone) 11 | unless zone_default 12 | raise 'Value assigned to config.time_zone not recognized. ' \ 13 | 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' 14 | end 15 | Time.zone_default = zone_default 16 | 17 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 18 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 19 | # @see config/initializers/setup_locale_settings.rb 20 | config.i18n.default_locale = Settings.default_locale 21 | end 22 | -------------------------------------------------------------------------------- /config/initializers/setup_mail.rb: -------------------------------------------------------------------------------- 1 | NineteenWu::Application.configure do 2 | config.action_mailer.delivery_method = Settings.email.delivery_method.to_sym 3 | 4 | delivery_settings_key = "#{Settings.email.delivery_method}_settings" 5 | if delivery_settings = Settings.email[delivery_settings_key] 6 | config.action_mailer.send "#{delivery_settings_key}=", delivery_settings.symbolize_keys 7 | ActionMailer::Base.send "#{delivery_settings_key}=", delivery_settings.symbolize_keys # fix for exception notification 8 | end 9 | 10 | config.action_mailer.default_url_options = { 11 | :host => Settings.host 12 | } 13 | ActionMailer::Base.default_url_options = config.action_mailer.default_url_options # http://git.io/biMyZA 14 | 15 | ActionMailer::Base.default :from => Settings.email.from 16 | end 17 | -------------------------------------------------------------------------------- /config/initializers/sms.rb: -------------------------------------------------------------------------------- 1 | Settings.sms.each do |service, account| 2 | options = { password: account['password'] } 3 | options[:username] = account['username'] if account['username'] 4 | ChinaSMS.use service.to_sym, options 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/cancan.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | unauthorized: 3 | default: "You are not authorized to access this page." 4 | -------------------------------------------------------------------------------- /config/locales/cancan.zh-CN.yml: -------------------------------------------------------------------------------- 1 | zh-CN: 2 | unauthorized: 3 | default: "你没有权限访问此页面。" 4 | -------------------------------------------------------------------------------- /config/locales/devise_invitable.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | devise: 3 | invitations: 4 | send_instructions: 'An invitation email has been sent to %{email}.' 5 | invitation_token_invalid: 'The invitation token provided is not valid!' 6 | updated: 'Your password was set successfully. You are now signed in.' 7 | no_invitations_remaining: "No invitations remaining" 8 | new: 9 | header: "Send invitation" 10 | submit_button: "Send an invitation" 11 | edit: 12 | header: "Set your password" 13 | submit_button: "Set my password" 14 | mailer: 15 | invitation_instructions: 16 | subject: 'Invitation instructions' 17 | -------------------------------------------------------------------------------- /config/locales/devise_invitable.zh-CN.yml: -------------------------------------------------------------------------------- 1 | 'zh-CN': 2 | errors: 3 | messages: 4 | wait_for_invite: "您正申请注册,请等待邀请邮件." 5 | devise: 6 | invitations: 7 | received: '申请已经提交,我们会尽快审核您的申请.' 8 | send_instructions: '邀请邮件已经发送到了 %{email}.' 9 | invitation_token_invalid: '邀请码不正确!' 10 | updated: '你的密码设置成功,现在可以开始了。' 11 | no_invitations_remaining: "没有邀请码。" 12 | new: 13 | header: "发送邀请" 14 | submit_button: "发送邀请" 15 | edit: 16 | header: "设置您的密码" 17 | submit_button: "设置我的密码" 18 | mailer: 19 | invitation_instructions: 20 | subject: '有人邀请你加入19屋' 21 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": 'Yes' 4 | "no": 'No' 5 | required: 6 | text: 'required' 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Labels and hints examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | 27 | -------------------------------------------------------------------------------- /config/schedule.rb: -------------------------------------------------------------------------------- 1 | # Use this file to easily define all of your cron jobs. 2 | # 3 | # It's helpful, but not entirely necessary to understand cron before proceeding. 4 | # http://en.wikipedia.org/wiki/Cron 5 | 6 | # Example: 7 | # 8 | # set :output, "/path/to/my/cron_log.log" 9 | # 10 | # every 2.hours do 11 | # command "/usr/bin/some_great_command" 12 | # runner "MyModel.some_method" 13 | # rake "some:great:rake:task" 14 | # end 15 | # 16 | # every 4.days do 17 | # runner "AnotherModel.prune_old_records" 18 | # end 19 | 20 | # Learn more: http://github.com/javan/whenever 21 | 22 | every 1.day, :at => '4:30 am' do 23 | runner "Event.remind_participants" 24 | end 25 | 26 | every 1.day, :at => '3:00 am' do 27 | runner "EventOrder.cleanup_expired" 28 | end 29 | -------------------------------------------------------------------------------- /custom_plan.rb: -------------------------------------------------------------------------------- 1 | # touch config/boot.rb to restart zeus 2 | require 'zeus/rails' 3 | 4 | class CustomPlan < Zeus::Rails 5 | 6 | # def my_custom_command 7 | # # see https://github.com/burke/zeus/blob/master/docs/ruby/modifying.md 8 | # end 9 | 10 | end 11 | 12 | Zeus.plan = CustomPlan.new 13 | -------------------------------------------------------------------------------- /db/migrate/20121227135540_add_login_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddLoginToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :login, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121231000051_create_events.rb: -------------------------------------------------------------------------------- 1 | class CreateEvents < ActiveRecord::Migration 2 | def change 3 | create_table :events do |t| 4 | t.string :title, :null => false 5 | t.datetime :start_time 6 | t.datetime :end_time 7 | t.string :location 8 | t.text :content 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130104101554_add_confirmable_to_devise.rb: -------------------------------------------------------------------------------- 1 | class AddConfirmableToDevise < ActiveRecord::Migration 2 | 3 | def self.up 4 | add_column :users, :confirmation_token, :string 5 | add_column :users, :confirmed_at, :datetime 6 | add_column :users, :confirmation_sent_at , :datetime 7 | add_column :users, :unconfirmed_email, :string 8 | 9 | add_index :users, :confirmation_token, :unique => true 10 | end 11 | 12 | def self.down 13 | remove_index :users, :confirmation_token 14 | remove_column :users, :unconfirmed_email 15 | remove_column :users, :confirmation_sent_at 16 | remove_column :users, :confirmed_at 17 | remove_column :users, :confirmation_token 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20130105124957_add_user_id_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToEvents < ActiveRecord::Migration 2 | def change 3 | Event.destroy_all # destroy it before release is safe. 4 | #add_column :events, :user_id, :integer, :null => false #197 5 | add_column :events, :user_id, :integer 6 | change_column :events, :user_id, :integer, :null => false 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130106071318_create_profiles.rb: -------------------------------------------------------------------------------- 1 | class CreateProfiles < ActiveRecord::Migration 2 | def change 3 | create_table :profiles do |t| 4 | t.string :name 5 | t.string :phone 6 | t.string :website 7 | t.text :bio 8 | t.references :user 9 | 10 | t.timestamps 11 | end 12 | add_index :profiles, :user_id 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130107053540_add_location_guide_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddLocationGuideToEvents < ActiveRecord::Migration 2 | def change 3 | add_column :events, :location_guide, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130107102539_create_photos.rb: -------------------------------------------------------------------------------- 1 | class CreatePhotos < ActiveRecord::Migration 2 | def change 3 | create_table :photos do |t| 4 | t.integer :user_id, null: false 5 | t.string :image 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130110074522_create_event_participants.rb: -------------------------------------------------------------------------------- 1 | class CreateEventParticipants < ActiveRecord::Migration 2 | def change 3 | create_table :event_participants do |t| 4 | t.integer :event_id 5 | t.integer :user_id 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :event_participants, :event_id 11 | add_index :event_participants, :user_id 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130124031857_create_groups.rb: -------------------------------------------------------------------------------- 1 | class CreateGroups < ActiveRecord::Migration 2 | def up 3 | create_table :groups do |t| 4 | t.integer :user_id, :null => false 5 | t.string :slug, :null => false 6 | 7 | t.timestamps 8 | end 9 | add_index :groups, :slug, :unique => true 10 | 11 | add_column :events, :group_id, :integer 12 | add_index :events, :group_id 13 | Event.all.each do |event| 14 | event.update_attributes! :slug => "e#{event.id}" 15 | end 16 | change_column :events, :group_id, :integer, :null => false 17 | end 18 | 19 | def down 20 | drop_table :groups 21 | remove_column :events, :group_id 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20130125081842_add_joined_to_event_participants.rb: -------------------------------------------------------------------------------- 1 | class AddJoinedToEventParticipants < ActiveRecord::Migration 2 | def change 3 | add_column :event_participants, :joined, :boolean, :null => false, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130225091732_devise_invitable_add_to_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseInvitableAddToUsers < ActiveRecord::Migration 2 | def up 3 | change_table :users do |t| 4 | t.string :invitation_token, :limit => 60 5 | t.datetime :invitation_sent_at 6 | t.datetime :invitation_accepted_at 7 | t.integer :invitation_limit 8 | t.references :invited_by, :polymorphic => true 9 | t.index :invitation_token # for invitable 10 | t.index :invited_by_id 11 | end 12 | 13 | # And allow null encrypted_password and password_salt: 14 | change_column_null :users, :encrypted_password, true 15 | end 16 | 17 | def down 18 | change_table :users do |t| 19 | t.remove_references :invited_by, :polymorphic => true 20 | t.remove :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20130226085502_add_admin_flag_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminFlagToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :admin, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130226122259_add_invite_reason_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddInviteReasonToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :invite_reason, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130319094925_acts_as_follower_migration.rb: -------------------------------------------------------------------------------- 1 | class ActsAsFollowerMigration < ActiveRecord::Migration 2 | def self.up 3 | create_table :follows, :force => true do |t| 4 | t.references :followable, :polymorphic => true, :null => false 5 | t.references :follower, :polymorphic => true, :null => false 6 | t.boolean :blocked, :default => false, :null => false 7 | t.timestamps 8 | end 9 | 10 | add_index :follows, ["follower_id", "follower_type"], :name => "fk_follows" 11 | add_index :follows, ["followable_id", "followable_type"], :name => "fk_followables" 12 | end 13 | 14 | def self.down 15 | drop_table :follows 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20130328114637_participant_automatic_follow_event.rb: -------------------------------------------------------------------------------- 1 | class ParticipantAutomaticFollowEvent < ActiveRecord::Migration 2 | def up 3 | Event.all.each do |event| 4 | group = event.group 5 | event.participated_users.each do |user| 6 | user.follow group 7 | end 8 | end 9 | end 10 | 11 | def down 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130403135737_create_fallback_urls.rb: -------------------------------------------------------------------------------- 1 | class CreateFallbackUrls < ActiveRecord::Migration 2 | def change 3 | create_table :fallback_urls do |t| 4 | t.string :origin 5 | t.string :change_to 6 | 7 | t.timestamps 8 | end 9 | add_index :fallback_urls, :origin, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130526080632_create_event_summaries.rb: -------------------------------------------------------------------------------- 1 | class CreateEventSummaries < ActiveRecord::Migration 2 | def up 3 | create_table :event_summaries do |t| 4 | t.text :content 5 | t.integer :event_id 6 | 7 | t.timestamp 8 | end 9 | end 10 | 11 | def down 12 | drop_table :event_summaries 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130527075012_create_group_collaborators.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupCollaborators < ActiveRecord::Migration 2 | def change 3 | create_table :group_collaborators do |t| 4 | t.integer :group_id 5 | t.integer :user_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130617104446_create_event_changes.rb: -------------------------------------------------------------------------------- 1 | class CreateEventChanges < ActiveRecord::Migration 2 | def change 3 | create_table :event_changes do |t| 4 | t.integer :event_id 5 | t.string :content 6 | 7 | t.timestamps 8 | end 9 | add_index :event_changes, :event_id 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130702125422_move_phone_to_users.rb: -------------------------------------------------------------------------------- 1 | class MovePhoneToUsers < ActiveRecord::Migration 2 | def up 3 | add_column :users, :phone, :string 4 | User.all.each do |user| 5 | user.phone = user.profile.phone 6 | user.save 7 | end 8 | remove_column :profiles, :phone 9 | end 10 | 11 | def down 12 | add_column :profiles, :phone, :string 13 | Profile.all.each do |profile| 14 | profile.phone = profile.user.phone 15 | profile.save 16 | end 17 | remove_column :users, :phone 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20130704141739_create_group_topics.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupTopics < ActiveRecord::Migration 2 | def change 3 | create_table :group_topics do |t| 4 | t.string :title 5 | t.text :body 6 | t.integer :user_id , null: false 7 | t.integer :group_id, null: false 8 | 9 | t.timestamps 10 | end 11 | add_index :group_topics, :group_id 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130706130132_create_group_topic_replies.rb: -------------------------------------------------------------------------------- 1 | class CreateGroupTopicReplies < ActiveRecord::Migration 2 | def change 3 | create_table :group_topic_replies do |t| 4 | t.text :body 5 | t.integer :group_topic_id, null: false 6 | t.integer :user_id , null: false 7 | 8 | t.timestamps 9 | end 10 | add_index :group_topic_replies, :group_topic_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130716040406_add_replies_count_to_group_topics.rb: -------------------------------------------------------------------------------- 1 | class AddRepliesCountToGroupTopics < ActiveRecord::Migration 2 | class GroupTopic < ActiveRecord::Base;end 3 | class GroupTopicReply < ActiveRecord::Base;end 4 | 5 | def change 6 | add_column :group_topics, :replies_count, :integer, :default => 0 7 | 8 | GroupTopic.reset_column_information 9 | GroupTopic.all.each do |topic| 10 | topic.update_attribute(:replies_count, GroupTopicReply.where(:group_topic_id => topic.id).count) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130807005443_create_event_tickets.rb: -------------------------------------------------------------------------------- 1 | class CreateEventTickets < ActiveRecord::Migration 2 | def change 3 | create_table :event_tickets do |t| 4 | t.string :name 5 | t.float :price 6 | t.string :description 7 | t.boolean :require_invoice 8 | t.integer :event_id 9 | 10 | t.timestamps 11 | end 12 | add_index :event_tickets, :event_id 13 | add_column :events, :tickets_quantity, :integer 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20130807133515_create_event_orders.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrders < ActiveRecord::Migration 2 | def change 3 | create_table :event_orders do |t| 4 | t.integer :event_id, null: false 5 | t.integer :user_id, null: false 6 | t.integer :quantity, null: false 7 | t.float :price, null: false 8 | t.string :status, limit: 16 9 | t.string :trade_no, limit: 16 10 | 11 | t.timestamps 12 | end 13 | add_index :event_orders, :event_id 14 | add_index :event_orders, :user_id 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20130808003107_create_event_order_items.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderItems < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_items do |t| 4 | t.integer :order_id , null: false 5 | t.integer :ticket_id, null: false 6 | t.integer :quantity , null: false 7 | t.float :price , null: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130811124504_create_event_order_shipping_addresses.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderShippingAddresses < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_shipping_addresses do |t| 4 | t.integer :order_id, null: false 5 | t.string :invoice_title 6 | t.string :province , limit: 64 7 | t.string :city , limit: 64 8 | t.string :district , limit: 64 9 | t.string :address 10 | t.string :name , limit: 64 11 | t.string :phone , limit: 64 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20130812033933_add_default_value_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultValueToEvents < ActiveRecord::Migration 2 | def change 3 | change_column :events, :tickets_quantity, :integer, :default => 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130812043238_add_canceled_at_to_event_orders.rb: -------------------------------------------------------------------------------- 1 | class AddCanceledAtToEventOrders < ActiveRecord::Migration 2 | def change 3 | add_column :event_orders, :canceled_at, :timestamp 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130814191134_remove_canceled_at_from_event_orders.rb: -------------------------------------------------------------------------------- 1 | class RemoveCanceledAtFromEventOrders < ActiveRecord::Migration 2 | def change 3 | remove_column :event_orders, :canceled_at 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130817025041_create_event_order_participants.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderParticipants < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_participants do |t| 4 | t.integer :order_id , null: false 5 | t.integer :event_id , null: false 6 | t.integer :user_id , null: false 7 | t.string :checkin_code, null: false, limit: 6 8 | t.datetime :checkin_at 9 | 10 | t.timestamps 11 | end 12 | add_index :event_order_participants, [:event_id, :checkin_code], unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130818175826_create_event_order_status_transitions.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderStatusTransitions < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_status_transitions do |t| 4 | t.references :event_order, index: true 5 | t.string :event 6 | t.string :from 7 | t.string :to 8 | t.timestamp :created_at 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130825132343_create_sequences.rb: -------------------------------------------------------------------------------- 1 | class CreateSequences < ActiveRecord::Migration 2 | def change 3 | create_table :sequences do |t| 4 | t.date :date , null: false 5 | t.integer :number, null: false, default: 0 6 | 7 | t.timestamps 8 | end 9 | add_index :sequences, :date, unique: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130825132914_add_number_to_order.rb: -------------------------------------------------------------------------------- 1 | class AddNumberToOrder < ActiveRecord::Migration 2 | def up 3 | add_column :event_orders, :number, :string, limit: 16 4 | EventOrder.all.each do |order| 5 | order.update_column :number, Sequence.get 6 | end 7 | change_column :event_orders, :number, :string, limit: 16, null: false 8 | end 9 | 10 | def down 11 | remove_column :event_orders, :number 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130916141824_add_paid_amount_and_refunded_amount_to_order.rb: -------------------------------------------------------------------------------- 1 | class AddPaidAmountAndRefundedAmountToOrder < ActiveRecord::Migration 2 | def up 3 | add_column :event_orders, :paid_amount_in_cents , :integer, default: 0, null: false 4 | EventOrder.where(status: [:paid, :refund_pending]).each do |order| 5 | order.update_column :paid_amount_in_cents, order.price_in_cents 6 | end 7 | end 8 | 9 | def down 10 | remove_column :event_orders, :paid_amount_in_cents 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130916145041_create_refund_batches.rb: -------------------------------------------------------------------------------- 1 | class CreateRefundBatches < ActiveRecord::Migration 2 | def change 3 | create_table :refund_batches do |t| 4 | t.string :batch_no, null: false, limit: 24 5 | t.string :status , null: false, limit: 16 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130916145506_create_event_order_refunds.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderRefunds < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_refunds do |t| 4 | t.integer :order_id, null: false 5 | t.integer :refund_batch_id 6 | t.integer :amount_in_cents, null: false 7 | t.string :reason 8 | t.string :status, null: false, limit: 16 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130921140156_add_unit_price_to_event_order_item.rb: -------------------------------------------------------------------------------- 1 | class AddUnitPriceToEventOrderItem < ActiveRecord::Migration 2 | def change 3 | add_column :event_order_items, :unit_price_in_cents, :integer, default: 0, null: false 4 | EventOrderItem.all.each do |event_order_item| 5 | event_order_item.update_column :unit_price_in_cents, (event_order_item.price_in_cents / event_order_item.quantity) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130925031818_revert_ticket_quantity_of_request_refund_order.rb: -------------------------------------------------------------------------------- 1 | class RevertTicketQuantityOfRequestRefundOrder < ActiveRecord::Migration 2 | def up 3 | # full-refunded order will be cancel, and then the quantity will be restock. 4 | EventOrder.where(status: 'refund_pending').each do |order| 5 | order.event.decrement! :tickets_quantity, order.quantity if order.event.tickets_quantity 6 | end 7 | end 8 | 9 | def down 10 | EventOrder.where(status: 'refund_pending').each do |order| 11 | order.event.increment! :tickets_quantity, order.quantity if order.event.tickets_quantity 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20131031070813_create_event_order_fulfillments.rb: -------------------------------------------------------------------------------- 1 | class CreateEventOrderFulfillments < ActiveRecord::Migration 2 | def change 3 | create_table :event_order_fulfillments do |t| 4 | t.integer :order_id, null: true 5 | t.string :tracking_number, limit: 64 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20131125011434_send_checkin_code_sms_for_free_event.rb: -------------------------------------------------------------------------------- 1 | class SendCheckinCodeSmsForFreeEvent < ActiveRecord::Migration 2 | def change 3 | Event.all.reject{|e| e.finished? or e.started?}.each do |event| 4 | event.orders.each do |order| 5 | order.create_participant if order.free? # 正在进行的免费活动补发签到码 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20150128225959_change_trade_no_to_event_order.rb: -------------------------------------------------------------------------------- 1 | class ChangeTradeNoToEventOrder < ActiveRecord::Migration 2 | def up 3 | change_column :event_orders, :trade_no, :string, limit: 32 4 | end 5 | 6 | def down 7 | change_column :event_orders, :trade_no, :string, limit: 16 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20150501061606_add_status_to_event_tickets.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToEventTickets < ActiveRecord::Migration 2 | def change 3 | add_column :event_tickets, :status, :string 4 | 5 | EventTicket.all.update_all(status: :opened) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20150516142649_add_require_invoice_to_event_orders.rb: -------------------------------------------------------------------------------- 1 | class AddRequireInvoiceToEventOrders < ActiveRecord::Migration 2 | def change 3 | add_column :event_orders, :require_invoice, :boolean, default: false 4 | 5 | EventOrder.find_each(batch_size: 100) do |order| 6 | order.update_column :require_invoice, order.provide_invoice 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | 9 | is_heroku = ['/app/','/app'].include?(ENV['HOME']) # ENV['HOME'] = '/app' in rails console or rake 10 | email = Settings.raw_email(Settings.email.from) 11 | emails = [email] 12 | emails << 'demo@19wu.com' if Rails.env.development? || is_heroku 13 | emails.each do |email| 14 | login = email.sub(/@.*/,'') # 'support@19wu.com' => 'support' 15 | user = User.where(:login => login, :email => email).first_or_create(:password => '666666').confirm! 16 | user.admin = true 17 | user.save(validate:false) 18 | end 19 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/core_ext/hash/angularjs.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | # {a:1, b:2}.to_ng_init # a=1;b=2 3 | def to_ng_init 4 | self.inject([]) do |result,(key,value)| 5 | value = if value.is_a?(Array) 6 | "[#{value.map(&:to_json).join(',')}]" 7 | else 8 | value.to_json 9 | end 10 | result << "#{key}=#{value}" 11 | end.join(';') 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/fancy_url/fancy_url.rb: -------------------------------------------------------------------------------- 1 | # stolen code from 2 | # https://scribdtech.wordpress.com/2010/09/01/vanity-user-profile-urls-in-rails/ 3 | module FancyUrl 4 | extend self 5 | 6 | def generate_cached_routes 7 | # Find all route we use 8 | @cached_routes = Rails.application.routes.routes.map {|route| 9 | route.path.spec.to_s.split('(').first.split('/').second 10 | }.compact.uniq 11 | 12 | # Remove routes whose first path component is a variable or wildcard 13 | @cached_routes.reject! { |route| 14 | route.starts_with?(':') or route.starts_with?('*') } 15 | 16 | # Remove the default rails route 17 | @cached_routes.delete 'rails' 18 | @cached_routes 19 | end 20 | 21 | def cached_routes 22 | @cached_routes || self.generate_cached_routes 23 | end 24 | 25 | def valid_for_short_url?(slug) 26 | not (slug.include?('.') or self.cached_routes.include?(slug)) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/jruby_patches/escape_utils.rb: -------------------------------------------------------------------------------- 1 | require 'cgi' 2 | require 'uri' 3 | 4 | module EscapeUtils 5 | extend self 6 | 7 | def escape_html(html) 8 | CGI.escapeHTML(html) 9 | end 10 | 11 | def escape_uri(uri) 12 | URI.escape(uri) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/jruby_patches/github/markdown.rb: -------------------------------------------------------------------------------- 1 | require 'kramdown' 2 | 3 | module GitHub 4 | class Markdown 5 | def self.to_html(content, ignore) 6 | Kramdown::Document.new(content).to_html 7 | end 8 | 9 | def self.render(content) 10 | self.to_html(content, :markdown) 11 | end 12 | 13 | def self.render_gfm(content) 14 | self.to_html(content, :gfm) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/assets.rake: -------------------------------------------------------------------------------- 1 | namespace :assets do 2 | # Because of config.assets.initialize_on_precompile = false, 3 | # must load i18n manually 4 | task :i18n_environment do 5 | require Rails.root.join('app/models/settings') 6 | I18n.locale = Settings.default_locale 7 | I18n.load_path += Dir[Rails.root.join('config/locales/*.yml')] 8 | end 9 | 10 | task :environment => :i18n_environment 11 | end 12 | 13 | -------------------------------------------------------------------------------- /lib/tasks/check.rake: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # 对帐 3 | # rake check 4 | task :check do 5 | unless Rails.env == 'production' # 防止生产环境下执行 6 | refunds = 0 7 | prices = 0 8 | 9 | File.readlines('alipay.txt')[5..-5].each do |line| 10 | info = line.split(',') 11 | number = info[1] 12 | price = info[9].to_f 13 | fee = info[12] 14 | refund = info[13].to_f 15 | 16 | prices += price 17 | refunds += refund 18 | puts number if refund > 0 19 | # puts number if refund > 0 && price != refund 20 | # puts "#{number}, #{price}, #{fee}, #{refund}" 21 | end 22 | 23 | puts prices 24 | puts refunds 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/tasks/schema_ext.rake: -------------------------------------------------------------------------------- 1 | desc "Extend ActiveRecord::Schema to ignore enable_extension for adapter does not support it" 2 | task :schema_ext => :environment do 3 | class ActiveRecord::Schema 4 | def enable_extension(*args, &block) 5 | if connection.respond_to?(:enable_extension) 6 | connection.enable_extension(*args, &block) 7 | else 8 | $stderr.puts "enable_extension is not supported in current database adapter" 9 | end 10 | end 11 | end 12 | end 13 | 14 | task 'db:schema:load' => :schema_ext 15 | -------------------------------------------------------------------------------- /lib/tasks/stats.rake: -------------------------------------------------------------------------------- 1 | desc "Add files that DHH doesn't consider to be 'code' to stats" 2 | task :stats_setup do 3 | require 'rails/code_statistics' 4 | 5 | class CodeStatistics 6 | alias calculate_statistics_orig calculate_statistics 7 | def calculate_statistics 8 | @pairs.inject({}) do |stats, pair| 9 | if 3 == pair.size 10 | stats[pair.first] = calculate_directory_statistics(pair[1], pair[2]); stats 11 | else 12 | stats[pair.first] = calculate_directory_statistics(pair.last); stats 13 | end 14 | end 15 | end 16 | end 17 | ::STATS_DIRECTORIES << ['Views', 'app/views', /\.(rhtml|erb|rb|slim|jbuilder)$/] 18 | ::STATS_DIRECTORIES << ['JS', 'app/assets/javascripts', /\.(js|coffee)$/] 19 | ::STATS_DIRECTORIES << ['CSS', 'app/assets/stylesheets', /\.(css|sass|scss)$/] 20 | 21 | ::STATS_DIRECTORIES << ['JS Test', 'spec/javascripts', /\.(js|coffee)$/] 22 | ::CodeStatistics::TEST_TYPES << 'JS Test' 23 | end 24 | task :stats => :stats_setup 25 | -------------------------------------------------------------------------------- /lib/tasks/travis.rake: -------------------------------------------------------------------------------- 1 | namespace :travis do 2 | def verbose_system(*args) 3 | puts(args.join(' ')) 4 | system(*args) 5 | end 6 | task :before_script do 7 | db = ENV['DATABASE'] || 'pg' 8 | verbose_system("cp -f config/database.yml.example.#{db} config/database.yml") 9 | verbose_system("cp -f config/settings.yml.example config/settings.yml") 10 | verbose_system("bundle exec rake db:drop db:create db:schema:load --trace 2>&1") 11 | end 12 | 13 | # append 'jasmine:ci' to run js tests 14 | task :script => [:'spec:javascript', :spec] 15 | end 16 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/_form.html.slim: -------------------------------------------------------------------------------- 1 | = simple_form_for(@<%= singular_table_name %>) do |f| 2 | = f.error_notification 3 | 4 | .form-inputs 5 | - attributes.each do |attribute| 6 | = f.send (attribute.reference? ? :association : :input), attribute.name 7 | 8 | .form-actions 9 | = f.button :submit 10 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/public/favicon.ico -------------------------------------------------------------------------------- /public/flash/clippy.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/public/flash/clippy.swf -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/controllers/admin/order_fulfillments_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Admin::OrderFulfillmentsController do 4 | let(:user) { create(:user, :confirmed, :admin) } 5 | let!(:order) { create(:order_with_items, provide_invoice: true, paid: true) } 6 | before { login_user user } 7 | 8 | describe "GET index" do 9 | it "should be success" do 10 | get :index 11 | expect(assigns[:orders].first).to eql order 12 | end 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /spec/controllers/autocomplete_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AutocompleteController do 4 | 5 | describe "GET 'users'" do 6 | let(:user) { login_user } 7 | it "returns http success" do 8 | get 'users', login: user.login 9 | response.should be_success 10 | JSON.parse(response.body).should eql [user.login] 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /spec/controllers/collaborators_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe CollaboratorsController do 4 | let(:user) { login_user } 5 | let(:partner) { create(:user) } 6 | let(:event) { create(:event, user: user) } 7 | let(:collaborator) { create(:group_collaborator, user_id: partner.id, group_id: event.group.id) } 8 | 9 | describe "GET 'index'" do 10 | before { collaborator } 11 | it "renders the participant list" do 12 | get :index, event_id: event.id 13 | assigns[:collaborators].first[:id].should eql collaborator.id 14 | end 15 | end 16 | describe "POST 'create'" do 17 | it 'creates the collaborator' do 18 | expect { 19 | post :create, event_id: event.id, login: partner.login 20 | }.to change{GroupCollaborator.count}.by(1) 21 | end 22 | end 23 | describe "DELETE 'destroy'" do 24 | before { collaborator } 25 | it 'destroy the collaborator' do 26 | expect { 27 | delete :destroy, event_id: event.id, id: collaborator.id 28 | }.to change{GroupCollaborator.count}.by(-1) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/controllers/event/invoices_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Event::InvoicesController do 4 | 5 | describe "GET 'index'" do 6 | let(:user) { create(:user, :confirmed, :admin) } 7 | let!(:order) { create(:order_with_items, provide_invoice: true, paid: true) } 8 | before { login_user user } 9 | 10 | it "renders the invoice" do 11 | get 'index', event_id: order.event.id, format: :xlsx 12 | expect(assigns[:orders].first).to eql order 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /spec/controllers/event_changes_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventChangesController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/controllers/event_tickets_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventTicketsController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/controllers/exprot_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ExportController do 4 | 5 | describe "GET 'index'" do 6 | let(:user) { login_user } 7 | let(:event) { create(:event, user: user) } 8 | 9 | it "renders the export index" do 10 | get 'index', :event_id => event.id 11 | response.should render_template('index') 12 | end 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /spec/controllers/group_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GroupController do 4 | 5 | describe "GET 'show'" do 6 | let(:event) { create :event } 7 | it "returns http success" do 8 | get 'event', :slug => event.group.slug 9 | response.should be_success 10 | end 11 | end 12 | 13 | describe "GET 'followers'" do 14 | let(:event) { create :event } 15 | it "returns http success" do 16 | get 'event', :slug => event.group.slug 17 | response.should be_success 18 | end 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/controllers/home_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HomeController do 4 | 5 | describe "GET 'index'" do 6 | login_user 7 | it "returns http success" do 8 | get 'index' 9 | response.should be_success 10 | end 11 | end 12 | 13 | describe "POST content_preview" do 14 | it "returns http response" do 15 | post 'content_preview', :content => "# test" 16 | response.should be_success 17 | JSON.parse(response.body)["result"].should == "

test

" 18 | end 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/controllers/participants_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ParticipantsController do 4 | let(:user) { create(:user, :confirmed) } 5 | let(:event) { create(:event, user: user) } 6 | let(:trade_no) { '2013080841700373' } 7 | let!(:order) { create(:order_with_items, event: event) } 8 | before { login_user user } 9 | 10 | describe "GET export" do 11 | before { order.pay! trade_no } 12 | it "should be success" do 13 | get :export, format: :xlsx, event_id: event.id 14 | expect(assigns[:orders].first).to eql order 15 | expect(response).to be_success 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/controllers/photo_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PhotoController do 4 | 5 | describe "POST 'create'" do 6 | let(:path) { Rails.root.join("spec/factories/data/event/map.png") } 7 | let(:files) { [Rack::Test::UploadedFile.new(path)] } 8 | context 'when user has signed in' do 9 | login_user 10 | it 'creates the photo' do 11 | expect { 12 | post :create, :files => files 13 | JSON[response.body]['files'][0]['name'].should == 'map' 14 | JSON[response.body]['files'][0]['url'].should_not be_blank 15 | }.to change{Photo.count}.by(1) 16 | end 17 | end 18 | context 'when user has not yet signed in' do 19 | before { post :create, :files => files } 20 | it {should redirect_to new_user_session_path } 21 | end 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /spec/controllers/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SessionsController do 4 | end 5 | -------------------------------------------------------------------------------- /spec/controllers/topic_reply_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe TopicReplyController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/controllers/topics_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe TopicsController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/controllers/user_phones_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe UserPhonesController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe UsersController do 4 | describe 'GET show' do 5 | let(:login) { '19wu' } 6 | let!(:user) { create(:user, :login => login) } 7 | 8 | it 'finds user by login' do 9 | get 'show', :id => login 10 | assigns[:user].should == user 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/data/event/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/spec/factories/data/event/map.png -------------------------------------------------------------------------------- /spec/factories/data/event/map_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/spec/factories/data/event/map_big.png -------------------------------------------------------------------------------- /spec/factories/event_changes.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | FactoryGirl.define do 3 | factory :event_change do 4 | content "深圳 Rubist 活动举办的时间由原来的6月15日(周六)改为6月16日(周日),请见谅。" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/event_order_fulfillments.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order_fulfillment do 5 | order 6 | tracking_number '112521197075' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/event_order_items.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order_item do 5 | order 6 | ticket 7 | quantity 1 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/event_order_participants.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order_participant do 5 | order_id 1 6 | event_id 1 7 | checkin_code "1234" 8 | 9 | trait :checkin do 10 | checkin_at "2013-08-17 10:50:41" 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/event_order_refunds.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order_refund do 5 | amount '10' 6 | reason "test" 7 | 8 | trait :submited do 9 | status 'submited' 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/factories/event_order_shipping_addresses.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Read about factories at https://github.com/thoughtbot/factory_girl 3 | 4 | FactoryGirl.define do 5 | factory :event_order_shipping_address do 6 | order_id 1 7 | invoice_title "深圳市19屋电子商务有限公司" 8 | province "440000" 9 | city "440300" 10 | district "440305" 11 | address "科技园南区" 12 | name "saberma" 13 | phone "13928452888" 14 | end 15 | 16 | factory :shipping_address, parent: :event_order_shipping_address do 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/factories/event_order_status_transitions.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order_status_transition do 5 | event_order nil 6 | event "MyString" 7 | from "MyString" 8 | to "MyString" 9 | created_at "2013-08-19 01:58:26" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/factories/event_orders.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_order do 5 | event 6 | user 7 | price 1.5 8 | 9 | factory :order_with_items do 10 | ignore do 11 | items_count 1 12 | quantity 1 13 | tickets_price 299 14 | provide_invoice false 15 | paid false 16 | end 17 | after(:build) do |order, evaluator| 18 | FactoryGirl.create_list(:ticket, evaluator.items_count, price: evaluator.tickets_price, require_invoice: evaluator.provide_invoice, event: order.event).each do |ticket| 19 | order.items.build ticket: ticket, quantity: evaluator.quantity, price: ticket.price 20 | end 21 | if order.require_invoice = evaluator.provide_invoice 22 | order.shipping_address_attributes = attributes_for(:shipping_address) 23 | end 24 | end 25 | after(:create) do |order, evaluator| 26 | order.pay!('2013080841700373') if evaluator.paid 27 | end 28 | end 29 | end 30 | 31 | factory :order, parent: :event_order do 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/factories/event_participants.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event_participant do 5 | user_id 1 6 | event_id 1 7 | trait :random_user do 8 | user 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/factories/event_summaries.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :event_summary do 3 | content "This is a great party! I really like it!" 4 | event 5 | end 6 | end -------------------------------------------------------------------------------- /spec/factories/event_tickets.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Read about factories at https://github.com/thoughtbot/factory_girl 3 | 4 | FactoryGirl.define do 5 | factory :event_ticket do 6 | name "公司票" 7 | price 299 8 | require_invoice false 9 | description "提供发票,发票项目为技术服务费,会后45天内统一发快递,票价包含顺丰发票快递费22元" 10 | tickets_quantity 400 11 | event_id 1 12 | 13 | trait :free do 14 | price 0 15 | end 16 | end 17 | 18 | factory :ticket, parent: :event_ticket do 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/factories/events.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :event do 5 | title "19wu development meeting" 6 | start_time 8.day.since 7 | end_time 9.days.since 8 | location "Tianjin, China" 9 | content "Contents here" 10 | slug "rubyconfchina" 11 | user 12 | 13 | trait :finished do 14 | start_time 8.day.ago 15 | end_time 7.days.ago 16 | end 17 | 18 | trait :markdown do 19 | content <<-MD 20 | # Awesome Event # 21 | 22 | - free wifi 23 | - free coffee 24 | MD 25 | location_guide <<-MD 26 | subway line 2, foo station 27 | MD 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/factories/fallback_urls.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :fallback_url do 5 | origin "ruby-conf-china" 6 | change_to "rubyconfchina" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/group_collaborators.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :group_collaborator do 5 | group_id 1 6 | user_id 1 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/group_topic_replies.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | FactoryGirl.define do 3 | factory :group_topic_reply do 4 | body "非常赞成!" 5 | group_topic_id 1 6 | user_id 1 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/group_topics.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | FactoryGirl.define do 3 | factory :group_topic do 4 | title "收集大家感兴趣的主题" 5 | body "如题" 6 | user 7 | 8 | trait :with_group do 9 | group 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/factories/groups.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :group do 5 | slug "rubyconfchina" 6 | user 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/profiles.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :profile do 5 | trait :with_user do 6 | user 7 | end 8 | 9 | trait :autofill do 10 | name { user.try(:login).try(:capitalize) } 11 | website ['http://19wu.org', 'http://19wu.com'].sample 12 | bio '**Launch your Event today**' 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/factories/refund_batches.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :refund_batch do 5 | batch_no "MyString" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/sequences.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :sequence do 5 | date "2013-08-25" 6 | number 1 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | sample_login = ['jack', 'lucy', 'dave', 'lily', 'john', 'beth'].sample 3 | sequence(:login) { |n| "#{sample_login}#{n}" } 4 | sequence(:email) { |n| "#{sample_login}#{n}@19wu.org".downcase } 5 | sequence(:phone) { |n| "1392845288#{n}"} 6 | 7 | factory :user do 8 | login 9 | email 10 | phone 11 | password ['DJX5nvyX', 'GG83Sr4{', '_pW.2P*8', 'MH^IN3B_'].sample 12 | 13 | trait :confirmed do 14 | confirmed_at '2013-01-01' 15 | end 16 | trait :admin do 17 | admin true 18 | end 19 | trait :with_profile do 20 | profile 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/features/admin/order_fulfillments_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.expand_path('../../../spec_helper', __FILE__) 3 | 4 | feature 'orders fulfillments', js: true do 5 | given(:support) { create(:user, :confirmed, :admin) } 6 | given!(:order) { create(:order_with_items, provide_invoice: true, paid: true) } 7 | given(:tracking_number) { '112521197075' } 8 | 9 | before { sign_in support } 10 | 11 | scenario 'I can save fulfillment tracking number' do 12 | visit admin_fulfillments_path 13 | fill_in '顺丰快递单号', with: tracking_number 14 | click_on '提交' 15 | expect(page).to have_content("抬头:#{order.shipping_address.invoice_title}") 16 | expect(page).to have_content("金额:#{order.paid_amount.to_i}") 17 | expect(page).to have_content("顺丰快递单号:#{tracking_number}") 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/features/event_changes_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.expand_path('../../spec_helper', __FILE__) 3 | 4 | feature 'event changes' do 5 | given(:user) { create(:user, :confirmed) } 6 | given(:event) { create(:event, user: user) } 7 | given(:content) { attributes_for(:event_change)[:content] } 8 | before { sign_in user } 9 | scenario 'I can create changes' do 10 | visit new_event_change_path(event) 11 | fill_in 'event_change[content]', with: content 12 | click_on '新增变更' 13 | page.should have_content(content) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/features/event_page_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | feature 'event page' do 4 | given(:content) { 5 | <<-MD 6 | # title # 7 | 8 | - list 9 | MD 10 | } 11 | 12 | given(:event) { FactoryGirl.create(:event, :content => content, :user => login_user) } 13 | 14 | scenario 'I see the markdown formatted event content' do 15 | visit event_path(event) 16 | 17 | page.should have_selector('.event-body h1', :text => 'title') 18 | page.should have_selector('.event-body li', :text => 'list') 19 | end 20 | 21 | context 'with orders' do 22 | let(:orders) { create_list(:order_with_items, 5, event: event) } 23 | before { orders } 24 | scenario 'I see list of participants', js: true do 25 | visit event_path(event) 26 | page.should have_selector('.event-participants img.gravatar', :count => 5) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/features/event_summary_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature "add event summary to an event" do 4 | let(:user) { create(:user, :confirmed) } 5 | let(:event) { create(:event, user: user) } 6 | let(:submit) { I18n.t('helpers.submit.create', model: I18n.t('activerecord.models.event_summary')) } 7 | 8 | background do 9 | sign_in user 10 | end 11 | 12 | scenario "submit with valid data" do 13 | visit new_event_summary_path(event) 14 | fill_in :event_summary_content, with: "I really like this project, I learned a lot and growed my self." 15 | click_button submit 16 | 17 | page.should have_content 'I really like this project' 18 | end 19 | 20 | scenario "submit with invalid data" do 21 | visit new_event_summary_path(event) 22 | fill_in :event_summary_content, with: "broken!" 23 | click_on submit 24 | 25 | page.should have_content I18n.t('simple_form.error_notification.default_message') 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/features/fallback_url_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature "Fallback Url" do 4 | given(:event) { create :event, slug: 'ruby-conf-china' } 5 | before do 6 | event.update_attributes! slug: 'rubyconfchina' 7 | end 8 | scenario "I can use origin url to visit event page" do 9 | visit '/ruby-conf-china' 10 | page.should have_content event.title 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/features/follower_page_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | feature 'follower page' do 4 | describe 'create event with participant info' do 5 | let(:event) { create(:event, :user => create(:user)) } 6 | let(:participant) { login_user } 7 | subject { event.group } 8 | 9 | before do 10 | Event.stub(:find).with(event.id.to_s).and_return(event) 11 | participant.follow(subject) 12 | end 13 | 14 | describe 'init show' do 15 | it "has user information" do 16 | visit event_path(event)+'/followers' 17 | page.should have_selector('a', text: event.title) 18 | page.find("li#group_event_#{participant.id} span.follower-login-name").should have_content(participant.login) 19 | end 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/features/landing_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature "Landing Page" do 4 | scenario "I see the sign up form in landing page" do 5 | visit root_path 6 | page.should have_selector('form') 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/features/loc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature "LOC" do 4 | before do 5 | stats = `bundle exec rake stats | grep -v specs` 6 | @total_loc = stats.scan(/Code LOC: (\d+)/).first.first.to_i 7 | @views_loc = stats.scan(/Views\s*\|\s*\d+\s*\|\s*(\d+)/).first.first.to_i 8 | @css_loc = stats.scan(/CSS\s*\|\s*\d+\s*\|\s*(\d+)/).first.first.to_i 9 | end 10 | 11 | pending "should keep Ruby&JS code less than 2000 line" do 12 | (@total_loc - @views_loc - @css_loc).should <= 2000 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/features/magic_encoding_spec.rb: -------------------------------------------------------------------------------- 1 | describe "Files missing magic encoding comment" do 2 | subject(:files_missing_magic_encoding) { 3 | load File.expand_path('../../../bin/magic_encoding_find', __FILE__) 4 | magic_encoding_find 5 | } 6 | 7 | it "should be empty" do 8 | unless files_missing_magic_encoding.empty? 9 | count = files_missing_magic_encoding.count 10 | $stderr.puts < 'bob') } 6 | background { sign_in } 7 | 8 | scenario "I see bob's profile page using /bob" do 9 | visit '/bob' 10 | page.should have_content 'bob' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/features/user_phones_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.expand_path('../../spec_helper', __FILE__) 3 | 4 | feature 'user phones' do 5 | given(:user) { create(:user, :confirmed, phone: nil) } 6 | given(:code) { '1234' } 7 | before do 8 | sign_in user 9 | Rails.cache.clear 10 | end 11 | scenario 'I can save phone', js: true do 12 | click_on I18n.t('flash.user_phones.here') 13 | fill_in 'phone', with: '13928452888' 14 | click_on I18n.t('buttons.user_phone.get_code') 15 | fill_in 'code', with: code 16 | click_on I18n.t('helpers.submit.user.update') 17 | page.should have_content(I18n.t('flash.user_phones.updated')) 18 | page.should_not have_content(I18n.t('flash.user_phones.here')) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/helpers/event_order_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe EventOrderHelper do 5 | describe '#stats_tickets_price' do 6 | let(:event) { create :event } 7 | let!(:orders) { create_list(:order_with_items, 6, tickets_price: 0.01, event: event) } 8 | subject { helper.stats_tickets_price(orders) } 9 | it { should eql 0.0 } 10 | it { should_not eql 0.060000000000000005 } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/helpers/home_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe HomeHelper do 5 | 6 | describe 'active?' do 7 | context 'menu is events' do 8 | let(:menu) { :events } 9 | subject { helper.active?(current, menu) } 10 | context 'current is events' do 11 | let(:current) { :events } 12 | it { should == :active } 13 | end 14 | context 'current is not events' do 15 | let(:current) { :activities } 16 | it { should be_blank } 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/javascripts/angular/filters.js.coffee: -------------------------------------------------------------------------------- 1 | describe "filters", -> 2 | money = null 3 | beforeEach -> 4 | module '19wu' 5 | inject ($filter) -> 6 | money = $filter('money') 7 | 8 | it 'should load the data', () -> 9 | expect(money('0.0')).toEqual('免费') 10 | expect(money('0.01')).toEqual('0.01') 11 | -------------------------------------------------------------------------------- /spec/javascripts/angular/services.js.coffee: -------------------------------------------------------------------------------- 1 | describe "participants", -> 2 | participants = null 3 | beforeEach -> 4 | module '19wu' # fixed jasmine: Error: Unknown provider: participantsProvider <- participants 5 | inject ($rootScope, $injector, $http) -> 6 | $rootScope.event = {id: 1} 7 | data = [{"id":1,"login":"demo","created_at":"2013-03-09T08:36:49+08:00","avatar_url":"","profile":{"name":"","website":"","bio_html":""}}] 8 | $httpBackend = $injector.get('$httpBackend') 9 | $httpBackend.when('GET', '/api/events/1/participants').respond(200, data) 10 | participants = $injector.get('participants') 11 | $httpBackend.flush() 12 | 13 | it 'should load the data', () -> 14 | expect(participants.data.length).toBe(1) 15 | -------------------------------------------------------------------------------- /spec/javascripts/helpers/factory.js.coffee: -------------------------------------------------------------------------------- 1 | # https://gist.github.com/2022574 2 | 3 | # TODO: add method to create Spine Model directly. 4 | 5 | @Factory = Factory = {} 6 | 7 | ids = {} 8 | sequences = {} 9 | sequence = (name, callback) -> sequences[name] = callback 10 | 11 | define = (name, defaults = {}) -> 12 | Factory[name] = (attrs = {}) -> 13 | result = $.extend {}, defaults, attrs 14 | for k, v of result 15 | if typeof v is 'function' 16 | result[k] = v.call(result, result) 17 | result 18 | 19 | next = (name) -> 20 | -> 21 | ids[name] = (ids[name] ? 0) + 1 22 | console.log ids[name] 23 | sequences[name]?(ids[name]) -------------------------------------------------------------------------------- /spec/javascripts/helpers/html_sandbox_helpers.js.coffee: -------------------------------------------------------------------------------- 1 | # beforeEach -> 2 | # @htmlSandbox = $("
").appendTo($("body")) 3 | # 4 | # afterEach -> 5 | # @htmlSandbox.remove() 6 | # @htmlSandbox = null 7 | -------------------------------------------------------------------------------- /spec/javascripts/specs.js.coffee: -------------------------------------------------------------------------------- 1 | # This pulls in all your specs from the javascripts directory into Jasmine: 2 | # 3 | # spec/javascripts/*_spec.js.coffee 4 | # spec/javascripts/*_spec.js 5 | # spec/javascripts/*_spec.js.erb 6 | # IT IS UNLIKELY THAT YOU WILL NEED TO CHANGE THIS FILE 7 | # 8 | #= require application 9 | #= require angular-mocks 10 | #= require locales/zh-CN 11 | #=require_tree ./helpers 12 | #=require_tree ./ 13 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # path to parent directory of src_files 2 | # relative path from Rails.root 3 | # defaults to app/assets/javascripts 4 | # src_dir: "app/assets/javascripts" 5 | 6 | # list of file expressions to include as source files 7 | # relative path from scr_dir 8 | src_files: 9 | - "**/*.{js,coffee}" 10 | 11 | # path to parent directory of spec_files 12 | # relative path from Rails.root 13 | # defaults to spec/javascripts 14 | # spec_dir: spec/javascripts 15 | 16 | # list of file expressions to include as helpers into spec runner 17 | # relative path from spec_dir 18 | helpers: 19 | - "helpers/**/*.{js,coffee}" 20 | 21 | # list of file expressions to include as specs into spec runner 22 | # relative path from spec_dir 23 | spec_files: 24 | - "**/*.{js,coffee}" -------------------------------------------------------------------------------- /spec/javascripts/users/phones.js.coffee: -------------------------------------------------------------------------------- 1 | describe "user phones send code", -> 2 | scope = $httpBackend = null 3 | beforeEach(inject(($rootScope, $controller, $injector, $http) -> 4 | scope = $rootScope.$new() 5 | scope.phone = '13928452888' 6 | $httpBackend = $injector.get('$httpBackend') 7 | ctrl = $controller(UserPhonesCtrl, {$scope: scope, $http: $http}) 8 | )) 9 | 10 | it "should be success", -> 11 | $httpBackend.when('POST', '/user_phone/send_code').respond({}) 12 | scope.send_code() 13 | $httpBackend.flush() 14 | expect(scope.showed).toEqual true 15 | expect(scope.wait).toEqual true 16 | expect(scope.timeleft).toBeLessThan 60 17 | -------------------------------------------------------------------------------- /spec/lib/core_ext/hash/angularjs_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Hash do 4 | describe '#to_ng_init' do 5 | subject { {a: 1, c:true, d: 's'}.to_ng_init } 6 | it { should eql "a=1;c=true;d=\"s\"" } 7 | context 'with hash value' do 8 | subject { {a: {b: 1}}.to_ng_init } 9 | it { should eql "a={\"b\":1}" } 10 | end 11 | context 'with array value' do 12 | subject { {tickets: [{name: 'person'}, {name: 'company'}]}.to_ng_init } 13 | it { should eql "tickets=[{\"name\":\"person\"},{\"name\":\"company\"}]" } 14 | end 15 | context 'with nil value' do 16 | subject { {a: nil}.to_ng_init } 17 | it { should eql "a=null" } 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/mailers/user_mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe UserMailer do 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/event_change_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe EventChange do 5 | describe 'send change to participants' do 6 | let(:user) { create(:user, :confirmed) } 7 | let(:event) { create(:event, user: user, title: '深圳Rubyist活动') } 8 | let!(:order1) { create(:order_with_items, event: event) } 9 | let!(:order2) { create(:order_with_items, event: event) } 10 | let(:change) { create(:event_change, event: event) } 11 | describe 'by email' do 12 | subject { ActionMailer::Base.deliveries.last } 13 | before do 14 | ChinaSMS.stub(:to) 15 | ActionMailer::Base.deliveries.clear 16 | change 17 | end 18 | its(:subject) { should eql '「重要」深圳Rubyist活动 变更通知' } 19 | its('body.decoded') { should match change.content } 20 | end 21 | describe 'by sms' do 22 | it 'should be success' do 23 | phones = [order2.user.phone, order1.user.phone].sort 24 | ChinaSMS.stub(:to).with(phones, I18n.t('sms.event.change', content: attributes_for(:event_change)[:content])) 25 | change 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/models/event_order_fulfillment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventOrderFulfillment do 4 | let(:order) { create(:order_with_items, provide_invoice: true, paid: true) } 5 | 6 | describe 'mail to' do 7 | let(:mail) { double('mail') } 8 | describe 'user' do 9 | it 'should be done' do 10 | mail.should_receive(:deliver) 11 | OrderFulfillmentMailer.should_receive(:notify_user_fulfilled).and_return(mail) 12 | order.create_fulfillment attributes_for(:event_order_fulfillment) 13 | end 14 | end 15 | describe 'organizer' do 16 | it 'should be done' do 17 | mail.should_receive(:deliver) 18 | OrderFulfillmentMailer.should_receive(:notify_organizer_fulfilled).and_return(mail) 19 | order.create_fulfillment attributes_for(:event_order_fulfillment) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/models/event_order_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventOrderItem do 4 | let(:event) { create(:event) } 5 | let(:order) { create(:order_with_items, event: event, quantity: 2) } 6 | let(:order_item) { order.items.first } 7 | 8 | describe 'create' do 9 | subject { order_item } 10 | its(:unit_price) { should eql 299.0 } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/models/event_order_shipping_address_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | require 'spec_helper' 3 | 4 | describe EventOrderShippingAddress do 5 | subject(:shipping_address) { build(:shipping_address) } 6 | describe '#info' do 7 | its(:info) { should eql '广东省深圳市南山区 科技园南区 saberma 13928452888'} 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/event_summary_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventSummary do 4 | it "must have a content" do 5 | summary = EventSummary.new(event_id: create(:event).id) 6 | expect(summary.valid?).to be_false 7 | end 8 | 9 | it "have a content with a minimum lenght of 10" do 10 | summary = EventSummary.new(event_id: create(:event).id, content: "12345") 11 | expect(summary.valid?).to be_false 12 | end 13 | end -------------------------------------------------------------------------------- /spec/models/fallback_url_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe FallbackUrl do 4 | let(:event) { create :event, slug: 'ruby-conf-china' } 5 | context 'is not exists' do 6 | before { event } 7 | it 'should be create' do 8 | expect do 9 | event.update_attributes! slug: 'rubyconfchina' 10 | end.to change{FallbackUrl.count}.by(1) 11 | fallback_url = FallbackUrl.find_by_origin('ruby-conf-china') 12 | fallback_url.change_to.should eql 'rubyconfchina' 13 | end 14 | end 15 | context 'exists' do 16 | before { create :fallback_url, origin: 'ruby-conf-china', change_to: 'rubyconf' } 17 | it 'should be update' do 18 | expect do 19 | event.update_attributes! slug: 'rubyconfchina' 20 | end.not_to change{FallbackUrl.count} 21 | fallback_url = FallbackUrl.find_by_origin('ruby-conf-china') 22 | fallback_url.change_to.should eql 'rubyconfchina' 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/models/group_collaborator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GroupCollaborator do 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Group do 4 | describe "#last_event_with_summary" do 5 | let(:user) { create(:user, :confirmed) } 6 | let(:event1) { create(:event, user: user, start_time: 6.day.ago, end_time: 5.day.ago) } 7 | let(:event2) { create(:event, user: user, start_time: 4.day.ago, end_time: 3.day.ago) } 8 | let(:event3) { create(:event, user: user, start_time: 2.day.ago, end_time: 1.day.ago) } 9 | 10 | it "should reture the last event with summary" do 11 | create(:event_summary, event: event1) 12 | create(:event_summary, event: event2) 13 | 14 | event1.group.last_event_with_summary.should == event2 15 | end 16 | 17 | it "should return nil if none of the events has a summary" do 18 | event1.group.last_event_with_summary.should == nil 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/models/group_topic_reply_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GroupTopicReply do 4 | let(:topic) { create(:group_topic, :with_group) } 5 | 6 | # https://github.com/19wu/19wu/issues/421 7 | describe '#topic' do 8 | it 'returns topic the reply belongs to' do 9 | reply = build(:group_topic_reply, :group_topic_id => topic.id) 10 | expect(reply.topic).to be_a_kind_of(GroupTopic) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/models/group_topic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GroupTopic do 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/photo_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Photo do 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/refund_batch_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RefundBatch do 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/sequence_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Sequence do 4 | before { Timecop.freeze('2013-08-25') } 5 | after { Timecop.return } 6 | describe '#get' do 7 | subject { Sequence.get } 8 | context 'do not exist record' do 9 | it { should eql '201308250001' } 10 | end 11 | context 'exist record' do 12 | context 'in the same day' do 13 | before { create(:sequence, date: Time.zone.today, number: 1) } 14 | it { should eql '201308250002' } 15 | end 16 | context 'in the other day' do 17 | before { create(:sequence, date: 1.day.ago, number: 1) } 18 | it { should eql '201308250001' } 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/support/delayed_job.rb: -------------------------------------------------------------------------------- 1 | shared_context "delay jobs", :delay => true do 2 | before { 3 | Delayed::Job.delete_all 4 | Delayed::Worker.stub(:delay_jobs => true) 5 | } 6 | end 7 | -------------------------------------------------------------------------------- /spec/support/javascript.rb: -------------------------------------------------------------------------------- 1 | module JavascriptFeatureMacros 2 | extend ActiveSupport::Concern 3 | 4 | def stub_alert 5 | page.execute_script("window.alert = function(msg) { return true; }") 6 | end 7 | 8 | def stub_confirm 9 | page.execute_script("window.confirm = function(msg) { return true; }") 10 | end 11 | end 12 | 13 | RSpec.configure do |config| # http://git.io/WHvARA 14 | config.include JavascriptFeatureMacros, type: :feature 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/stub_gravatar_url.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | def gravatar_url(options={}) # do not use gravatar.com service in test environment 3 | ActionController::Base.helpers.asset_path("avatar-default.png") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/uploaders/photo_uploader_spec.rb: -------------------------------------------------------------------------------- 1 | require 'carrierwave/test/matchers' 2 | 3 | describe PhotoUploader do # http://git.io/XhkwBw 4 | include CarrierWave::Test::Matchers 5 | let(:photo) { Photo.new } 6 | 7 | before do 8 | PhotoUploader.enable_processing = true 9 | @uploader = PhotoUploader.new(photo, :image) 10 | @uploader.store!(File.open(path)) 11 | end 12 | 13 | after do 14 | PhotoUploader.enable_processing = false 15 | @uploader.remove! 16 | end 17 | 18 | 19 | context 'the normal version' do 20 | context 'from big image' do 21 | let(:path) { Rails.root.join("spec/factories/data/event/map_big.png") } # 630 x 650 22 | it "should scale down a landscape image to fit with 620 pixels width" do 23 | @uploader.normal.should have_dimensions(620, 640) # http://j.mp/14rqhZk 24 | end 25 | end 26 | context 'from small image' do 27 | let(:path) { Rails.root.join("spec/factories/data/event/map.png") } # 284 x 347 28 | it "should not be scale down" do 29 | @uploader.normal.should have_dimensions(284, 347) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/views/events/show.html.slim_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../../spec_helper', __FILE__) 2 | 3 | describe 'events/show.html.slim' do 4 | let(:event) { create(:event, :markdown) } 5 | let(:current_user) { build_stubbed(:user) } 6 | before do 7 | controller.stub(:current_user) { current_user } 8 | assign :event, event 9 | render :template => 'events/show', :locals => { :current_user => current_user} 10 | end 11 | subject { rendered } 12 | it { should have_content(event.title) } 13 | it { should include(event.content_html) } 14 | it { should include(event.location_guide_html) } 15 | end 16 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/javascripts/jquery.ba-throttle-debounce.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery throttle / debounce - v1.1 - 3/7/2010 3 | * http://benalman.com/projects/jquery-throttle-debounce-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this); -------------------------------------------------------------------------------- /vendor/assets/javascripts/jquery.textarea.caret.js: -------------------------------------------------------------------------------- 1 | // http://git.io/9dC7Gg 2 | jQuery.fn.extend({ 3 | insertAtCursor: function(myValue) { 4 | return this.each(function(i) { 5 | if (document.selection) { 6 | this.focus(); 7 | sel = document.selection.createRange(); 8 | sel.text = myValue; 9 | this.focus(); 10 | } 11 | else if (this.selectionStart || this.selectionStart == "0") { 12 | var startPos = this.selectionStart; 13 | var endPos = this.selectionEnd; 14 | var scrollTop = this.scrollTop; 15 | this.value = this.value.substring(0,startPos) + myValue + this.value.substring(endPos,this.value.length); 16 | this.focus(); 17 | this.selectionStart = startPos + myValue.length; 18 | this.selectionEnd = startPos + myValue.length; 19 | this.scrollTop = scrollTop; 20 | } 21 | else { 22 | this.value += myValue; 23 | this.focus(); 24 | } 25 | }); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saberma/19wu/f3c09e66908a61e930ef6e4d7d45ad9bac5a7397/vendor/plugins/.gitkeep -------------------------------------------------------------------------------- /zeus.json: -------------------------------------------------------------------------------- 1 | { 2 | "command": "ruby -rubygems -r./custom_plan -eZeus.go", 3 | 4 | "plan": { 5 | "boot": { 6 | "default_bundle": { 7 | "development_environment": { 8 | "prerake": {"rake": []}, 9 | "runner": ["r"], 10 | "console": ["c"], 11 | "server": ["s"], 12 | "generate": ["g"], 13 | "destroy": ["d"], 14 | "dbconsole": [] 15 | }, 16 | "test_environment": { 17 | "test_helper": {"test": ["rspec", "testrb"]} 18 | } 19 | } 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------