orange-more 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. data/README.markdown +17 -0
  2. data/lib/orange-more.rb +23 -0
  3. data/lib/orange-more/administration.rb +1 -0
  4. data/lib/orange-more/administration/assets/css/admin.css +286 -0
  5. data/lib/orange-more/administration/assets/css/blueprint-ie.css +35 -0
  6. data/lib/orange-more/administration/assets/css/blueprint-print.css +29 -0
  7. data/lib/orange-more/administration/assets/css/blueprint.css +257 -0
  8. data/lib/orange-more/administration/assets/js/jquery.js +152 -0
  9. data/lib/orange-more/administration/cartons/site.rb +7 -0
  10. data/lib/orange-more/administration/cartons/site_carton.rb +12 -0
  11. data/lib/orange-more/administration/cartons/user.rb +27 -0
  12. data/lib/orange-more/administration/middleware/access_control.rb +156 -0
  13. data/lib/orange-more/administration/middleware/site_load.rb +25 -0
  14. data/lib/orange-more/administration/plugin.rb +23 -0
  15. data/lib/orange-more/administration/resources/admin_resource.rb +27 -0
  16. data/lib/orange-more/administration/resources/site_resource.rb +10 -0
  17. data/lib/orange-more/administration/resources/user_resource.rb +40 -0
  18. data/lib/orange-more/administration/templates/admin.haml +25 -0
  19. data/lib/orange-more/administration/views/openid_login.haml +14 -0
  20. data/lib/orange-more/administration/views/users/create.haml +10 -0
  21. data/lib/orange-more/administration/views/users/edit.haml +15 -0
  22. data/lib/orange-more/adverts.rb +1 -0
  23. data/lib/orange-more/adverts/cartons/adverts_carton.rb +14 -0
  24. data/lib/orange-more/adverts/plugin.rb +13 -0
  25. data/lib/orange-more/adverts/resources/adverts_resource.rb +27 -0
  26. data/lib/orange-more/adverts/views/adverts/adverts.haml +2 -0
  27. data/lib/orange-more/analytics.rb +1 -0
  28. data/lib/orange-more/analytics/middleware/analytics.rb +30 -0
  29. data/lib/orange-more/analytics/plugin.rb +11 -0
  30. data/lib/orange-more/analytics/resources/analytics_resource.rb +42 -0
  31. data/lib/orange-more/assets.rb +1 -0
  32. data/lib/orange-more/assets/cartons/asset_carton.rb +27 -0
  33. data/lib/orange-more/assets/plugin.rb +13 -0
  34. data/lib/orange-more/assets/resources/asset_resource.rb +115 -0
  35. data/lib/orange-more/assets/views/assets/change.haml +1 -0
  36. data/lib/orange-more/assets/views/assets/create.haml +13 -0
  37. data/lib/orange-more/assets/views/assets/insert.haml +1 -0
  38. data/lib/orange-more/blog.rb +1 -0
  39. data/lib/orange-more/blog/cartons/blog.rb +8 -0
  40. data/lib/orange-more/blog/cartons/blog_post.rb +60 -0
  41. data/lib/orange-more/blog/plugin.rb +14 -0
  42. data/lib/orange-more/blog/resources/blog_post_resource.rb +54 -0
  43. data/lib/orange-more/blog/resources/blog_resource.rb +104 -0
  44. data/lib/orange-more/blog/views/blog/blog_archive_view.haml +6 -0
  45. data/lib/orange-more/blog/views/blog/blog_offset_list_view.haml +12 -0
  46. data/lib/orange-more/blog/views/blog/blog_post_view.haml +4 -0
  47. data/lib/orange-more/blog/views/blog/sitemap_row.haml +14 -0
  48. data/lib/orange-more/blog/views/blog_posts/edit.haml +8 -0
  49. data/lib/orange-more/blog/views/blog_posts/show.haml +4 -0
  50. data/lib/orange-more/cloud.rb +1 -0
  51. data/lib/orange-more/cloud/plugin.rb +10 -0
  52. data/lib/orange-more/cloud/resources/cloud_resource.rb +21 -0
  53. data/lib/orange-more/contactforms.rb +1 -0
  54. data/lib/orange-more/contactforms/assets/js/jquery.validate.pack.js +15 -0
  55. data/lib/orange-more/contactforms/assets/js/lib/jquery-1.4.2.js +6240 -0
  56. data/lib/orange-more/contactforms/assets/js/lib/jquery.form.js +660 -0
  57. data/lib/orange-more/contactforms/assets/js/lib/jquery.js +4376 -0
  58. data/lib/orange-more/contactforms/assets/js/lib/jquery.metadata.js +122 -0
  59. data/lib/orange-more/contactforms/cartons/contactforms_carton.rb +12 -0
  60. data/lib/orange-more/contactforms/plugin.rb +13 -0
  61. data/lib/orange-more/contactforms/resources/contactforms_resource.rb +56 -0
  62. data/lib/orange-more/contactforms/views/contactforms/contactform.haml +20 -0
  63. data/lib/orange-more/debugger.rb +1 -0
  64. data/lib/orange-more/debugger/assets/css/debug_bar.css +46 -0
  65. data/lib/orange-more/debugger/middleware/debugger.rb +50 -0
  66. data/lib/orange-more/debugger/plugin.rb +12 -0
  67. data/lib/orange-more/debugger/views/debug_bar.haml +87 -0
  68. data/lib/orange-more/disqus.rb +1 -0
  69. data/lib/orange-more/disqus/plugin.rb +11 -0
  70. data/lib/orange-more/disqus/resources/disqus_resource.rb +9 -0
  71. data/lib/orange-more/disqus/views/disqus/comment_thread.haml +7 -0
  72. data/lib/orange-more/donations.rb +1 -0
  73. data/lib/orange-more/donations/cartons/donation_carton.rb +9 -0
  74. data/lib/orange-more/donations/plugin.rb +12 -0
  75. data/lib/orange-more/donations/resources/donations_resource.rb +51 -0
  76. data/lib/orange-more/donations/views/donations/donate_form.haml +21 -0
  77. data/lib/orange-more/donations/views/donations/donate_thanks.haml +2 -0
  78. data/lib/orange-more/donations/views/donations/paypal_form.haml +13 -0
  79. data/lib/orange-more/events.rb +1 -0
  80. data/lib/orange-more/events/assets/js/events.js +55 -0
  81. data/lib/orange-more/events/cartons/orange_calendar.rb +8 -0
  82. data/lib/orange-more/events/cartons/orange_event.rb +60 -0
  83. data/lib/orange-more/events/plugin.rb +14 -0
  84. data/lib/orange-more/events/resources/calendar_resource.rb +33 -0
  85. data/lib/orange-more/events/resources/event_resource.rb +147 -0
  86. data/lib/orange-more/events/views/calendar/calendar.haml +8 -0
  87. data/lib/orange-more/events/views/events/create.haml +55 -0
  88. data/lib/orange-more/events/views/events/edit.haml +59 -0
  89. data/lib/orange-more/events/views/events/list.haml +13 -0
  90. data/lib/orange-more/events/views/events/show.haml +4 -0
  91. data/lib/orange-more/events/views/events/table_row.haml +17 -0
  92. data/lib/orange-more/members.rb +1 -0
  93. data/lib/orange-more/members/cartons/member_carton.rb +38 -0
  94. data/lib/orange-more/members/plugin.rb +13 -0
  95. data/lib/orange-more/members/resources/members_resource.rb +327 -0
  96. data/lib/orange-more/members/views/members/create.haml +12 -0
  97. data/lib/orange-more/members/views/members/edit.haml +15 -0
  98. data/lib/orange-more/members/views/members/live.show.haml +1 -0
  99. data/lib/orange-more/members/views/members/login.haml +17 -0
  100. data/lib/orange-more/members/views/members/logout.haml +1 -0
  101. data/lib/orange-more/members/views/members/profile.haml +50 -0
  102. data/lib/orange-more/members/views/members/register.haml +37 -0
  103. data/lib/orange-more/news.rb +1 -0
  104. data/lib/orange-more/news/cartons/news.rb +12 -0
  105. data/lib/orange-more/news/plugin.rb +13 -0
  106. data/lib/orange-more/news/resources/news_resource.rb +83 -0
  107. data/lib/orange-more/news/views/news/archive.haml +14 -0
  108. data/lib/orange-more/news/views/news/edit.haml +7 -0
  109. data/lib/orange-more/news/views/news/latest.haml +8 -0
  110. data/lib/orange-more/news/views/news/sitemap_row.haml +14 -0
  111. data/lib/orange-more/pages.rb +1 -0
  112. data/lib/orange-more/pages/cartons/page_carton.rb +14 -0
  113. data/lib/orange-more/pages/cartons/page_version_carton.rb +12 -0
  114. data/lib/orange-more/pages/plugin.rb +13 -0
  115. data/lib/orange-more/pages/resources/page_resource.rb +179 -0
  116. data/lib/orange-more/pages/views/pages/edit.haml +34 -0
  117. data/lib/orange-more/pages/views/pages/show.haml +6 -0
  118. data/lib/orange-more/sitemap.rb +1 -0
  119. data/lib/orange-more/sitemap/assets/img/sitemap_down.png +0 -0
  120. data/lib/orange-more/sitemap/assets/img/sitemap_down_d.png +0 -0
  121. data/lib/orange-more/sitemap/assets/img/sitemap_indent.png +0 -0
  122. data/lib/orange-more/sitemap/assets/img/sitemap_indent_d.png +0 -0
  123. data/lib/orange-more/sitemap/assets/img/sitemap_outdent.png +0 -0
  124. data/lib/orange-more/sitemap/assets/img/sitemap_outdent_d.png +0 -0
  125. data/lib/orange-more/sitemap/assets/img/sitemap_up.png +0 -0
  126. data/lib/orange-more/sitemap/assets/img/sitemap_up_d.png +0 -0
  127. data/lib/orange-more/sitemap/assets/js/sitemap.js +11 -0
  128. data/lib/orange-more/sitemap/cartons/route.rb +44 -0
  129. data/lib/orange-more/sitemap/middleware/flex_router.rb +25 -0
  130. data/lib/orange-more/sitemap/plugin.rb +17 -0
  131. data/lib/orange-more/sitemap/resources/sitemap_resource.rb +295 -0
  132. data/lib/orange-more/sitemap/views/default_resource/sitemap_row.haml +30 -0
  133. data/lib/orange-more/sitemap/views/sitemap/breadcrumb.haml +12 -0
  134. data/lib/orange-more/sitemap/views/sitemap/list.haml +4 -0
  135. data/lib/orange-more/sitemap/views/sitemap/one_level.haml +7 -0
  136. data/lib/orange-more/sitemap/views/sitemap/route_actions.haml +15 -0
  137. data/lib/orange-more/sitemap/views/sitemap/sitemap_links.haml +14 -0
  138. data/lib/orange-more/sitemap/views/sitemap/table_row.haml +30 -0
  139. data/lib/orange-more/sitemap/views/sitemap/two_level.haml +13 -0
  140. data/lib/orange-more/slices.rb +1 -0
  141. data/lib/orange-more/slices/middleware/radius_parser.rb +24 -0
  142. data/lib/orange-more/slices/plugin.rb +15 -0
  143. data/lib/orange-more/slices/resources/radius.rb +117 -0
  144. data/lib/orange-more/slices/resources/slices.rb +35 -0
  145. data/lib/orange-more/subsites.rb +1 -0
  146. data/lib/orange-more/subsites/cartons/subsite.rb +8 -0
  147. data/lib/orange-more/subsites/middleware/subsite_load.rb +29 -0
  148. data/lib/orange-more/subsites/plugin.rb +17 -0
  149. data/lib/orange-more/subsites/resources/subsite_resource.rb +54 -0
  150. data/lib/orange-more/subsites/views/subsites/index.haml +2 -0
  151. data/lib/orange-more/testimonials.rb +1 -0
  152. data/lib/orange-more/testimonials/cartons/testimonials_carton.rb +15 -0
  153. data/lib/orange-more/testimonials/plugin.rb +13 -0
  154. data/lib/orange-more/testimonials/resources/testimonials_resource.rb +42 -0
  155. data/lib/orange-more/testimonials/views/testimonials/testimonials.haml +4 -0
  156. data/spec/orange-core/application_spec.rb +183 -0
  157. data/spec/orange-core/carton_spec.rb +136 -0
  158. data/spec/orange-core/core_spec.rb +248 -0
  159. data/spec/orange-core/magick_spec.rb +96 -0
  160. data/spec/orange-core/middleware/base_spec.rb +38 -0
  161. data/spec/orange-core/middleware/globals_spec.rb +3 -0
  162. data/spec/orange-core/middleware/rerouter_spec.rb +3 -0
  163. data/spec/orange-core/middleware/restful_router_spec.rb +3 -0
  164. data/spec/orange-core/middleware/route_context_spec.rb +3 -0
  165. data/spec/orange-core/middleware/route_site_spec.rb +3 -0
  166. data/spec/orange-core/middleware/show_exceptions_spec.rb +3 -0
  167. data/spec/orange-core/middleware/static_file_spec.rb +3 -0
  168. data/spec/orange-core/middleware/static_spec.rb +3 -0
  169. data/spec/orange-core/mock/mock_app.rb +16 -0
  170. data/spec/orange-core/mock/mock_carton.rb +43 -0
  171. data/spec/orange-core/mock/mock_core.rb +2 -0
  172. data/spec/orange-core/mock/mock_middleware.rb +25 -0
  173. data/spec/orange-core/mock/mock_mixins.rb +19 -0
  174. data/spec/orange-core/mock/mock_model_resource.rb +47 -0
  175. data/spec/orange-core/mock/mock_pulp.rb +24 -0
  176. data/spec/orange-core/mock/mock_resource.rb +26 -0
  177. data/spec/orange-core/mock/mock_router.rb +10 -0
  178. data/spec/orange-core/orange_spec.rb +19 -0
  179. data/spec/orange-core/packet_spec.rb +203 -0
  180. data/spec/orange-core/resource_spec.rb +96 -0
  181. data/spec/orange-core/resources/mapper_spec.rb +5 -0
  182. data/spec/orange-core/resources/model_resource_spec.rb +246 -0
  183. data/spec/orange-core/resources/parser_spec.rb +5 -0
  184. data/spec/orange-core/resources/routable_resource_spec.rb +5 -0
  185. data/spec/orange-core/spec_helper.rb +53 -0
  186. data/spec/orange-core/stack_spec.rb +232 -0
  187. data/spec/stats.rb +182 -0
  188. metadata +312 -0
@@ -0,0 +1,8 @@
1
+ -# Given a list of events...
2
+ - for event in list
3
+ .event
4
+ %h2 #{event.name}
5
+ .description
6
+ = event.blurb
7
+
8
+ %a{:href => route_to(:events, event.id)} Learn More
@@ -0,0 +1,55 @@
1
+ - packet.add_js('events.js', :module => '_events_')
2
+ %form{:action => "#{packet.route_to(model_name, 'new')}", :method => 'post'}
3
+ - for prop in props.reject{|v| v[:name].to_s =~ /location_/ || v[:name].to_s =~ /eventbrite/}
4
+ %p= view_attribute(prop, model_name, :label => true)
5
+ %fieldset
6
+ %legend Venue
7
+ %p
8
+ %select{:id => "#{model_name}-venue", :name => "#{model_name}[orange_venue_id]"}
9
+ %option(disabled) Choose a venue
10
+ - for venue in eventbrite_venues
11
+ %option{:value => venue.id}= venue.name
12
+ %option{:value => "new"} New...
13
+ %a.show-venue-details{:href=>"#"} (Details)
14
+ .new-venue
15
+ :javascript
16
+ var venues = #{venues_json};
17
+ - for prop in props.select{|v| v[:name].to_s =~ /location_/}
18
+ %p= view_attribute(prop, model_name, :label => true)
19
+ %fieldset
20
+ %legend EventBrite
21
+ %p
22
+ %label Link to EventBrite?
23
+ %input(type="hidden" value="0" name="events[link_to_eventbrite]")
24
+ %input(type="checkbox" value="1" name="events[link_to_eventbrite]")
25
+ %p.eventbrite-link(style="display:none;")
26
+ :javascript
27
+ var events = #{events_json};
28
+ %label Which EventBrite Event?
29
+ %br
30
+ %select{:id => "#{model_name}-eventbrite-id", :name => "#{model_name}[eventbrite_id]"}
31
+ %optgroup(label="Create a new event")
32
+ %option{:value => "new"} New...
33
+ %optgroup(label="Existing event")
34
+ - for event in eventbrite_events
35
+ %option{:value => event.id}= event.title
36
+ %p
37
+ %label{:for => "#{model_name}-calendar"} Calendar
38
+ %br
39
+ %select{:id => "#{model_name}-calendar", :name => "#{model_name}[orange_calendar_id]"}
40
+ - for calendar in calendars
41
+ %option{:value => calendar.id}= calendar.name
42
+ %p
43
+ %label{:for => "#{model_name}-starts"} Starts
44
+ %br
45
+ %input{:type => 'text', :id => "#{model_name}-starts", :class => 'date', :name => "#{model_name}[starts_date]", :size => 10, :value => Time.now.strftime("%m/%d/%Y") }
46
+ at
47
+ %input{:type => 'text', :name => "#{model_name}[starts_time]", :size => 8, :value => Time.now.strftime("%I:%M %p") }
48
+ %p
49
+ %label{:for => "#{model_name}-ends"} Ends
50
+ %br
51
+ %input{:type => 'text', :id => "#{model_name}-ends", :size => 10, :class => 'date', :name => "#{model_name}[ends_date]", :value => (Time.now + 60*60).strftime("%m/%d/%Y") }
52
+ at
53
+ %input{:type => 'text', :name => "#{model_name}[ends_time]", :size => 8, :value => (Time.now + 60*60).strftime("%I:%M %p") }
54
+ %input{:type => 'submit', :value => 'Save New Event'}
55
+
@@ -0,0 +1,59 @@
1
+ -if model
2
+ - my_event = eventbrite_events.select{|e| e.id == model.eventbrite_id.to_i }.first || nil
3
+ - packet.add_js('events.js', :module => '_events_')
4
+ %form{:action => "#{packet.route_to(model_name, model[:id], 'save')}", :method => 'post'}
5
+ - for prop in props.reject{|v| v[:name].to_s =~ /location_/ || v[:name].to_s =~ /eventbrite/}
6
+ %p= view_attribute(prop, model_name, :label => true, :value => model.attribute_get(prop[:name]))
7
+ %fieldset
8
+ %legend Venue
9
+ %p
10
+ %select{:id => "#{model_name}-venue", :name => "#{model_name}[orange_venue_id]"}
11
+ %option(disabled) Choose a venue
12
+ - for venue in eventbrite_venues
13
+ %option{:value => venue.id, :selected => ((my_event && (venue.id == my_event.venue.id)) || venue.name == model.location_name)}= venue.name
14
+ %option{:value => "new"} New...
15
+ %a.show-venue-details{:href=>"#"} (Details)
16
+ .new-venue
17
+ :javascript
18
+ var venues = #{venues_json};
19
+ - for prop in props.select{|v| v[:name].to_s =~ /location_/}
20
+ %p= view_attribute(prop, model_name, :label => true, :value => model.attribute_get(prop[:name]))
21
+ %fieldset
22
+ %legend EventBrite
23
+ %p
24
+ %label Link to EventBrite?
25
+ %input(type="hidden" value="0" name="events[link_to_eventbrite]")
26
+ %input{:type=>"checkbox", :value=>"1", :name=>"events[link_to_eventbrite]", :checked=>(model.link_to_eventbrite ? true : false)}
27
+ %p.eventbrite-link{:style => (model.link_to_eventbrite ? "" : "display:none;")}
28
+ :javascript
29
+ var events = #{events_json};
30
+ %label Which EventBrite Event?
31
+ %br
32
+ %select{:id => "#{model_name}-eventbrite-id", :name => "#{model_name}[eventbrite_id]"}
33
+ %optgroup(label="Create a new event")
34
+ %option{:value => "new"} New...
35
+ %optgroup(label="Existing event")
36
+ - for event in eventbrite_events
37
+ %option{:value => event.id, :selected => (my_event && (my_event.id == event.id))}= event.title
38
+ %p
39
+ %label{:for => "#{model_name}-calendar"} Calendar
40
+ %br
41
+ %select{:id => "#{model_name}-calendar", :name => "#{model_name}[orange_calendar_id]"}
42
+ - for calendar in calendars
43
+ %option{:value => calendar.id, :selected => (model.calendar.id == calendar.id)}= calendar.name
44
+ %p
45
+ %label{:for => "#{model_name}-starts"} Starts
46
+ %br
47
+ %input{:type => 'text', :id => "#{model_name}-starts", :class => 'date', :name => "#{model_name}[starts_date]", :size => 10, :value => model.starts_date }
48
+ at
49
+ %input{:type => 'text', :name => "#{model_name}[starts_time]", :size => 8, :value => model.starts_time }
50
+ %p
51
+ %label{:for => "#{model_name}-ends"} Ends
52
+ %br
53
+ %input{:type => 'text', :id => "#{model_name}-ends", :size => 10, :class => 'date', :name => "#{model_name}[ends_date]", :value => model.ends_date }
54
+ at
55
+ %input{:type => 'text', :name => "#{model_name}[ends_time]", :size => 8, :value => model.ends_time }
56
+ %input{:type => 'submit', :value => 'Save Event'}
57
+ = form_link('Synchronize Eventbrite Attendees to Mailchimp', route_to(model_name, model.id, 'synchronize_attendees'))
58
+ - else
59
+ %p Couldn't find the event.
@@ -0,0 +1,13 @@
1
+ %table
2
+ %thead
3
+ %tr
4
+ %th Name
5
+ %th Description
6
+ %th Location
7
+ %th Calendar
8
+ %th EventBrite Link
9
+ %th
10
+ %tbody
11
+ - for obj in list
12
+ = view_table_row(model_name, :model => obj)
13
+ %a{:href => route_to(model_name, 'create')} New
@@ -0,0 +1,4 @@
1
+ -# Given a list of events...
2
+ .event
3
+ %h2 #{model.name}
4
+ .description= model.description
@@ -0,0 +1,17 @@
1
+ - if model
2
+ %tr
3
+ %td= model.name
4
+ %td= model.description
5
+ %td= model.location_name
6
+ %td= model.calendar.blank? ? "None" : model.calendar.name
7
+ %td
8
+ - if model.link_to_eventbrite
9
+ - if model.eventbrite_id && model.eventbrite_id != "new"
10
+ %a{:href => "http://www.eventbrite.com/myevent?eid="+model.eventbrite_id}= model.name
11
+ - else
12
+ N/A
13
+ - else
14
+ N/A
15
+ %td.actions
16
+ = form_link('Delete', route_to(model_name, model.id, 'delete'), 'Are you sure you want to delete this?', {:method => 'delete'})
17
+ %a{:href => route_to(model_name, model.id, 'edit')} Edit
@@ -0,0 +1 @@
1
+ require File.join('orange-more', 'members', 'plugin')
@@ -0,0 +1,38 @@
1
+ require 'md5'
2
+
3
+ class OrangeMember < Orange::Carton
4
+ id
5
+ front do
6
+ text :first_name
7
+ text :last_name
8
+ text :email
9
+ text :title
10
+ text :organization
11
+ end
12
+ text :hashed_password
13
+ text :salt
14
+ text :reset_token
15
+ property :reset_on, DateTime
16
+
17
+ def password=(val)
18
+ attribute_set(:hashed_password, Digest::MD5.hexdigest("#{salt}orange-is-awesome#{val}"))
19
+ end
20
+
21
+ def salt
22
+ my_salt = attribute_get(:salt)
23
+ unless(my_salt)
24
+ my_salt = Digest::MD5.hexdigest(Time.now.iso8601)
25
+ attribute_set(:salt, my_salt)
26
+ end
27
+ my_salt
28
+ end
29
+
30
+ def reset!
31
+ token = Digest::MD5.hexdigest(Time.now.iso8601)
32
+ # Invalidate password
33
+ attribute_set(:hashed_password, Digest::MD5.hexdigest("#{salt}#{token}"))
34
+ attribute_set(:reset_on, DateTime)
35
+ attribute_set(:reset_token, token)
36
+ end
37
+
38
+ end
@@ -0,0 +1,13 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), 'cartons', '*.rb')).each {|f| require f }
2
+ Dir.glob(File.join(File.dirname(__FILE__), 'resources', '*.rb')).each {|f| require f }
3
+
4
+ module Orange::Plugins
5
+ class Members < Base
6
+ views_dir File.join(File.dirname(__FILE__), 'views')
7
+
8
+ resource Orange::MembersResource.new
9
+ end
10
+ end
11
+
12
+ Orange.plugin(Orange::Plugins::Members.new)
13
+
@@ -0,0 +1,327 @@
1
+ require 'spreedly'
2
+ require 'hominid'
3
+
4
+ module Orange
5
+ class MembersResource < Orange::ModelResource
6
+ use OrangeMember
7
+ call_me :members
8
+ def stack_init
9
+ options[:spreedly_key] = orange.options['spreedly_key'] || false
10
+ options[:spreedly_plan] = orange.options['spreedly_plan'] || false
11
+ options[:spreedly_site] = orange.options['spreedly_site'] || false
12
+ options[:mailchimp_key] = orange.options["mailchimp_key"] || false
13
+ options[:mailchimp_list] = orange.options["mailchimp_list"] || false
14
+ options[:mailchimp_interests] = orange.options["mailchimp_interests"] || false
15
+ options[:mailchimp_merge_fields] = orange.options["mailchimp_merge_fields"] || {}
16
+ Spreedly.configure(options[:spreedly_site], options[:spreedly_key]) if options[:spreedly_key]
17
+
18
+ orange[:admin, true].add_link("Settings", :resource => @my_orange_name, :text => 'Members')
19
+ orange[:radius, true].define_tag "if_member" do |tag|
20
+ packet = tag.locals.packet
21
+ if packet.session["member"]
22
+ tag.expand
23
+ else
24
+ if(tag.attr["else_view"])
25
+ orange[:members].do_view(packet, tag.attr["else_view"].to_sym)
26
+ else
27
+ ""
28
+ end
29
+ end
30
+ end
31
+ orange[:radius, true].define_tag "if_paid_member" do |tag|
32
+ packet = tag.locals.packet
33
+ if packet.session["member"] && orange[:members].paid?(packet, packet.session["member"])
34
+ tag.expand
35
+ else
36
+ if(tag.attr["else_view"])
37
+ orange[:members].do_view(packet, tag.attr["else_view"].to_sym)
38
+ else
39
+ ""
40
+ end
41
+ end
42
+ end
43
+ orange[:radius, true].define_tag "unless_member" do |tag|
44
+ packet = tag.locals.packet
45
+ unless packet.session["member"]
46
+ tag.expand
47
+ else
48
+ ""
49
+ end
50
+ end
51
+ end
52
+
53
+ def hominid
54
+ @hominid ||= Hominid::Base.new({:api_key => options[:mailchimp_key]}) if options[:mailchimp_key]
55
+ end
56
+
57
+ def hominid_list
58
+ @hominid_list ||= hominid.find_list_by_id(options[:mailchimp_list]) if options[:mailchimp_list] && hominid
59
+ end
60
+
61
+
62
+
63
+ def paid?(packet, member)
64
+ return true unless options[:spreedly_key]
65
+ unless member.is_a? model_class
66
+ member = model_class.get(member)
67
+ end
68
+ subscriber = Spreedly::Subscriber.find(member.id)
69
+ subscriber ? subscriber.active? : false
70
+ end
71
+
72
+ def subscription_url(packet, member)
73
+ return "" unless options[:spreedly_key]
74
+ unless member.is_a? model_class
75
+ member = model_class.get(member)
76
+ end
77
+ Spreedly.subscribe_url(member.id, options[:spreedly_plan])
78
+ end
79
+
80
+ def list_groups(packet)
81
+ hominid.call("listInterestGroupings", options[:mailchimp_list]).select{|g| options[:mailchimp_interests].include? g["name"]}
82
+ end
83
+
84
+ def list_groups_for_email(packet, email)
85
+ chimp_info = hominid.member_info(options[:mailchimp_list], email)
86
+ end
87
+ #
88
+ # def add_to_mailchimp(packet, member_params)
89
+ # email = member_params[:email]
90
+ # fname = member_params[:first_name]
91
+ # lname = member_params[:last_name]
92
+ # interests = member_params[:groups].map{|key, val|
93
+ # { "name" => key, "groups" => groups.map{|str| str.gsub(/,/, "\,")}.join(",") }
94
+ # }
95
+ # hominid.subscribe(options[:mailchimp_list], email, {:FNAME => fname, :LNAME => lname, :INTERESTS => interests}, {:update_existing => true})
96
+ # end
97
+
98
+ def synchronize_with_mailchimp(packet, member_params)
99
+ email = member_params["email"]
100
+ old_email = member_params["old_email"] || email
101
+ fname = member_params["first_name"] || ''
102
+ lname = member_params["last_name"] || ''
103
+ others = {}
104
+ for key, val in options[:mailchimp_merge_fields]
105
+ others[val.upcase.to_sym] = member_params[key] || ''
106
+ end
107
+ interests = member_params["groups"].blank? ? [] : member_params["groups"].map{|key, val|
108
+ { "name" => key, "groups" => val.reject{|str| str.blank? }.map{|str| str.gsub(/,/, '\,')}.join(",") }
109
+ }
110
+ hominid.subscribe(options[:mailchimp_list], old_email, {:FNAME => fname, :LNAME => lname, :GROUPINGS => interests}.merge(others), {:update_existing => true})
111
+ end
112
+
113
+ def synchronize_from_mailchimp(packet, member)
114
+ email = member.email
115
+ member_info = mailchimp_member_info(packet, member)
116
+ for key, val in options[:mailchimp_merge_fields]
117
+ member.attribute_set(key, member_info["merges"][val])
118
+ end
119
+ member.save
120
+ end
121
+
122
+ def batch_update_interest_mailchimp(packet, emails, grouping_name, groups)
123
+ interests = [{ "name" => grouping_name, "groups" => groups }]
124
+ emails = emails.map{|e| {:EMAIL => e, :GROUPINGS => interests}}
125
+ hominid.subscribe_many(options[:mailchimp_list], emails, {:double_opt_in => true, :update_existing => true, :replace_interests => false})
126
+ end
127
+
128
+ def add_attendee_group(packet, grouping_name, name, limit = 15)
129
+ list = options[:mailchimp_list]
130
+ my_groups = hominid.call("listInterestGroupings", list)
131
+ grouping = my_groups.select{|a| a["name"] == grouping_name}.first
132
+ if grouping
133
+ # Return true if already in the list.
134
+ return true if grouping["groups"].select{|a| a["name"] == name }.size > 0
135
+
136
+ # Keep group size manageable
137
+ if grouping["groups"].size > limit
138
+ remove = grouping["groups"].shift
139
+ hominid.call("listInterestGroupDel", list, remove["name"], grouping["id"])
140
+ end
141
+ hominid.call("listInterestGroupAdd", list, name, grouping["id"])
142
+ else
143
+ hominid.call("listInterestGroupingAdd", list, grouping_name, "checkboxes", [name] )
144
+ end
145
+ end
146
+
147
+ def unsubscribe_from_mailchimp(packet, email)
148
+ hominid.unsubscribe(options[:mailchimp_list], email)
149
+ end
150
+
151
+ def mailchimp_member_info(packet, member)
152
+ # Can give member id, hydrate to member before continuing.
153
+ unless member.is_a? model_class
154
+ member = model_class.get(member)
155
+ end
156
+ return [] unless member && options[:mailchimp_list]
157
+ list = options[:mailchimp_list]
158
+ begin
159
+ mailchimp_info = hominid.member_info(list, member.email)
160
+ rescue Hominid::ListError => e
161
+ mailchimp_info = {}
162
+ end
163
+ mailchimp_info
164
+ end
165
+
166
+ def login(packet, opts = {})
167
+ if packet.request.post?
168
+ params = packet.request.params["members"]
169
+ login = params["login_email"]
170
+ password = params["login_password"]
171
+ member = model_class.first({:email => login})
172
+ tester = model_class.new({:salt => (member.salt || "")})
173
+ tester.password = password
174
+ if member && tester.hashed_password == member.hashed_password
175
+ packet.session["member"] = member.id
176
+ packet.reroute(@my_orange_name, :orange, :profile)
177
+ else
178
+ packet.flash("error", "Invalid email or password")
179
+ do_view(packet, :login, opts)
180
+ end
181
+ else
182
+ packet.reroute(@my_orange_name, :orange, :profile) if packet.session["member"]
183
+ do_view(packet, :login, opts)
184
+ end
185
+ end
186
+
187
+ def register(packet, opts = {})
188
+ if packet.request.post?
189
+ params = packet.request.params["members"]
190
+ unless params["name"].blank?
191
+ # Problem, stay on registration
192
+ packet.flash("error", "It looks like you might be a spam robot. Make sure you didn't fill out an extra field by mistake.")
193
+ return do_view(packet, :register, opts.merge(:list_groups => list_groups(packet)))
194
+ end
195
+ params.delete("name")
196
+ member = model_class.first(:email => params["email"])
197
+ if member
198
+ # Existing member... do they already have password?
199
+ if member.hashed_password.blank?
200
+ # A member who is part of the mailing list, but hasn't
201
+ # set a password
202
+ email = params.delete("email")
203
+ mailing_list = params.delete("email_subscribe")
204
+ groups = params.delete("groups")
205
+ save(packet, {:resource_id => member.id, :params => params, :no_reroute => true})
206
+ member = model_class.first(:email => email)
207
+ if member.hashed_password.blank?
208
+ # Problem, stay on registration
209
+ do_view(packet, :register, opts.merge(:list_groups => list_groups(packet)))
210
+ else
211
+ packet.flash("info", "Looks like this email address was already on our mailing list. Please take the time to correct our details about you.")
212
+ packet.session["member"] = member.id
213
+ # Synchronize with the mailchimp account.
214
+ params.merge!("groups" => groups)
215
+ synchronize_with_mailchimp(packet, params.merge("groups" => groups, "email" => email)) if mailing_list
216
+ packet.reroute(@my_orange_name, :orange, :profile)
217
+ end
218
+ else
219
+ # A person trying to sign up with an email that already
220
+ # has a password
221
+ packet.flash("error", "Looks like this email address already has an account. Try logging in instead.")
222
+ packet.reroute(@my_orange_name, :orange, :login)
223
+ end
224
+ else
225
+ # New member time!
226
+ mailing_list = params.delete("email_subscribe")
227
+ groups = params.delete("groups")
228
+ new(packet, {:no_reroute => true, :params => params})
229
+ # success ?
230
+ member = model_class.first(:email => params["email"])
231
+ if member && !(mailchimp_member_info(packet, member).blank?)
232
+ packet.session["member"] = member.id
233
+ synchronize_from_mailchimp(packet, member)
234
+ synchronize_with_mailchimp(packet, params.merge("groups" => groups)) if mailing_list
235
+ packet.flash("info", "Looks like this email address was already on our mailing list. Please take the time to correct our details about you.")
236
+ packet.reroute(@my_orange_name, :orange, :profile)
237
+ elsif member
238
+ packet.session["member"] = member.id
239
+ params.merge!("groups" => groups)
240
+ synchronize_with_mailchimp(packet, params.merge("groups" => groups)) if mailing_list
241
+ packet.flash("info", "You've successfully created an account. Please take the time to fill in more details about yourself.")
242
+ packet.reroute(@my_orange_name, :orange, :profile)
243
+ else
244
+ # Problem, stay on registration
245
+ do_view(packet, :register, opts.merge(:list_groups => list_groups(packet)))
246
+ end
247
+ end # End pre-existing member if
248
+ else
249
+ do_view(packet, :register, opts.merge(:list_groups => list_groups(packet)))
250
+ end
251
+ end
252
+
253
+ def logout(packet, opts = {})
254
+ packet.session["member"] = nil
255
+ do_view(packet, :logout, opts)
256
+ end
257
+
258
+ def profile(packet, opts = {})
259
+ no_reroute = opts.delete(:no_reroute)
260
+ # login check
261
+ unless packet.session["member"]
262
+ packet.flash("error", "You must log in to view this page")
263
+ packet.reroute(@my_orange_name, :orange, :login)
264
+ end
265
+
266
+ if packet.request.post?
267
+ params = packet.request.params["members"]
268
+ if params["current_password"].blank?
269
+ # Can't change password without confirming current
270
+ params.delete("password")
271
+ params.delete("repeat_password")
272
+ password_change = false
273
+ else
274
+ password_change = true
275
+ end
276
+ member = model_class.get(packet.session["member"])
277
+ check = model_class.new(:password => params.delete("current_password"))
278
+ if !password_change || member.hashed_password == check.hashed_password
279
+
280
+ mailing_list = params.delete("email_subscribe")
281
+ groups = params.delete("groups")
282
+ old_email = member.email
283
+ save(packet, {:resource_id => packet.session["member"], :no_reroute => true, :params => params})
284
+ if mailing_list == "1"
285
+ synchronize_with_mailchimp(packet, params.merge("groups" => groups, "old_email" => old_email))
286
+ else
287
+ unsubscribe_from_mailchimp(packet, params["email"])
288
+ end
289
+ else
290
+ packet.flash("error", "Your old password was not input correctly")
291
+ end
292
+ end
293
+ member = model_class.get(packet.session["member"])
294
+ opts[:model] = member
295
+ do_view(packet, :profile, opts.merge(:list_groups => list_groups(packet)))
296
+ end
297
+
298
+
299
+ def beforeNew(packet, params)
300
+ unless params["password"] == params["repeat_password"]
301
+ packet.flash("error", "New password does not match repeated password")
302
+ return false
303
+ end
304
+ unless params["password"].blank? || params["password"].size >= 6
305
+ packet.flash("error", "Password should be over 6 characters")
306
+ return false
307
+ end
308
+ params.delete("password") if params["password"].blank?
309
+ params.delete("repeat_password")
310
+ true
311
+ end
312
+
313
+ def beforeSave(packet, m, params)
314
+ unless params["password"] == params["repeat_password"]
315
+ packet.flash("error", "New password does not match repeated password")
316
+ return false
317
+ end
318
+ unless params["password"].blank? || params["password"].size >= 6
319
+ packet.flash("error", "Password should be over 6 characters")
320
+ return false
321
+ end
322
+ params.delete("password") if params["password"].blank?
323
+ params.delete("repeat_password")
324
+ true
325
+ end
326
+ end
327
+ end