├── log
└── .gitkeep
├── .rspec
├── lib
├── tasks
│ ├── .gitkeep
│ ├── assets.rake
│ ├── schema_ext.rake
│ ├── travis.rake
│ ├── check.rake
│ └── stats.rake
├── assets
│ └── .gitkeep
├── jruby_patches
│ ├── escape_utils.rb
│ └── github
│ │ └── markdown.rb
├── templates
│ └── slim
│ │ └── scaffold
│ │ └── _form.html.slim
├── core_ext
│ └── hash
│ │ └── angularjs.rb
└── fancy_url
│ └── fancy_url.rb
├── app
├── mailers
│ ├── .gitkeep
│ ├── event_mailer.rb
│ ├── user_mailer.rb
│ └── order_fulfillment_mailer.rb
├── models
│ ├── .gitkeep
│ ├── fallback_url.rb
│ ├── photo.rb
│ ├── group_collaborator.rb
│ ├── event_order_status_transition.rb
│ ├── profile.rb
│ ├── content_filter.rb
│ ├── group_topic.rb
│ ├── event_summary.rb
│ ├── group_topic_reply.rb
│ ├── settings.rb
│ ├── event_order_shipping_address.rb
│ ├── follow.rb
│ ├── event_order_fulfillment.rb
│ ├── event_change.rb
│ ├── ability.rb
│ ├── sequence.rb
│ ├── group.rb
│ ├── event_order_refund_observer.rb
│ ├── event_ticket.rb
│ ├── event_order_observer.rb
│ ├── refund_batch.rb
│ └── event_observer.rb
├── assets
│ ├── stylesheets
│ │ ├── mixins
│ │ │ ├── .gitkeep
│ │ │ └── _sticky-footer.css.scss
│ │ ├── _3.states.css.scss
│ │ ├── _4.themes.css.scss
│ │ ├── modules
│ │ │ ├── .gitkeep
│ │ │ ├── _icons.css.scss
│ │ │ ├── _home.css.scss
│ │ │ ├── _table.css.scss
│ │ │ ├── _nav-bar.css.scss
│ │ │ ├── _label.css.scss
│ │ │ ├── _menu.css.scss
│ │ │ ├── _brand.css.scss
│ │ │ ├── _event-map.css.scss
│ │ │ ├── _user-bar.css.scss
│ │ │ ├── _event-order.css.scss
│ │ │ ├── _user-profile.css.scss
│ │ │ ├── _list.css.scss
│ │ │ ├── _social-count.css.scss
│ │ │ ├── _topic.css.scss
│ │ │ └── _jumbotron.css.scss
│ │ ├── _1.base.css.scss
│ │ └── _0.variables.css.scss
│ ├── images
│ │ ├── favicon.ico
│ │ ├── event
│ │ │ └── map.png
│ │ ├── feature-slug.png
│ │ ├── logo-wordmark.png
│ │ ├── sponsors
│ │ │ └── tui3.png
│ │ ├── avatar-default.png
│ │ ├── feature-follow.png
│ │ ├── spotlight-free.png
│ │ ├── spotlight-team.png
│ │ ├── spotlight-devices.png
│ │ └── feature-notification.png
│ └── javascripts
│ │ ├── angularjs
│ │ ├── filters.js.coffee
│ │ ├── services.js.coffee
│ │ ├── directives.js.coffee
│ │ ├── admin
│ │ │ └── order_fulfillments.js.coffee
│ │ ├── participants.js.coffee
│ │ ├── user_phones.js.coffee
│ │ ├── follows.js.coffee
│ │ ├── order_refund_submit.js.coffee
│ │ └── collaborators.js.coffee
│ │ ├── bootstrap-custom.js
│ │ ├── angularjs.js.coffee
│ │ ├── locales
│ │ └── zh-CN.js.erb
│ │ ├── application.js.coffee
│ │ └── datetime.js.coffee
├── views
│ ├── application
│ │ ├── 403.html.slim
│ │ ├── _flashes.html.slim
│ │ ├── _signed_out_user_bar.html.slim
│ │ ├── _signed_in_nav_bar.html.slim
│ │ ├── _signed_in_user_bar.html.slim
│ │ └── _clippy.html.slim
│ ├── user_orders
│ │ ├── alipay_done.html.slim
│ │ ├── _order_item.html.slim
│ │ └── index.html.slim
│ ├── order_refunds
│ │ ├── submit.json.jbuilder
│ │ ├── index.html.slim
│ │ └── _refunds.html.slim
│ ├── admin
│ │ └── order_fulfillments
│ │ │ └── create.json.jbuilder
│ ├── home
│ │ ├── index.html.slim
│ │ └── _menu.html.slim
│ ├── user_mailer
│ │ ├── welcome_email.zh-CN.text.erb
│ │ ├── welcome_email.text.erb
│ │ ├── invited_email.html.slim
│ │ ├── reminder_email.zh-CN.html.slim
│ │ ├── notify_email.zh-CN.html.slim
│ │ ├── welcome_email.zh-CN.html.slim
│ │ └── welcome_email.html.slim
│ ├── sessions
│ │ └── create.json.jbuilder
│ ├── registrations
│ │ └── create.json.jbuilder
│ ├── event_mailer
│ │ └── change_email.zh-CN.html.slim
│ ├── events
│ │ ├── _header.html.slim
│ │ ├── edit.html.slim
│ │ ├── index.html.slim
│ │ ├── ordered.html.slim
│ │ ├── new.html.slim
│ │ ├── followers.html.slim
│ │ └── _menu.html.slim
│ ├── event_orders
│ │ ├── create.json.jbuilder
│ │ └── _submenu.html.slim
│ ├── order_mailer
│ │ ├── notify_organizer_paid.html.slim
│ │ ├── notify_user_checkin_code.html.slim
│ │ ├── notify_user_paid.html.slim
│ │ ├── notify_support_refund.html.slim
│ │ ├── notify_organizer_refunded.html.slim
│ │ ├── notify_user_refunded.html.slim
│ │ ├── notify_organizer_created.html.slim
│ │ └── notify_user_created.html.slim
│ ├── participants
│ │ ├── update.json.jbuilder
│ │ ├── _submenu.html.slim
│ │ ├── checkin.html.slim
│ │ ├── index.html.slim
│ │ └── export.xlsx.axlsx
│ ├── devise
│ │ ├── mailer
│ │ │ ├── confirmation_instructions.zh-CN.text.erb
│ │ │ ├── confirmation_instructions.zh-CN.html.slim
│ │ │ ├── confirmation_instructions.text.erb
│ │ │ ├── confirmation_instructions.html.slim
│ │ │ ├── reset_password_instructions.zh-CN.html.slim
│ │ │ ├── reset_password_instructions.zh-CN.text.erb
│ │ │ ├── reset_password_instructions.html.slim
│ │ │ ├── reset_password_instructions.text.erb
│ │ │ └── invitation_instructions.html.slim
│ │ ├── invitations
│ │ │ ├── new.html.slim
│ │ │ └── edit.html.slim
│ │ ├── unlocks
│ │ │ └── new.html.slim
│ │ ├── passwords
│ │ │ ├── edit.html.slim
│ │ │ └── new.html.slim
│ │ ├── confirmations
│ │ │ └── new.html.slim
│ │ ├── registrations
│ │ │ ├── new.html.slim
│ │ │ └── edit.html.slim
│ │ ├── sessions
│ │ │ └── new.html.slim
│ │ └── shared
│ │ │ └── _links.slim
│ ├── event_tickets
│ │ ├── new.html.slim
│ │ ├── edit.html.slim
│ │ └── _form.html.slim
│ ├── event_summaries
│ │ ├── new.html.slim
│ │ └── _form.html.slim
│ ├── api
│ │ └── events
│ │ │ └── participants.rabl
│ ├── event_changes
│ │ ├── _submenu.html.slim
│ │ ├── index.html.slim
│ │ └── new.html.slim
│ ├── order_fulfillment_mailer
│ │ ├── notify_user_fulfilled.html.slim
│ │ └── notify_organizer_fulfilled.html.slim
│ ├── profiles
│ │ └── show.html.slim
│ ├── export
│ │ └── index.html.slim
│ ├── topics
│ │ ├── new.html.slim
│ │ └── _index.html.slim
│ ├── layouts
│ │ └── settings.html.slim
│ ├── invitations
│ │ ├── upgrade.html.slim
│ │ └── index.html.slim
│ ├── users
│ │ ├── show.html.slim
│ │ └── cohort.html.slim
│ └── collaborators
│ │ └── index.html.slim
├── controllers
│ ├── mockup_controller.rb
│ ├── export_controller.rb
│ ├── api
│ │ └── events_controller.rb
│ ├── autocomplete_controller.rb
│ ├── sessions_controller.rb
│ ├── registrations_controller.rb
│ ├── group_controller.rb
│ ├── home_controller.rb
│ ├── photo_controller.rb
│ ├── topic_reply_controller.rb
│ ├── users_controller.rb
│ ├── admin_orders_controller.rb
│ ├── profiles_controller.rb
│ ├── concerns
│ │ ├── has_api_response.rb
│ │ └── alipay_generatable.rb
│ ├── admin
│ │ └── order_fulfillments_controller.rb
│ ├── event_changes_controller.rb
│ ├── topics_controller.rb
│ ├── event_summaries_controller.rb
│ ├── collaborators_controller.rb
│ ├── participants_controller.rb
│ └── user_phones_controller.rb
├── helpers
│ ├── home_helper.rb
│ ├── user_helper.rb
│ ├── event_ticket_helper.rb
│ ├── admin
│ │ └── order_fulfillment_helper.rb
│ ├── event_order_helper.rb
│ └── user_orders_helper.rb
├── constraints
│ └── slug_constraint.rb
├── inputs
│ └── upload_photo_input.rb
├── menus
│ ├── event_submenu.rb
│ ├── event_orders_submenu.rb
│ └── event_menu.rb
└── middlewares
│ └── fallback_url_redirector.rb
├── vendor
├── plugins
│ └── .gitkeep
└── assets
│ ├── javascripts
│ ├── .gitkeep
│ ├── jquery.ba-throttle-debounce.min.js
│ └── jquery.textarea.caret.js
│ └── stylesheets
│ └── .gitkeep
├── public
├── favicon.ico
├── flash
│ └── clippy.swf
├── robots.txt
├── 500.html
├── 422.html
└── 404.html
├── spec
├── models
│ ├── photo_spec.rb
│ ├── group_topic_spec.rb
│ ├── refund_batch_spec.rb
│ ├── group_collaborator_spec.rb
│ ├── event_order_shipping_address_spec.rb
│ ├── event_order_item_spec.rb
│ ├── event_summary_spec.rb
│ ├── group_topic_reply_spec.rb
│ ├── sequence_spec.rb
│ ├── group_spec.rb
│ ├── event_order_fulfillment_spec.rb
│ ├── fallback_url_spec.rb
│ ├── event_change_spec.rb
│ └── event_ticket_spec.rb
├── mailers
│ └── user_mailer_spec.rb
├── factories
│ ├── data
│ │ └── event
│ │ │ ├── map.png
│ │ │ └── map_big.png
│ ├── event_summaries.rb
│ ├── event_changes.rb
│ ├── group_topic_replies.rb
│ ├── groups.rb
│ ├── refund_batches.rb
│ ├── sequences.rb
│ ├── group_collaborators.rb
│ ├── event_order_items.rb
│ ├── fallback_urls.rb
│ ├── event_order_fulfillments.rb
│ ├── group_topics.rb
│ ├── event_participants.rb
│ ├── event_order_refunds.rb
│ ├── event_order_status_transitions.rb
│ ├── event_order_participants.rb
│ ├── profiles.rb
│ ├── event_tickets.rb
│ ├── event_order_shipping_addresses.rb
│ ├── users.rb
│ ├── events.rb
│ └── event_orders.rb
├── controllers
│ ├── sessions_controller_spec.rb
│ ├── topics_controller_spec.rb
│ ├── topic_reply_controller_spec.rb
│ ├── user_phones_controller_spec.rb
│ ├── event_changes_controller_spec.rb
│ ├── event_tickets_controller_spec.rb
│ ├── users_controller_spec.rb
│ ├── autocomplete_controller_spec.rb
│ ├── exprot_controller_spec.rb
│ ├── admin
│ │ └── order_fulfillments_controller_spec.rb
│ ├── group_controller_spec.rb
│ ├── home_controller_spec.rb
│ ├── participants_controller_spec.rb
│ ├── photo_controller_spec.rb
│ ├── collaborators_controller_spec.rb
│ └── admin_orders_controller_spec.rb
├── support
│ ├── delayed_job.rb
│ ├── stub_gravatar_url.rb
│ └── javascript.rb
├── javascripts
│ ├── helpers
│ │ ├── html_sandbox_helpers.js.coffee
│ │ └── factory.js.coffee
│ ├── angular
│ │ ├── filters.js.coffee
│ │ └── services.js.coffee
│ ├── specs.js.coffee
│ ├── users
│ │ └── phones.js.coffee
│ └── support
│ │ └── jasmine.yml
├── features
│ ├── landing_page_spec.rb
│ ├── user_page_spec.rb
│ ├── fallback_url_spec.rb
│ ├── loc_spec.rb
│ ├── event_changes_spec.rb
│ ├── profile_spec.rb
│ ├── magic_encoding_spec.rb
│ ├── user_phones_spec.rb
│ ├── admin
│ │ └── order_fulfillments_spec.rb
│ ├── follower_page_spec.rb
│ ├── event_page_spec.rb
│ ├── event_summary_spec.rb
│ └── new_event_spec.rb
├── helpers
│ ├── event_order_helper_spec.rb
│ └── home_helper_spec.rb
├── views
│ └── events
│ │ └── show.html.slim_spec.rb
├── lib
│ └── core_ext
│ │ └── hash
│ │ └── angularjs_spec.rb
└── uploaders
│ └── photo_uploader_spec.rb
├── Procfile
├── config
├── locales
│ ├── cancan.zh-CN.yml
│ ├── cancan.en.yml
│ ├── devise_invitable.zh-CN.yml
│ ├── devise_invitable.en.yml
│ └── simple_form.en.yml
├── initializers
│ ├── alipay.rb
│ ├── load_lib_files.rb
│ ├── sms.rb
│ ├── 0_jruby_patches.rb
│ ├── mime_types.rb
│ ├── carrierwave.rb
│ ├── delayed_job_config.rb
│ ├── middlewares.rb
│ ├── backtrace_silencers.rb
│ ├── session_store.rb
│ ├── html_pipeline.rb
│ ├── wrap_parameters.rb
│ ├── inflections.rb
│ ├── setup_mail.rb
│ ├── secret_token.rb
│ ├── setup_locale.rb
│ └── ransack.rb
├── environment.rb
├── boot.rb
├── database.yml.example.sqlite3
├── database.yml.example.mysql2
├── database.yml.example.pg
└── schedule.rb
├── CHANGELOG.md
├── bin
├── rake
├── bundle
├── rails
├── delayed_job
├── magic_encoding_fix
└── magic_encoding_find
├── db
├── migrate
│ ├── 20121227135540_add_login_to_users.rb
│ ├── 20130226122259_add_invite_reason_to_users.rb
│ ├── 20130107053540_add_location_guide_to_events.rb
│ ├── 20130226085502_add_admin_flag_to_users.rb
│ ├── 20130812043238_add_canceled_at_to_event_orders.rb
│ ├── 20130814191134_remove_canceled_at_from_event_orders.rb
│ ├── 20130812033933_add_default_value_to_events.rb
│ ├── 20130125081842_add_joined_to_event_participants.rb
│ ├── 20130107102539_create_photos.rb
│ ├── 20130527075012_create_group_collaborators.rb
│ ├── 20150128225959_change_trade_no_to_event_order.rb
│ ├── 20130617104446_create_event_changes.rb
│ ├── 20130916145041_create_refund_batches.rb
│ ├── 20130403135737_create_fallback_urls.rb
│ ├── 20131031070813_create_event_order_fulfillments.rb
│ ├── 20130526080632_create_event_summaries.rb
│ ├── 20130825132343_create_sequences.rb
│ ├── 20121231000051_create_events.rb
│ ├── 20130328114637_participant_automatic_follow_event.rb
│ ├── 20131125011434_send_checkin_code_sms_for_free_event.rb
│ ├── 20130105124957_add_user_id_to_events.rb
│ ├── 20130106071318_create_profiles.rb
│ ├── 20130110074522_create_event_participants.rb
│ ├── 20130704141739_create_group_topics.rb
│ ├── 20130818175826_create_event_order_status_transitions.rb
│ ├── 20130808003107_create_event_order_items.rb
│ ├── 20130706130132_create_group_topic_replies.rb
│ ├── 20130921140156_add_unit_price_to_event_order_item.rb
│ ├── 20130916145506_create_event_order_refunds.rb
│ ├── 20130825132914_add_number_to_order.rb
│ ├── 20130807005443_create_event_tickets.rb
│ ├── 20130916141824_add_paid_amount_and_refunded_amount_to_order.rb
│ ├── 20130716040406_add_replies_count_to_group_topics.rb
│ ├── 20130807133515_create_event_orders.rb
│ ├── 20130702125422_move_phone_to_users.rb
│ ├── 20130817025041_create_event_order_participants.rb
│ ├── 20130811124504_create_event_order_shipping_addresses.rb
│ ├── 20130925031818_revert_ticket_quantity_of_request_refund_order.rb
│ ├── 20130124031857_create_groups.rb
│ ├── 20130319094925_acts_as_follower_migration.rb
│ ├── 20130104101554_add_confirmable_to_devise.rb
│ └── 20130225091732_devise_invitable_add_to_users.rb
└── seeds.rb
├── config.ru
├── doc
└── README_FOR_APP
├── custom_plan.rb
├── Rakefile
├── Capfile
├── 19wu.sublime-project
├── zeus.json
├── .travis.yml
├── .gitignore
└── LICENSE
/log/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 |
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/plugins/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/mixins/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_3.states.css.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_4.themes.css.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/modules/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/modules/_icons.css.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/application/403.html.slim:
--------------------------------------------------------------------------------
1 | = I18n.t("unauthorized.default")
2 |
--------------------------------------------------------------------------------
/app/models/fallback_url.rb:
--------------------------------------------------------------------------------
1 | class FallbackUrl < ActiveRecord::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/views/user_orders/alipay_done.html.slim:
--------------------------------------------------------------------------------
1 | p.text-success 谢谢,您的订单已经完成支付。
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/public/favicon.ico
--------------------------------------------------------------------------------
/app/views/order_refunds/submit.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.(@refund, :id, :amount, :reason)
2 |
--------------------------------------------------------------------------------
/spec/models/photo_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Photo do
4 | end
5 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec rails s -p $PORT
2 | worker: bundle exec bin/delayed_job run
3 |
--------------------------------------------------------------------------------
/config/locales/cancan.zh-CN.yml:
--------------------------------------------------------------------------------
1 | zh-CN:
2 | unauthorized:
3 | default: "你没有权限访问此页面。"
4 |
--------------------------------------------------------------------------------
/public/flash/clippy.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/public/flash/clippy.swf
--------------------------------------------------------------------------------
/app/assets/stylesheets/modules/_home.css.scss:
--------------------------------------------------------------------------------
1 | .home-nav {
2 | @extend .nav, .nav-tabs;
3 | }
4 |
--------------------------------------------------------------------------------
/app/controllers/mockup_controller.rb:
--------------------------------------------------------------------------------
1 | class MockupController < ApplicationController
2 | end
3 |
--------------------------------------------------------------------------------
/spec/mailers/user_mailer_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe UserMailer do
4 | end
5 |
--------------------------------------------------------------------------------
/spec/models/group_topic_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe GroupTopic do
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/admin/order_fulfillments/create.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.(@fulfillment, :id, :tracking_number)
2 |
--------------------------------------------------------------------------------
/spec/models/refund_batch_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe RefundBatch do
4 | end
5 |
--------------------------------------------------------------------------------
/app/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/favicon.ico
--------------------------------------------------------------------------------
/app/models/photo.rb:
--------------------------------------------------------------------------------
1 | class Photo < ActiveRecord::Base
2 | mount_uploader :image, PhotoUploader
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/images/event/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/event/map.png
--------------------------------------------------------------------------------
/app/views/home/index.html.slim:
--------------------------------------------------------------------------------
1 | = render 'menu', :current => :activities
2 |
3 | div = t('home.body.empty')
4 |
--------------------------------------------------------------------------------
/spec/factories/data/event/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/spec/factories/data/event/map.png
--------------------------------------------------------------------------------
/spec/models/group_collaborator_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe GroupCollaborator do
4 | end
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 变更记录
2 |
3 | https://github.com/19wu/19wu/pulls?direction=desc&page=1&sort=created&state=closed
4 |
--------------------------------------------------------------------------------
/app/assets/images/feature-slug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/feature-slug.png
--------------------------------------------------------------------------------
/app/assets/images/logo-wordmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/logo-wordmark.png
--------------------------------------------------------------------------------
/app/assets/images/sponsors/tui3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/sponsors/tui3.png
--------------------------------------------------------------------------------
/app/views/user_mailer/welcome_email.zh-CN.text.erb:
--------------------------------------------------------------------------------
1 | 欢迎加入19屋,
2 |
3 | 在使用中如果有什么问题,请联系
4 | [管理员](support@19wu.com)
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/spec/controllers/sessions_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe SessionsController do
4 | end
5 |
--------------------------------------------------------------------------------
/app/assets/images/avatar-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/avatar-default.png
--------------------------------------------------------------------------------
/app/assets/images/feature-follow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/feature-follow.png
--------------------------------------------------------------------------------
/app/assets/images/spotlight-free.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/spotlight-free.png
--------------------------------------------------------------------------------
/app/assets/images/spotlight-team.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/spotlight-team.png
--------------------------------------------------------------------------------
/app/assets/stylesheets/modules/_table.css.scss:
--------------------------------------------------------------------------------
1 | .table-operate {
2 | a {
3 | margin-right: 10px;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/app/views/sessions/create.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.(@user, :id, :name, :phone)
2 |
3 | json.token form_authenticity_token
4 |
--------------------------------------------------------------------------------
/config/locales/cancan.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | unauthorized:
3 | default: "You are not authorized to access this page."
4 |
--------------------------------------------------------------------------------
/spec/controllers/topics_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe TopicsController do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/data/event/map_big.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/spec/factories/data/event/map_big.png
--------------------------------------------------------------------------------
/app/assets/images/spotlight-devices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/spotlight-devices.png
--------------------------------------------------------------------------------
/app/views/registrations/create.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.(@user, :id, :name, :phone)
2 |
3 | json.token form_authenticity_token
4 |
--------------------------------------------------------------------------------
/spec/controllers/topic_reply_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe TopicReplyController 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 |
--------------------------------------------------------------------------------
/app/assets/images/feature-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/19wu/master/app/assets/images/feature-notification.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/models/group_collaborator.rb:
--------------------------------------------------------------------------------
1 | class GroupCollaborator < ActiveRecord::Base
2 | belongs_to :group
3 | belongs_to :user
4 | end
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/models/event_order_status_transition.rb:
--------------------------------------------------------------------------------
1 | class EventOrderStatusTransition < ActiveRecord::Base
2 | belongs_to :event_order
3 | end
4 |
--------------------------------------------------------------------------------
/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/helpers/home_helper.rb:
--------------------------------------------------------------------------------
1 | module HomeHelper
2 |
3 | def active?(current, menu)
4 | current == menu && :active
5 | end
6 |
7 | end
8 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/_menu.css.scss:
--------------------------------------------------------------------------------
1 | .menu .tooltip-inner {
2 | width: 156px;
3 | }
4 |
5 | .submenu {
6 | @extend .well;
7 | padding: 20px 0;
8 | }
9 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/assets/javascripts/angularjs/filters.js.coffee:
--------------------------------------------------------------------------------
1 | @app.filter 'money', () ->
2 | (input) ->
3 | if parseFloat(input) == 0
4 | '免费'
5 | else
6 | input
7 |
--------------------------------------------------------------------------------
/config/initializers/sms.rb:
--------------------------------------------------------------------------------
1 | Settings.sms.each do |service, account|
2 | ChinaSMS.use service.to_sym, username: account['username'], password: account['password']
3 | end
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/spec/javascripts/helpers/html_sandbox_helpers.js.coffee:
--------------------------------------------------------------------------------
1 | # beforeEach ->
2 | # @htmlSandbox = $("
").appendTo($("body"))
3 | #
4 | # afterEach ->
5 | # @htmlSandbox.remove()
6 | # @htmlSandbox = null
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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'
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/spec/features/user_page_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../spec_helper', __FILE__)
2 |
3 | feature 'user page' do
4 |
5 | given!(:bob) { create(:user, :login => '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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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 | "#{ChinaCity.get(province)}#{ChinaCity.get(city)}#{ChinaCity.get(district)} #{address} #{name} #{phone}"
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/helpers/event_ticket_helper.rb:
--------------------------------------------------------------------------------
1 | module EventTicketHelper
2 | def init_tickets(event)
3 | tickets = event.tickets.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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | ChinaSMS.delay.to phones, I18n.t('sms.event.change', content: content)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.06 }
10 | it { should_not eql 0.060000000000000005 }
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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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, require_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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | before_save do
10 | event.update_column :tickets_quantity, @tickets_quantity if @tickets_quantity.present?
11 | end
12 |
13 | after_destroy do
14 | event.update_column :tickets_quantity, nil if event.tickets.count.zero?
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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
19 | puts "#{number}, #{price}, #{fee}, #{refund}"
20 | end
21 |
22 | puts prices
23 | puts refunds
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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])
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/spec/features/profile_spec.rb:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | require File.expand_path('../../spec_helper', __FILE__)
3 |
4 | feature 'profile settings' do
5 | given(:expected_flash) {
6 | I18n.t('flash.profiles.updated')
7 | }
8 |
9 | background do
10 | sign_in
11 | end
12 |
13 | background do
14 | visit profile_path
15 | end
16 |
17 | scenario "I update my profile" do
18 | fill_in 'profile_name', with: '19wu'
19 | fill_in 'profile_website', with: 'http://19wu.com'
20 | fill_in 'profile_bio', with: '**Launch your event now**'
21 |
22 | click_button '保存资料'
23 |
24 | expect(page).to have_content(expected_flash)
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 < :patch
22 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.where(status: 'paid').joins(items: :ticket).where(event_tickets: {require_invoice: true}).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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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}"
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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);
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
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 |
--------------------------------------------------------------------------------
/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, require_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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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
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/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 |
--------------------------------------------------------------------------------
/spec/models/event_order_fulfillment_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe EventOrderFulfillment do
4 | let(:order) { create(:order_with_items, require_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 } # 订单号改为字符串格式
17 | end
18 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 | span.label = @event.ordered_users.with_phone.size
19 | | 人登记了手机号码,将收到短信通知。
20 | p.alert.alert-error ng-show="form.$invalid" ng-cloak="" 变更内容 不能超过 100 个字
21 | .form-actions
22 | = f.button :submit, class: 'btn-info', 'ng-disabled' => "form.$invalid"
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 | require_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.require_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
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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/assets/javascripts/datetime.js.coffee:
--------------------------------------------------------------------------------
1 | $(document).ready ->
2 | body = $("body")
3 | datepickerDefaults =
4 | autoclose: true
5 | language: I18n.locale
6 | format: I18n.date.formats.datepicker
7 | weekStart: parseInt(I18n.date.weekstart, 10)
8 |
9 | body.on "click", ".datepicker-trigger", ->
10 | el = $(this).closest(".date").find('.datepicker')
11 | datepicker = el.data("datepicker")
12 | unless datepicker?
13 | el.datepicker(datepickerDefaults)
14 | el.off 'focus'
15 |
16 | el.datepicker "show"
17 |
18 | body.on "click", ".timepicker-trigger", (e) ->
19 | $this = $(this)
20 | el = $this.closest(".time").find(".timepicker")
21 |
22 | # Init and show it the first time. After time picker is initialized, it
23 | # will register events handlers.
24 | timepicker = el.data("timepicker")
25 | unless timepicker?
26 | el.timepicker({minuteStep: 5, defaultTime: false})
27 | # Set default meridian, otherwise the picker shows undefined in the text field.
28 | timepicker = el.data("timepicker")
29 | timepicker.meridian = 'AM'
30 |
31 | setTimeout (-> $this.trigger('click')), 0
32 |
--------------------------------------------------------------------------------
/spec/controllers/admin_orders_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe AdminOrdersController do
4 | before { login_user user }
5 |
6 | describe "GET 'index'" do
7 | context 'user' do
8 | let(:user) { create(:user) }
9 | it "returns http failure" do
10 | get 'index'
11 | response.should_not be_success
12 | end
13 | end
14 | context 'admin' do
15 | let(:user) { create(:user, :admin) }
16 | it "returns http success" do
17 | get 'index'
18 | response.should be_success
19 | end
20 | end
21 | end
22 |
23 | describe "PATCH 'pay'" do
24 | let(:order) { create(:order_with_items) }
25 | context 'user' do
26 | let(:user) { create(:user) }
27 | it "returns http failure" do
28 | patch 'pay', id: order.id
29 | response.should_not be_success
30 | end
31 | end
32 | context 'admin' do
33 | let(:user) { create(:user, :admin) }
34 | it "returns http success" do
35 | patch 'pay', id: order.id
36 | response.should redirect_to(admin_orders_path(number: order.number))
37 | end
38 | end
39 | end
40 |
41 | end
42 |
--------------------------------------------------------------------------------
/spec/features/new_event_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | feature 'new event' do
4 | let(:user) { create :user }
5 | let(:submit) { I18n.t('helpers.submit.event.create') }
6 | let(:flash) { I18n.t('flash.events.created') }
7 | let(:attributes) { attributes_for(:event, :markdown) }
8 | before { sign_in }
9 |
10 | scenario 'I create the event with valid attributes' do
11 | visit new_event_path
12 |
13 | fill_in 'event_title', with: attributes[:title]
14 | fill_in 'event_slug', with: attributes[:slug]
15 | fill_in 'event_compound_start_time_attributes_date', with: attributes[:start_time].strftime("%Y-%m-%d")
16 | fill_in 'event_compound_start_time_attributes_time', with: attributes[:start_time].strftime("%H:%M:%S")
17 | fill_in 'event_compound_end_time_attributes_date', with: attributes[:end_time].strftime("%Y-%m-%d")
18 | fill_in 'event_compound_end_time_attributes_time', with: attributes[:end_time].strftime("%H:%M:%S")
19 | fill_in 'event_location', with: attributes[:location]
20 | fill_in 'event_content', with: attributes[:content]
21 |
22 | click_button submit
23 | expect(page).to have_content(flash)
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/models/event_ticket_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe EventTicket do
4 | let(:event) { create :event }
5 | describe '#tickets_quantity' do
6 | let(:ticket) { build(:event_ticket, event: Event.find(event.id), tickets_quantity: 400) }
7 | describe '#create' do
8 | subject { ticket.tap(&:save) }
9 | its(:tickets_quantity) { should eql 400 }
10 | end
11 |
12 | context 'exists' do
13 | before { ticket.save }
14 | subject { event.reload }
15 | describe '#update' do
16 | before { ticket.update_attribute :tickets_quantity, 500 }
17 | its(:tickets_quantity) { should eql 500 }
18 | end
19 |
20 | describe '#destroy' do
21 | context 'with other ticket' do
22 | before do
23 | create(:event_ticket, event: Event.find(event.id), name: 'other', tickets_quantity: 500)
24 | ticket.destroy
25 | end
26 | its(:tickets_quantity) { should eql 500 }
27 | end
28 | context 'without other ticket' do
29 | before { event.tickets.destroy_all }
30 | its(:tickets_quantity) { should eql nil }
31 | end
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/controllers/user_phones_controller.rb:
--------------------------------------------------------------------------------
1 | class UserPhonesController < ApplicationController
2 | before_filter :authenticate_user!
3 | layout 'settings'
4 |
5 | def edit
6 | @user = current_user
7 | end
8 |
9 | def update
10 | @user = current_user
11 | @user.phone = params[:phone]
12 | if @user.phone.present? && params[:code].to_i == cookies.signed[:code] && @user.save
13 | cookies.delete(:code)
14 | redirect_to user_path(@user), notice: I18n.t('flash.user_phones.updated')
15 | else
16 | render :edit
17 | end
18 | end
19 |
20 | def send_code
21 | ip_key = "sms:#{request.remote_ip}" # TODO: config to use memcached.
22 | send_times = Rails.cache.fetch(ip_key, expires_in: 24.hours){ 100 }
23 | if send_times > 0
24 | Rails.cache.decrement ip_key
25 | code = cookies.signed[:code] || rand(1000..9999)
26 | code = 1234 if Rails.env.test?
27 | Rails.logger.info "#{send_times}: #{I18n.t('sms.user.phone_code', code: code)}"
28 | ChinaSMS.to params[:phone], I18n.t('sms.user.phone_code', code: code)
29 | cookies.signed[:code] = { value: code, expires: 1.hour.from_now }
30 | end
31 | render nothing: true
32 | end
33 | end
34 |
--------------------------------------------------------------------------------