Merge pull request #107 from houdiniproject/federated_p2p_campaigns
Federated P2P campaigns
This commit is contained in:
commit
4f27593311
51 changed files with 1010 additions and 318 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -7,6 +7,7 @@ client/js/nonprofits/donate/plugins-enabled
|
|||
|
||||
|
||||
.DS_Store
|
||||
/postgres-data
|
||||
|
||||
# Ignore bundler config
|
||||
/.bundle
|
||||
|
@ -62,4 +63,4 @@ javascripts/api
|
|||
!public/css/donate-button.css
|
||||
!public/css/donate-button.v2.css
|
||||
!public/svgs
|
||||
!public/svgs/*
|
||||
!public/svgs/*
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -1,6 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.3.6'
|
||||
ruby '~> 2.3.6'
|
||||
gem 'rake'
|
||||
gem 'rails', '3.2.22.5'
|
||||
gem 'rails_12factor'
|
||||
|
@ -27,7 +27,7 @@ gem 'hamster'
|
|||
|
||||
gem 'aws-ses'
|
||||
gem 'aws-sdk'
|
||||
|
||||
|
||||
# for blocking ip addressses
|
||||
gem 'rack-attack'
|
||||
|
||||
|
|
9
app/assets/stylesheets/campaigns/common.css.scss
Normal file
9
app/assets/stylesheets/campaigns/common.css.scss
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later */
|
||||
|
||||
body .campaign-banner {
|
||||
margin: auto;
|
||||
width: 980px;
|
||||
max-height: 120px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
@import 'components/simple_tabs';
|
||||
@import 'pikaday';
|
||||
@import 'common/image_uploader';
|
||||
@import '../common';
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background: $fog;
|
||||
}
|
||||
|
||||
|
||||
main {
|
||||
display: block;
|
||||
padding: 60px 0 50px 0;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
@import 'components/draggable';
|
||||
@import 'gift_levels';
|
||||
@import '../../nonprofits/donation_form/form';
|
||||
@import '../common';
|
||||
|
||||
.button--gift {
|
||||
font-size: 17px;
|
||||
|
@ -103,6 +104,43 @@
|
|||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.campaigner-profile {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.campaigner-profile > figure {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: auto 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.campaigner-profile > figure > .avatar {
|
||||
flex-basis: 20%;
|
||||
min-width: 100px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.avatar > img {
|
||||
clip-path: circle(50% at center);
|
||||
}
|
||||
|
||||
.campaigner-profile > figure > figcaption {
|
||||
flex-basis: 40%;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.campaigner-profile .reason {
|
||||
flex-basis: 60%;
|
||||
}
|
||||
|
||||
.campaigner-profile .pastelBox-body {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
.box,
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
width: 192px;
|
||||
}
|
||||
|
||||
.image-upload--banner {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.image-upload span {
|
||||
margin-top: 30px;
|
||||
padding: 5px;
|
||||
|
@ -28,6 +33,11 @@
|
|||
@include basicShadow;
|
||||
}
|
||||
|
||||
.image-upload--banner span {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.image-upload--large span {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ class CampaignsController < ApplicationController
|
|||
helper_method :current_campaign_editor?
|
||||
before_filter :authenticate_confirmed_user!, only: [:create, :name_and_id, :duplicate]
|
||||
before_filter :authenticate_campaign_editor!, only: [:update, :soft_delete]
|
||||
before_filter :authenticate_nonprofit_user!, only: [:create_via_template]
|
||||
before_filter :check_nonprofit_status, only: [:index, :show]
|
||||
|
||||
def index
|
||||
|
@ -36,23 +37,34 @@ class CampaignsController < ApplicationController
|
|||
@nonprofit = current_nonprofit
|
||||
@url = Format::Url.concat(root_url, @campaign.url)
|
||||
|
||||
@campaign_background_image = FetchBackgroundImage.with_model(@campaign)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
if @campaign.child_campaign?
|
||||
@parent_campaign = @campaign.parent_campaign
|
||||
@peer_to_peer_campaign_param = @parent_campaign.id
|
||||
else
|
||||
@peer_to_peer_campaign_param = @campaign.id
|
||||
end
|
||||
|
||||
@campaign_background_image = FetchBackgroundImage.with_model(@campaign)
|
||||
end
|
||||
|
||||
def activities
|
||||
render json: QueryDonations.for_campaign_activities(params[:id])
|
||||
@campaign = current_campaign
|
||||
render json: QueryDonations.for_campaign_activities(@campaign.id)
|
||||
end
|
||||
|
||||
def create
|
||||
Time.use_zone(current_nonprofit.timezone || 'UTC') do
|
||||
params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present?
|
||||
end
|
||||
campaign = current_nonprofit.campaigns.create params[:campaign]
|
||||
json_saved campaign, 'Campaign created! Well done.'
|
||||
|
||||
if !params[:campaign][:parent_campaign_id]
|
||||
campaign = current_nonprofit.campaigns.create params[:campaign]
|
||||
json_saved campaign, 'Campaign created! Well done.'
|
||||
else
|
||||
profile_id = params[:campaign][:profile_id]
|
||||
Profile.find(profile_id).update_attributes params[:profile]
|
||||
render json: CreatePeerToPeerCampaign.create(params[:campaign], profile_id)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -63,7 +75,6 @@ class CampaignsController < ApplicationController
|
|||
json_saved current_campaign, 'Successfully updated!'
|
||||
end
|
||||
|
||||
|
||||
# post 'nonprofits/:np_id/campaigns/:campaign_id/duplicate'
|
||||
def duplicate
|
||||
|
||||
|
@ -98,7 +109,22 @@ class CampaignsController < ApplicationController
|
|||
|
||||
def peer_to_peer
|
||||
session[:donor_signup_url] = request.env["REQUEST_URI"]
|
||||
@npo = Nonprofit.find_by_id(params[:npo_id])
|
||||
@nonprofit = Nonprofit.find_by_id(params[:npo_id])
|
||||
@parent_campaign = Campaign.find_by_id(params[:campaign_id])
|
||||
|
||||
if params[:campaign_id].present? && !@parent_campaign
|
||||
raise ActionController::RoutingError.new('Not Found')
|
||||
end
|
||||
|
||||
if current_user
|
||||
@profile = current_user.profile
|
||||
if (@parent_campaign)
|
||||
@child_campaign = Campaign.where(
|
||||
profile_id: @profile.id,
|
||||
parent_campaign_id: @parent_campaign.id
|
||||
).first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -108,5 +134,4 @@ class CampaignsController < ApplicationController
|
|||
raise ActionController::RoutingError.new('Not Found')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ class Campaign < ActiveRecord::Base
|
|||
:remove_main_image, # for carrierwave
|
||||
:background_image,
|
||||
:remove_background_image, #bool carrierwave
|
||||
:banner_image,
|
||||
:remove_banner_image,
|
||||
:published,
|
||||
:video_url, #str
|
||||
:vimeo_video_id,
|
||||
|
@ -30,7 +32,10 @@ class Campaign < ActiveRecord::Base
|
|||
:hide_thermometer, #bool
|
||||
:hide_title, # bool
|
||||
:receipt_message, # text
|
||||
:hide_custom_amounts # boolean
|
||||
:hide_custom_amounts, # boolean
|
||||
:parent_campaign_id,
|
||||
:reason_for_supporting,
|
||||
:default_reason_for_supporting
|
||||
|
||||
validate :end_datetime_cannot_be_in_past, :on => :create
|
||||
validates :profile, :presence => true
|
||||
|
@ -47,6 +52,7 @@ class Campaign < ActiveRecord::Base
|
|||
|
||||
mount_uploader :main_image, CampaignMainImageUploader
|
||||
mount_uploader :background_image, CampaignBackgroundImageUploader
|
||||
mount_uploader :banner_image, CampaignBannerImageUploader
|
||||
|
||||
has_many :donations
|
||||
has_many :charges, through: :donations
|
||||
|
@ -61,6 +67,9 @@ class Campaign < ActiveRecord::Base
|
|||
belongs_to :profile
|
||||
belongs_to :nonprofit
|
||||
|
||||
belongs_to :parent_campaign, class_name: 'Campaign'
|
||||
has_many :children_campaigns, class_name: 'Campaign', foreign_key: 'parent_campaign_id'
|
||||
|
||||
scope :published, -> {where(:published => true)}
|
||||
scope :active, -> {where(:published => true).where("end_datetime IS NULL OR end_datetime >= ?", Date.today)}
|
||||
scope :past, -> {where(:published => true).where("end_datetime < ?", Date.today)}
|
||||
|
@ -155,4 +164,21 @@ class Campaign < ActiveRecord::Base
|
|||
(self.end_datetime.to_date - Date.today).to_i
|
||||
end
|
||||
|
||||
|
||||
def child_params
|
||||
excluded_for_peer_to_peer = %w(
|
||||
id created_at updated_at slug profile_id url
|
||||
total_raised show_recurring_amount external_identifier parent_campaign_id
|
||||
reason_for_supporting metadata
|
||||
)
|
||||
attributes.except(*excluded_for_peer_to_peer)
|
||||
end
|
||||
|
||||
def child_campaign?
|
||||
parent_campaign.present?
|
||||
end
|
||||
|
||||
def parent_campaign?
|
||||
!child_campaign?
|
||||
end
|
||||
end
|
||||
|
|
16
app/uploaders/campaign_banner_image_uploader.rb
Normal file
16
app/uploaders/campaign_banner_image_uploader.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
||||
class CampaignBannerImageUploader < CarrierWave::Uploader::Base
|
||||
include CarrierWave::MiniMagick
|
||||
|
||||
def store_dir
|
||||
"uploads/campaigns/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
|
||||
def extension_white_list
|
||||
%w(jpg jpeg png)
|
||||
end
|
||||
|
||||
def cache_dir
|
||||
"#{Rails.root}/tmp/uploads"
|
||||
end
|
||||
end
|
|
@ -5,39 +5,42 @@
|
|||
|
||||
<p><strong>Manage Campaign</strong></p>
|
||||
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'campaignSettings') -->
|
||||
<i class='fa fa-gear'></i> Settings
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit' target='_blank' href="<%= @campaign.url + '/supporters' %>">
|
||||
<i class='fa fa-th-list'></i> Dashboard
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'offlineCampaignDonation') -->
|
||||
<i class='fa fa-plus'></i> Offsite Donation
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'manageGiftOptionsModal') -->
|
||||
<i class='fa fa-gift'></i> Gift Options
|
||||
<!--= on 'click' (open_modal 'campaignSettings') -->
|
||||
<i class='fa fa-gear'></i> Settings
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'customReceiptModal') -->
|
||||
<i class='fa fa-pencil'></i> Custom Receipt
|
||||
</a>
|
||||
<% if @campaign.parent_campaign? %>
|
||||
<a class='button--tiny edit' target='_blank' href="<%= @campaign.url + '/supporters' %>">
|
||||
<i class='fa fa-th-list'></i> Dashboard
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'offlineCampaignDonation') -->
|
||||
<i class='fa fa-plus'></i> Offsite Donation
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'manageGiftOptionsModal') -->
|
||||
<i class='fa fa-gift'></i> Gift Options
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'customReceiptModal') -->
|
||||
<i class='fa fa-pencil'></i> Custom Receipt
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<a class='button--tiny edit' href='?preview=t'>
|
||||
<i class='fa fa-eye'></i> Preview Mode
|
||||
</a>
|
||||
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'duplicateFundraiserModal') -->
|
||||
<i class='fa fa-copy'></i> Copy Campaign
|
||||
</a>
|
||||
<% if @campaign.parent_campaign? %>
|
||||
<a class='button--tiny edit'>
|
||||
<!--= on 'click' (open_modal 'duplicateFundraiserModal') -->
|
||||
<i class='fa fa-copy'></i> Copy Campaign
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<%= render 'components/messages/deleted_campaign_or_event', type: 'campaign' %>
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%>
|
||||
<table class='table--striped u-marginBottom--0'>
|
||||
<% campaigns.each do |campaign |%>
|
||||
<% campaigns.each do |campaign|%>
|
||||
|
||||
<% metric = QueryCampaignMetrics.on_donations(campaign.id) %>
|
||||
<tr>
|
||||
<td class='u-padding--0 u-width--200 u-hideIf--400'>
|
||||
<img src='<%= campaign.main_image_url(:normal) %>'>
|
||||
|
@ -18,11 +20,11 @@
|
|||
<small><%= campaign.summary ? strip_tags(campaign.summary) : strip_tags(campaign.tagline) %></small>
|
||||
</p>
|
||||
<% if campaign.show_total_count %>
|
||||
<span class='pastelBox--white u-padding--3'><%= campaign.donations.count %> <small>supporters</small></span>
|
||||
<span class='pastelBox--white u-padding--3'><%= metric['supporters_count'] %> <small>supporters</small></span>
|
||||
<% end %>
|
||||
<% if campaign.show_total_raised %>
|
||||
<span class='pastelBox--white u-padding--3'>
|
||||
<%= print_currency campaign.total_raised, campaign.nonprofit.currency_symbol %> <small>raised</small>
|
||||
<%= print_currency metric['total_raised'], campaign.nonprofit.currency_symbol %> <small>raised</small>
|
||||
</span>
|
||||
<% end %>
|
||||
</td>
|
||||
|
|
77
app/views/campaigns/_new_peer_to_peer_modal.html.erb
Normal file
77
app/views/campaigns/_new_peer_to_peer_modal.html.erb
Normal file
|
@ -0,0 +1,77 @@
|
|||
<!-- partial campaigns/new-modal -->
|
||||
<!-- include 'campaigns/new/index' -->
|
||||
|
||||
<div class='modal' id='newPeerToPeerCampaign'>
|
||||
|
||||
<%= render 'common/modal_header', title: @parent_campaign.name %>
|
||||
|
||||
<div class='wizard-steps' style='display:none;'>
|
||||
<!--= wizard.init 'new_p2p_campaign_wiz' -->
|
||||
|
||||
<%= render 'components/wizard/step_index', wizard_name: 'new_p2p_campaign_wiz' %>
|
||||
|
||||
<div class='modal-body' style='display: table;'>
|
||||
|
||||
<div class='wizard-step reason-step'>
|
||||
<!--= wizard.set_step 'new_p2p_campaign_wiz' 'About you' -->
|
||||
|
||||
<form parsley-validate>
|
||||
<!--= on 'submit' (def 'new_p2p_campaign' form_object) (wizard.advance 'new_p2p_campaign_wiz') -->
|
||||
<input type='hidden' name='campaign[profile_id]' value='<%= current_user.profile.id %>'>
|
||||
<input type='hidden' name='campaign[parent_campaign_id]' value='<%= @parent_campaign.id %>'>
|
||||
<p>Personalize your campaign page with your name and photo to greatly increase the success of your campaign.</p>
|
||||
|
||||
<div class='layout--three'>
|
||||
<fieldset>
|
||||
<label>Name</label>
|
||||
<input type='text' name='profile[name]' placeholder='Your Name' value='<%= @profile.name %>' required>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>City</label>
|
||||
<input type='text' name='profile[city]' placeholder='City' value='<%= @profile.city %>'>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>State</label>
|
||||
<%= render 'common/states_dropdown', name: 'profile[state_code]', default: @profile.state_code %>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<%= render 'components/forms/submit_button', button_text: 'Next', scope: 'new_p2p_campaign_wiz', branded: true %>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class='wizard-step amount-step'>
|
||||
<!--= wizard.set_step 'new_p2p_campaign_wiz' 'Customize' -->
|
||||
|
||||
<form parsley-validate>
|
||||
<!--= on 'submit' create_p2p_campaign -->
|
||||
<!--= log new_p2p_campaign -->
|
||||
|
||||
<div class='u-margin--auto'>
|
||||
<fieldset class='col-right-12'>
|
||||
<label>What's your reason for supporting <%= @parent_campaign.name %>?</label>
|
||||
<p>Your passion is contagious! Inspire giving by telling your network why you’re getting involved in this campaign.</p>
|
||||
|
||||
<textarea class='u-marginTop--15' rows='4' name='campaign[reason_for_supporting]' required><%= @parent_campaign.default_reason_for_supporting %></textarea>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class='group u-marginBottom--0'>
|
||||
<label class='u-paddingTop--5'>Goal Amount</label>
|
||||
<div class='prepend--dollar'>
|
||||
<input class='input--100 u-marginBottom--5' value='1000' type='number' name='campaign[goal_amount_dollars]' required min='1'>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
|
||||
<%= render 'components/forms/submit_button', button_text: 'Preview Campaign!', scope: 'new_p2p_campaign_wiz', branded: true %>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- end partial campaigns/new-modal -->
|
|
@ -4,160 +4,205 @@
|
|||
<%= render 'common/modal_header', title: 'Campaign Settings' %>
|
||||
|
||||
<form class='form--flatFields' autosubmit action='/nonprofits/<%=@nonprofit.id%>/campaigns/<%=@campaign.id%>' method='put' data-reload parsley-validate>
|
||||
<% if @campaign.child_campaign? %>
|
||||
<div class='modal-body'>
|
||||
<section class='layout--three'>
|
||||
<fieldset>
|
||||
<label>Goal</label>
|
||||
<div class='prepend--dollar u-inlineBlock'>
|
||||
<input type='number' parsley-type='number' class='input--150' name='campaign[goal_amount_dollars]' value='<%= sprintf("%.2f", @campaign.goal_amount / 100.0) %>'>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<% else %>
|
||||
<div class='modal-body'>
|
||||
<section class='layout--two'>
|
||||
<fieldset>
|
||||
<label>Title </label>
|
||||
<input type='text' class='u-marginBottom--0' name='campaign[name]' value='<%= @campaign.name %>'>
|
||||
</fieldset>
|
||||
<section class='layout--two'>
|
||||
<fieldset>
|
||||
<label>Title </label>
|
||||
<input type='text' class='u-marginBottom--0' name='campaign[name]' value='<%= @campaign.name %>'>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label class='u-block'>Tagline</label>
|
||||
<input type='text' name='campaign[tagline]' value='<%= @campaign.tagline %>'>
|
||||
</fieldset>
|
||||
</section>
|
||||
<fieldset>
|
||||
<label class='u-block'>Tagline</label>
|
||||
<input type='text' name='campaign[tagline]' value='<%= @campaign.tagline %>'>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
<hr>
|
||||
|
||||
<section class='layout--three'>
|
||||
<fieldset>
|
||||
<label>Goal</label>
|
||||
<div class='prepend--dollar u-inlineBlock'>
|
||||
<input type='number' parsley-type='number' class='input--150' name='campaign[goal_amount_dollars]' value='<%= sprintf("%.2f", @campaign.goal_amount / 100.0) %>'>
|
||||
</div>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
<section class='layout--three'>
|
||||
<fieldset>
|
||||
<label>Goal</label>
|
||||
<div class='prepend--dollar u-inlineBlock'>
|
||||
<input type='number' parsley-type='number' class='input--150' name='campaign[goal_amount_dollars]' value='<%= sprintf("%.2f", @campaign.goal_amount / 100.0) %>'>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>End Date & Time</label>
|
||||
<div pikaday-timepicker='MM/DD/YYYY hh:mm a'>
|
||||
<input class='u-width--200 u-bold u-inlineBlock' type='text' name='campaign[end_datetime]' required parsley-trigger='change' placeholder='MM/DD/YYYY HH:MM' value='<%= Format::Date.full(@campaign.end_datetime, @nonprofit.timezone) %>'>
|
||||
<a class='button edit u-inlineBlock'>Set</a>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= render 'components/forms/slug_field', fundraiser: @campaign, url: @nonprofit.url + '/campaigns' %>
|
||||
|
||||
<hr>
|
||||
|
||||
<section class='layout--three u-overflow--hidden'>
|
||||
<fieldset>
|
||||
<label>Main Image <small>(at least 500x400)</small></label>
|
||||
<p><small>Used for previews and social media</small></p>
|
||||
<div class='image-upload u-inlineBlock' style='background-image: url("<%= @campaign.main_image_url(:thumb) %>");'>
|
||||
<span><i class='fa fa-pencil'></i> Edit</span>
|
||||
<input type='file' name='campaign[main_image]'>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>Header Image <small>(1000x600)</small></label>
|
||||
<p><small>Used at top of campaign page</small></p>
|
||||
<% if @campaign_background_image %>
|
||||
<div class='image-upload u-inlineBlock' style='background-image:url(<%= @campaign_background_image %>)'>
|
||||
<!--= on 'click' (open_modal 'uploadBackgroundImage') -->
|
||||
<span><i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<a class='button edit'>
|
||||
<!--= on 'click' (open_modal 'uploadBackgroundImage') -->
|
||||
<i class='fa fa-plus'></i> Add Image </i>
|
||||
</a>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>Campaign Video </label>
|
||||
<p><small>Videos are optional but recommended</small></p>
|
||||
<% if @campaign.vimeo_video_id || @campaign.youtube_video_id%>
|
||||
<% if @campaign.vimeo_video_id %>
|
||||
<div class='image-upload u-inlineBlock'>
|
||||
<!--= set_attr 'style' vimeo_image_url -->
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<span> <i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% else%>
|
||||
<div class='image-upload u-inlineBlock' style="background-image:url('http://img.youtube.com/vi/<%=@campaign.youtube_video_id%>/0.jpg')">
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<span> <i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<a class='button edit'><i class='fa fa-plus'></i> Add Video</a>
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<% end %>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<section class='layout--two'>
|
||||
<fieldset>
|
||||
<input type='hidden' name='campaign[published]' value='false'>
|
||||
<input id='campaign-published-input' type='checkbox' name='campaign[published]' <%= @campaign.published ? "checked='checked'" : '' %>>
|
||||
<label class='u-marginBottom--10' for='campaign-published-input'>Published?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[show_total_raised]'>
|
||||
<input id='show_total_raised-checkbox' type='checkbox' name='campaign[show_total_raised]' <%= @campaign.show_total_raised ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='show_total_raised-checkbox'>Show total dollars raised?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[show_total_count]'>
|
||||
<input id='show_total_count-checkbox' type='checkbox' name='campaign[show_total_count]' <%= @campaign.show_total_count ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='show_total_count-checkbox'>Show total supporters?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_goal]'>
|
||||
<input id='hide_goal-checkbox' type='checkbox' name='campaign[hide_goal]' <%= @campaign.hide_goal ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_goal-checkbox'>Hide goal amount?</label>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_thermometer]'>
|
||||
<input id='hide_thermometer-checkbox' type='checkbox' name='campaign[hide_thermometer]' <%= @campaign.hide_thermometer ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_thermometer-checkbox'>Hide thermometer?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_activity_feed]'>
|
||||
<input id='hide_activity_feed-checkbox' type='checkbox' name='campaign[hide_activity_feed]' <%= @campaign.hide_activity_feed ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_activity_feed-checkbox'>Hide activity feed?</label>
|
||||
</fieldset>
|
||||
|
||||
<% if @campaign_background_image %>
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_title]'>
|
||||
<input id='hide_title-checkbox' type='checkbox' name='campaign[hide_title]' <%= @campaign.hide_title ? 'checked="checked"' : '' %>>
|
||||
<label for='hide_title-checkbox'>Hide main title?</label>
|
||||
<label>End Date & Time</label>
|
||||
<div pikaday-timepicker='MM/DD/YYYY hh:mm a'>
|
||||
<input class='u-width--200 u-bold u-inlineBlock' type='text' name='campaign[end_datetime]' required parsley-trigger='change' placeholder='MM/DD/YYYY HH:MM' value='<%= Format::Date.full(@campaign.end_datetime, @nonprofit.timezone) %>'>
|
||||
<a class='button edit u-inlineBlock'>Set</a>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<%= render 'components/forms/slug_field', fundraiser: @campaign, url: @nonprofit.url + '/campaigns' %>
|
||||
|
||||
<hr>
|
||||
|
||||
<section class='layout--three u-overflow--hidden'>
|
||||
<fieldset>
|
||||
<label>Main Image <small>(at least 500x400)</small></label>
|
||||
<p><small>Used for previews and social media</small></p>
|
||||
<div class='image-upload u-inlineBlock' style='background-image: url("<%= @campaign.main_image_url(:thumb) %>");'>
|
||||
<span><i class='fa fa-pencil'></i> Edit</span>
|
||||
<input type='file' name='campaign[main_image]'>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>Header Image <small>(1000x600)</small></label>
|
||||
<p><small>Used at top of campaign page</small></p>
|
||||
<% if @campaign_background_image %>
|
||||
<div class='image-upload u-inlineBlock' style='background-image:url(<%= @campaign_background_image %>)'>
|
||||
<!--= on 'click' (open_modal 'uploadBackgroundImage') -->
|
||||
<span><i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<a class='button edit'>
|
||||
<!--= on 'click' (open_modal 'uploadBackgroundImage') -->
|
||||
<i class='fa fa-plus'></i> Add Image </i>
|
||||
</a>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label>Campaign Video </label>
|
||||
<p><small>Videos are optional but recommended</small></p>
|
||||
<% if @campaign.vimeo_video_id || @campaign.youtube_video_id%>
|
||||
<% if @campaign.vimeo_video_id %>
|
||||
<div class='image-upload u-inlineBlock'>
|
||||
<!--= set_attr 'style' vimeo_image_url -->
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<span> <i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% else%>
|
||||
<div class='image-upload u-inlineBlock' style="background-image:url('http://img.youtube.com/vi/<%=@campaign.youtube_video_id%>/0.jpg')">
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<span> <i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<a class='button edit'><i class='fa fa-plus'></i> Add Video</a>
|
||||
<!--= on 'click' (open_modal 'videoModal') -->
|
||||
<% end %>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
<section class='layout--one'>
|
||||
<fieldset>
|
||||
<label>Banner <small>(1000x120)</small></label>
|
||||
<p><small>Custom image at the very top of the campaign page</small></p>
|
||||
<% if @campaign.banner_image_url.present? %>
|
||||
<div class='image-upload image-upload--banner u-inlineBlock u-marginBottom--15' style='background-image:url(<%= @campaign.banner_image_url %>)'>
|
||||
<!--= on 'click' (open_modal 'uploadBannerImage') -->
|
||||
<span><i class='fa fa-pencil'></i> Edit</span>
|
||||
</div>
|
||||
<% else %>
|
||||
<a class='button edit u-marginBottom--15'>
|
||||
<!--= on 'click' (open_modal 'uploadBannerImage') -->
|
||||
<i class='fa fa-plus'></i> Add Image </i>
|
||||
</a>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<% if !@campaign.child_campaign? %>
|
||||
<hr>
|
||||
<section class='layout--two'>
|
||||
<fieldset>
|
||||
<label>Default reason for peer-to-peer campaigns</label>
|
||||
<p><small>Use it to suggest a good, pre-filled reason for campaigners</small></p>
|
||||
<textarea class='u-marginTop--15' rows='4' name='campaign[default_reason_for_supporting]'><%= @campaign.default_reason_for_supporting %></textarea>
|
||||
</fieldset>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<hr>
|
||||
|
||||
<section class='layout--two'>
|
||||
<fieldset>
|
||||
<input type='hidden' name='campaign[published]' value='false'>
|
||||
<input id='campaign-published-input' type='checkbox' name='campaign[published]' <%= @campaign.published ? "checked='checked'" : '' %>>
|
||||
<label class='u-marginBottom--10' for='campaign-published-input'>Published?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[show_total_raised]'>
|
||||
<input id='show_total_raised-checkbox' type='checkbox' name='campaign[show_total_raised]' <%= @campaign.show_total_raised ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='show_total_raised-checkbox'>Show total dollars raised?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[show_total_count]'>
|
||||
<input id='show_total_count-checkbox' type='checkbox' name='campaign[show_total_count]' <%= @campaign.show_total_count ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='show_total_count-checkbox'>Show total supporters?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_goal]'>
|
||||
<input id='hide_goal-checkbox' type='checkbox' name='campaign[hide_goal]' <%= @campaign.hide_goal ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_goal-checkbox'>Hide goal amount?</label>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_thermometer]'>
|
||||
<input id='hide_thermometer-checkbox' type='checkbox' name='campaign[hide_thermometer]' <%= @campaign.hide_thermometer ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_thermometer-checkbox'>Hide thermometer?</label>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_activity_feed]'>
|
||||
<input id='hide_activity_feed-checkbox' type='checkbox' name='campaign[hide_activity_feed]' <%= @campaign.hide_activity_feed ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_activity_feed-checkbox'>Hide activity feed?</label>
|
||||
</fieldset>
|
||||
|
||||
<% if @campaign_background_image %>
|
||||
<fieldset>
|
||||
<input type='hidden' value='false' name='campaign[hide_title]'>
|
||||
<input id='hide_title-checkbox' type='checkbox' name='campaign[hide_title]' <%= @campaign.hide_title ? 'checked="checked"' : '' %>>
|
||||
<label for='hide_title-checkbox'>Hide main title?</label>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
|
||||
<fieldset>
|
||||
<!--= show_if length gift_options.data -->
|
||||
<input type='hidden' value='false' name='campaign[hide_custom_amounts]'>
|
||||
<input id='hide_custom_amounts' type='checkbox' name='campaign[hide_custom_amounts]' <%= @campaign.hide_custom_amounts ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_custom_amounts'>Disallow regular contributions during campaign?</label>
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<fieldset>
|
||||
<!--= show_if length gift_options.data -->
|
||||
<input type='hidden' value='false' name='campaign[hide_custom_amounts]'>
|
||||
<input id='hide_custom_amounts' type='checkbox' name='campaign[hide_custom_amounts]' <%= @campaign.hide_custom_amounts ? 'checked="checked"' : '' %>>
|
||||
<label class='u-marginBottom--10' for='hide_custom_amounts'>Disallow regular contributions during campaign?</label>
|
||||
<label>Short Description <small>(used for sharing on social media)</small></label>
|
||||
<textarea rows='4' name='campaign[summary]'><%= @campaign.summary %></textarea>
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
<div><p class='error'></p></div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<% end %>
|
||||
|
||||
<fieldset>
|
||||
<label>Short Description <small>(used for sharing on social media)</small></label>
|
||||
<textarea rows='4' name='campaign[summary]'><%= @campaign.summary %></textarea>
|
||||
</fieldset>
|
||||
|
||||
<div><p class='error'></p></div>
|
||||
</div>
|
||||
|
||||
<footer class='modal-footer u-padding--15 layout--two'>
|
||||
<span>
|
||||
|
|
|
@ -10,15 +10,27 @@
|
|||
|
||||
<% content_for :javascripts do %>
|
||||
<script>
|
||||
<% if @npo %>
|
||||
appl.def('selected_result', {name: '<%= @npo.name.html_safe %>', id: <%= @npo.id %>})
|
||||
app.nonprofit_id = <%= @npo.id %>
|
||||
<% if @nonprofit %>
|
||||
appl.def('selected_result', {
|
||||
name: '<%= @nonprofit.name.html_safe %>',
|
||||
id: <%= @nonprofit.id %>,
|
||||
modal_id: 'newCampaign'
|
||||
})
|
||||
app.nonprofit_id = <%= @nonprofit.id %>
|
||||
<% elsif @parent_campaign %>
|
||||
appl.def('selected_result', {
|
||||
name: '<%= @parent_campaign.nonprofit.name.html_safe %>',
|
||||
parent_campaign_id: <%= @parent_campaign.id %>,
|
||||
campaign_name: '<%= @parent_campaign.name %>',
|
||||
modal_id: 'newPeerToPeerCampaign'
|
||||
})
|
||||
app.nonprofit_id = <%= @parent_campaign.nonprofit.id %>
|
||||
<% end %>
|
||||
<% if current_user %>
|
||||
appl.def('is_logged_in', true)
|
||||
appl.def('is_confirmed', <%= !current_user.confirmed_at.nil? %>)
|
||||
appl.def('user', {
|
||||
name: '<%= current_user.profile.name %>',
|
||||
name: '<%= current_user.profile.name || current_user.email %>',
|
||||
id: <%= current_user.id %>
|
||||
})
|
||||
<% end %>
|
||||
|
@ -26,13 +38,15 @@
|
|||
<%= IncludeAsset.js '/client/js/campaigns/peer_to_peer/page.js' %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'layouts/top_nav' %>
|
||||
<% if @parent_campaign && @parent_campaign.banner_image_url %>
|
||||
<div class='campaign-banner u-margin'>
|
||||
<img src='<%= @parent_campaign.banner_image_url %>'>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<main>
|
||||
<header class='u-paddingX--15 container'>
|
||||
<h1>Start a campaign</h1>
|
||||
<h4>Start raising funds for your favorite nonprofit in less than 10 minutes.</h4>
|
||||
</div>
|
||||
<h1>Start my campaign</h1>
|
||||
</header>
|
||||
|
||||
<hr>
|
||||
|
@ -43,19 +57,39 @@
|
|||
<i class='icon icon-bicycle u-fontSize--50'></i>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class='u-paddingLeft--20'>Below is a roadmap to get you started on your campaign</h5>
|
||||
<h5 class='u-paddingLeft--20'>Below is a roadmap to get you started with your campaign</h5>
|
||||
</td>
|
||||
</tr></table>
|
||||
|
||||
<ul class='u-paddingLeft--10 timeline--checklist'>
|
||||
<li>
|
||||
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||
<span class='annot'>10 seconds</span>
|
||||
<p class='strong u-marginBottom--15'>Search for a nonprofit that you want to fundraise for</p>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<%= render 'search_for_npo' %>
|
||||
</div>
|
||||
</li>
|
||||
<% if @parent_campaign.present? %>
|
||||
<li>
|
||||
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||
<span class='annot'>10 seconds</span>
|
||||
<p class='strong u-marginBottom--15'>My campaign</p>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<h4><%= @parent_campaign.name %></h4>
|
||||
</div>
|
||||
</li>
|
||||
<% elsif @nonprofit.present?%>
|
||||
<li>
|
||||
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||
<span class='annot'>10 seconds</span>
|
||||
<p class='strong u-marginBottom--15'>My nonprofit</p>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<h4><%= @nonprofit.name %></h4>
|
||||
</div>
|
||||
</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||
<span class='annot'>10 seconds</span>
|
||||
<p class='strong u-marginBottom--15'>Search for a nonprofit that you want to fundraise for</p>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<%= render 'search_for_npo' %>
|
||||
</div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<li>
|
||||
<!--= set_attr_if is_logged_in 'class' 'is-complete' -->
|
||||
|
@ -81,6 +115,8 @@
|
|||
<p>
|
||||
<!--= hide_if is_confirmed -->
|
||||
Simply click the confirmation link in the email we sent you when you created your <%= Settings.general.name %> account.
|
||||
<br/><br/>
|
||||
Once you've clicked the confirmation link, come back here and <a href="javascript:window.location.reload(true)">reload this page</a>.
|
||||
</p>
|
||||
<p class='pastelBox--green u-padding--5'>
|
||||
<!--= show_if is_confirmed -->
|
||||
|
@ -90,33 +126,44 @@
|
|||
</li>
|
||||
|
||||
<li>
|
||||
<p class='strong u-marginBottom--15'>Start your campaign</p>
|
||||
<span class='annot'>5 minutes</span>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<p>Use our simple wizard to create your campaign for
|
||||
<strong>
|
||||
<!--= show_if selected_result -->
|
||||
<!--= put selected_result.name -->.
|
||||
</strong>
|
||||
<strong>
|
||||
<!--= hide_if selected_result -->
|
||||
your favorite nonprofit.
|
||||
</strong>
|
||||
</p>
|
||||
<p><small>If you need any further help setting up your campaign, check out <a target='_blank' href='/help/peer-to-peer-campaigns-essentials-package-feature'>this handy article</a>.</small></p>
|
||||
<div class='u-centered u-padding--15'>
|
||||
<button class='button orange'>
|
||||
<!--= show_if (all is_logged_in is_confirmed selected_result) -->
|
||||
<!--= on 'click' (open_modal 'newCampaign') -->
|
||||
Start Campaign
|
||||
</button>
|
||||
<% if !@child_campaign %>
|
||||
<p class='strong u-marginBottom--15'>Start your campaign</p>
|
||||
<span class='annot'>5 minutes</span>
|
||||
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||
<p>Use our simple wizard to create your campaign for
|
||||
<strong>
|
||||
<!--= show_if selected_result -->
|
||||
<!--= put selected_result.name -->.
|
||||
</strong>
|
||||
<strong>
|
||||
<!--= hide_if selected_result -->
|
||||
your favorite nonprofit.
|
||||
</strong>
|
||||
</p>
|
||||
<p><small>If you need any further help setting up your campaign, check out <a target='_blank' href='/help/peer-to-peer-campaigns-essentials-package-feature'>this handy article</a>.</small></p>
|
||||
<div class='u-centered u-padding--15'>
|
||||
<button class='button orange'>
|
||||
<!--= show_if (all is_logged_in is_confirmed selected_result) -->
|
||||
<!--= on 'click' (open_modal selected_result.modal_id) -->
|
||||
Start Campaign
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<p>
|
||||
Seems like you already have a campaign for that cause! <%= link_to "Click here to see it", @child_campaign.url %>.
|
||||
</p>
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<% if current_user %>
|
||||
<%= render 'campaigns/new_modal' %>
|
||||
<% if @nonprofit.present? %>
|
||||
<%= render 'campaigns/new_modal' %>
|
||||
<% elsif @parent_campaign.present? %>
|
||||
<%= render 'campaigns/new_peer_to_peer_modal' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
app.days_remaining = '<%= @campaign.days_left %>'
|
||||
app.recurring_fund = <%= @campaign.recurring_fund? %>
|
||||
app.vimeo_id = "<%= @campaign.vimeo_video_id ? @campaign.vimeo_video_id : '' %>"
|
||||
app.current_campaign_editor = <%= current_campaign_editor? %>
|
||||
app.current_campaign_editor = <%= current_campaign_editor? %>
|
||||
app.is_parent_campaign = <%= @campaign.parent_campaign? %>
|
||||
appl.def('has_video', <%= @campaign.video_url.present? %>)
|
||||
appl.def('campaign_is_deleted', <%= @campaign.deleted || false %>)
|
||||
appl.def('has_main_image', <%= @campaign.main_image.file.present? %>)
|
||||
</script>
|
||||
<%= render 'schema', campaign: @campaign, url: @url %>
|
||||
<%= render 'common/froala' if current_campaign_editor? %>
|
||||
appl.def('has_main_image', <%= @campaign.main_image.file.present? %>)
|
||||
</script>
|
||||
|
||||
<%= render 'schema', campaign: @campaign, url: @url %>
|
||||
<%= IncludeAsset.js '/client/js/campaigns/show/page.js' %>
|
||||
<% end %>
|
||||
|
||||
|
@ -64,27 +64,35 @@
|
|||
image_url: @campaign_background_image,
|
||||
is_editor: current_campaign_editor?,
|
||||
hide_title: @campaign.hide_title && @campaign_background_image,
|
||||
header_content_partial: 'header_content' %>
|
||||
header_content_partial: 'header_content',
|
||||
banner_image_url: @campaign.banner_image&.url %>
|
||||
|
||||
<%= render 'components/preview_mode_notification' %>
|
||||
|
||||
<div class="container u-marginTop--15 <%= @brand_color ? 'is-branded' : '' %>" data-id='<%= @campaign.id %>'>
|
||||
|
||||
|
||||
<section class='box'>
|
||||
<section class='box'>
|
||||
<%= render 'campaign_media' %>
|
||||
</section>
|
||||
|
||||
<section class='box-r'>
|
||||
|
||||
<% if current_campaign_editor? %>
|
||||
<% if current_campaign_editor? && !@campaign.child_campaign? %>
|
||||
<!-- Campaign editor gift option management modal -->
|
||||
<button class='button edit u-width--full u-marginBottom--15'>
|
||||
<!--= on 'click' (open_modal 'manageGiftOptionsModal') -->
|
||||
<i class='fa fa-gift'></i> Manage Gift Options
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
|
||||
<% if !@campaign.child_campaign? %>
|
||||
<aside class='u-marginBottom--15 pastelBox--grey'>
|
||||
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @peer_to_peer_campaign_param %>'>
|
||||
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||
</a>
|
||||
</aside>
|
||||
<% end %>
|
||||
|
||||
<!-- flimflam gift options javascript gets rendered into this div: -->
|
||||
<div class='ff-sidebar'></div>
|
||||
|
||||
|
@ -95,16 +103,22 @@
|
|||
</div>
|
||||
</aside>
|
||||
|
||||
<% unless current_campaign_editor? %>
|
||||
<aside class='u-marginTop--15'>
|
||||
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?npo_id=<%=@nonprofit.id %>'>
|
||||
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||
</a>
|
||||
</aside>
|
||||
<% end %>
|
||||
|
||||
</section>
|
||||
|
||||
<% if @campaign.child_campaign? %>
|
||||
<%= render 'components/fundraising_pages/campaigner_profile',
|
||||
profile: @campaign.profile,
|
||||
campaign_name: @campaign.name,
|
||||
reason_for_supporting: @campaign.reason_for_supporting
|
||||
%>
|
||||
|
||||
<section class='box u-centered' style="margin-top:20px; margin-bottom:20px;">
|
||||
<a class='button--jumbo' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @peer_to_peer_campaign_param %>'>
|
||||
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||
</a>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<section class='box'>
|
||||
|
||||
<div id='js-campaignBody' class='editable' data-path='/nonprofits/<%= @nonprofit.id %>/campaigns/<%= @campaign.id %>.json' data-key='campaign[body]'>
|
||||
|
@ -130,6 +144,10 @@
|
|||
end_point: "/nonprofits/#{@nonprofit.id}/campaigns/#{@campaign.id}",
|
||||
image_url: @campaign_background_image,
|
||||
input_name: 'campaign[background_image]' %>
|
||||
<%= render 'components/upload_banner_image',
|
||||
end_point: "/nonprofits/#{@nonprofit.id}/campaigns/#{@campaign.id}",
|
||||
image_url: @campaign.banner_image_url,
|
||||
input_name: 'campaign[banner_image]' %>
|
||||
<%= render 'components/custom_receipt_modal',
|
||||
title: 'Campaign Receipt Message',
|
||||
type: 'campaign',
|
||||
|
@ -141,4 +159,3 @@
|
|||
|
||||
<%= render 'components/share_modal', name: @campaign.name, type: 'campaign' %>
|
||||
<%= render 'common/email_share_modal', fundraiser: @campaign.name, fundraiser_url: @url %>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
Upload <%= image_url ? 'Different Image' : '' %>
|
||||
</button>
|
||||
<a class='button--small red u-marginTop--10 u-width--full' <%= image_url ? '' : 'disabled' %>>
|
||||
<!--= on 'click' (confirm 'Are you sure?' remove_this_image -->
|
||||
<!--= on 'click' (confirm 'Are you sure?' remove_background_image -->
|
||||
Remove Image
|
||||
</a>
|
||||
</div>
|
||||
|
|
31
app/views/components/_upload_banner_image.html.erb
Normal file
31
app/views/components/_upload_banner_image.html.erb
Normal file
|
@ -0,0 +1,31 @@
|
|||
<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%>
|
||||
<div class='modal' id='uploadBannerImage'>
|
||||
<%= render 'common/modal_header', title: 'Banner Image' %>
|
||||
<div class='modal-body'>
|
||||
|
||||
<fieldset class='u-centered'>
|
||||
<p>Select and upload a banner image for this page.</p>
|
||||
<p class='u-marginBottom--20'>For best results, the image should be <strong>980px by 120px</strong> in size.</p>
|
||||
<form autosubmit action='<%= end_point %>' method='put' data-reload>
|
||||
<div class='image-upload image-upload--banner' style='background-image:url(<%= image_url ? image_url : '' %>)'>
|
||||
<span><i class='fa fa-image'></i> Select</span>
|
||||
<input type='file' name="<%= input_name %>" required>
|
||||
</div>
|
||||
<p class='u-color--red'>
|
||||
<!--= show_if (length image_upload.error) -->
|
||||
<small><!--= put image_upload.error --></small>
|
||||
</p>
|
||||
<div class='u-width--500 u-margin--auto'>
|
||||
<button type='submit' class='button--small u-width--full u-marginTop--20' data-loading='Uploading...'>
|
||||
Upload <%= image_url ? 'Different Image' : '' %>
|
||||
</button>
|
||||
<a class='button--small red u-marginTop--10 u-width--full' <%= image_url ? '' : 'disabled' %>>
|
||||
<!--= on 'click' (confirm 'Are you sure?' remove_banner_image ) -->
|
||||
Remove Image
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
</div> <!-- modal-body -->
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<section class='box' style="font-style: italic">
|
||||
<div class="well" style="padding-left:16px;">
|
||||
<h4><%= profile.name %> <small>(<%= profile.city %><% if profile.state_code && profile.city %><%= profile.city_state %> <%= profile.state_code %><% end %>)</small></h4>
|
||||
<blockquote><p>"<%= reason_for_supporting %>"</p></blockquote></div>
|
||||
</section>
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
|
||||
<header class="container fundraisingHeader--plain" <%= css_style.html_safe %> id='js-fundraisingHeader'>
|
||||
<% if banner_image_url %>
|
||||
<div class='campaign-banner u-margin'>
|
||||
<img src='<%= banner_image_url %>'>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class='fundraisingHeader--image-container' id='js-fundraisingHeader-image' style='display: none;'>
|
||||
<img class='fundraisingHeader--image-aspectRatio' src="<%= asset_path 'graphics/10x6.jpg' %>">
|
||||
</div>
|
||||
|
@ -16,4 +21,4 @@
|
|||
</section>
|
||||
<% end %>
|
||||
</header>
|
||||
<!-- partial end: fundaising_pages/_header -->
|
||||
<!-- partial end: fundraising_pages/_header -->
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<section class='sideNav-section'>
|
||||
<a class='sideNav-link' href='<%= profile_url(current_user.profile) %>'>
|
||||
<% if current_user.profile.picture? %>
|
||||
<img class='sideNav-profile'src="<%= current_user.profile.get_profile_picture(:tiny) %>')">
|
||||
<img class='sideNav-profile' src="<%= current_user.profile.get_profile_picture(:tiny) %>">
|
||||
<% else %>
|
||||
<i class="sideNav-icon icon-user-1"></i>
|
||||
<% end %>
|
||||
|
@ -46,4 +46,3 @@
|
|||
</section>
|
||||
|
||||
<% end %>
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@
|
|||
<%= render 'components/fundraising_pages/header',
|
||||
image_url: @nonprofit_background_image,
|
||||
is_editor: current_nonprofit_user?,
|
||||
header_content_partial: 'header_content' %>
|
||||
header_content_partial: 'header_content',
|
||||
banner_image_url: nil %>
|
||||
|
||||
<%= render 'components/preview_mode_notification' %>
|
||||
|
||||
|
|
40
client/js/campaigns/new/peer_to_peer_wizard.js
Normal file
40
client/js/campaigns/new/peer_to_peer_wizard.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
require('../../components/wizard')
|
||||
var format_err = require('../../common/format_response_error')
|
||||
|
||||
appl.def('advance_p2p_campaign_name_step', function(form_obj) {
|
||||
var name = form_obj['campaign[name]']
|
||||
appl.def('new_p2p_campaign', form_obj)
|
||||
appl.wizard.advance('new_p2p_campaign_wiz')
|
||||
})
|
||||
|
||||
// Post a new campaign.
|
||||
appl.def('create_p2p_campaign', function(el) {
|
||||
var form_data = utils.toFormData(appl.prev_elem(el))
|
||||
form_data = utils.mergeFormData(form_data, appl.new_p2p_campaign)
|
||||
appl.def('new_p2p_campaign_wiz.loading', true)
|
||||
|
||||
post_campaign(form_data)
|
||||
.then(function(req) {
|
||||
appl.notify("Redirecting to your campaign...")
|
||||
appl.redirect(JSON.parse(req.response).url)
|
||||
})
|
||||
.catch(function(req) {
|
||||
appl.def('new_p2p_campaign_wiz.loading', false)
|
||||
appl.def('new_p2p_campaign_wiz.error', req.responseText)
|
||||
})
|
||||
})
|
||||
|
||||
// Using the bare-bones XMLHttpRequest API so we can post form data and upload the image
|
||||
function post_campaign(form_data) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var req = new XMLHttpRequest()
|
||||
req.open("POST", '/nonprofits/' + app.nonprofit_id + '/campaigns')
|
||||
req.setRequestHeader('X-CSRF-Token', window._csrf)
|
||||
console.log(form_data)
|
||||
req.send(form_data)
|
||||
req.onload = function(ev) {
|
||||
if(req.status === 200) resolve(req)
|
||||
else reject(req)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -20,6 +20,8 @@ appl.def('create_campaign', function(el) {
|
|||
form_data = utils.mergeFormData(form_data, appl.new_campaign)
|
||||
appl.def('new_campaign_wiz.loading', true)
|
||||
|
||||
// TODO: for p2p capmaigns, merge with preset campaing params
|
||||
|
||||
post_campaign(form_data)
|
||||
.then(function(req) {
|
||||
appl.notify("Redirecting to your campaign...")
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
require('../new/peer_to_peer_wizard')
|
||||
require('../../common/image_uploader')
|
||||
|
||||
require('../new/wizard')
|
||||
var request = require("../../common/client")
|
||||
|
||||
require('../../common/onboard')
|
||||
|
||||
// setting up some default values
|
||||
appl.def('is_signing_up', true)
|
||||
.def('selected_result_index', -1)
|
||||
.def('selected_result_index', -1)
|
||||
|
||||
|
||||
appl.def('search_nonprofits', function(value){
|
||||
|
@ -23,8 +22,8 @@ appl.def('search_nonprofits', function(value){
|
|||
|
||||
// if the the input is empty, it clears the dropdown
|
||||
if (!value) {
|
||||
appl.def('search_results', [])
|
||||
return
|
||||
appl.def('search_results', [])
|
||||
return
|
||||
}
|
||||
|
||||
// logic for controlling the dropdown options with up
|
||||
|
@ -67,12 +66,12 @@ function setIndexWithArrows(dir) {
|
|||
if(dir === 'down') {
|
||||
var search_length = appl.search_results.length -1
|
||||
appl.def('selected_result_index', appl.selected_result_index === search_length
|
||||
? search_length
|
||||
: appl.selected_result_index += 1)
|
||||
? search_length
|
||||
: appl.selected_result_index += 1)
|
||||
} else {
|
||||
appl.def('selected_result_index', appl.selected_result_index === 0
|
||||
? 0
|
||||
: appl.selected_result_index -= 1)
|
||||
? 0
|
||||
: appl.selected_result_index -= 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,12 @@ require('../../components/ajax/toggle_soft_delete')(url, 'campaign')
|
|||
|
||||
// Initialize the froala wysiwyg
|
||||
var editable = require('../../common/editable')
|
||||
editable($('#js-campaignBody'), {
|
||||
sticky: true,
|
||||
placeholder: "Add your campaign's story here. We strongly recommend that this section is filled out with at least 250 words. It will be saved automatically as you type. You can add images, videos and custom HTML too."
|
||||
})
|
||||
if (app.is_parent_campaign) {
|
||||
editable($('#js-campaignBody'), {
|
||||
sticky: true,
|
||||
placeholder: "Add your campaign's story here. We strongly recommend that this section is filled out with at least 250 words. It will be saved automatically as you type. You can add images, videos and custom HTML too."
|
||||
})
|
||||
}
|
||||
|
||||
editable($('#js-customReceipt'), {
|
||||
button: ["bold", "italic", "formatBlock", "align", "createLink",
|
||||
|
@ -34,8 +36,17 @@ editable($('#js-customReceipt'), {
|
|||
var path = '/nonprofits/' + app.nonprofit_id + '/campaigns/' + app.campaign_id
|
||||
|
||||
|
||||
appl.def('remove_this_image', function() {
|
||||
appl.remove_background_image(path, 'campaign')
|
||||
appl.def('remove_banner_image', function() {
|
||||
var url = '/nonprofits/' + app.nonprofit_id + '/campaigns/' + app.campaign_id
|
||||
var notification = 'Removing banner image...'
|
||||
var payload = {remove_banner_image : true}
|
||||
appl.remove_image(url, 'campaign', notification, payload)
|
||||
})
|
||||
|
||||
appl.def('remove_background_image', function(url, resource) {
|
||||
var notification = 'Removing background image...'
|
||||
var payload = {remove_background_image : true}
|
||||
appl.remove_image(url, resource, notification, payload)
|
||||
})
|
||||
|
||||
appl.def('count_story_words', function() {
|
||||
|
|
|
@ -30,7 +30,7 @@ function init(giftsNeedRefresh$, parentState) {
|
|||
state.giftOptions$ = flyd.mergeAll([
|
||||
pageloadGifts$
|
||||
, refreshedGifts$
|
||||
, flyd.stream([]) // default before ajax loads
|
||||
, flyd.stream([]) // default before ajax loads
|
||||
])
|
||||
return state
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ const giftBox = state => gift => {
|
|||
, h('p.u-marginBottom--15', gift.description)
|
||||
, h('div', [ giftButton(state, gift) ])
|
||||
])
|
||||
, app.current_campaign_editor // Show edit button only if the current user is a campaign editor
|
||||
, (app.current_campaign_editor && app.is_parent_campaign) // Show edit button only if the current user is a parent campaign editor
|
||||
? h('button.button--tiny.absolute.edit.hasShadow', {
|
||||
on: {click: ev => state.openEditGiftModal$(gift)}
|
||||
}, [
|
||||
|
@ -79,4 +79,3 @@ const totalContributions = gift => {
|
|||
}
|
||||
|
||||
module.exports = {view, init}
|
||||
|
||||
|
|
|
@ -7,7 +7,17 @@ module.exports = function(npo_id) {
|
|||
|
||||
request.get(campaignsPath).end(function(err, resp){
|
||||
resp.body.unshift(false)
|
||||
appl.def('campaigns.data', resp.body)
|
||||
let campaign_id_names = resp.body.map((i) => {
|
||||
if (i.isChildCampaign)
|
||||
{
|
||||
return {id: i.id, name: i.name + " - " + i.creator}
|
||||
}
|
||||
else
|
||||
{
|
||||
return {id: i.id, name: i.name}
|
||||
}
|
||||
})
|
||||
appl.def('campaigns.data', campaign_id_names)
|
||||
})
|
||||
request.get(eventsPath).end(function(err, resp){
|
||||
resp.body.unshift(false)
|
||||
|
|
|
@ -17,10 +17,10 @@ $('.image-upload input').change(function(e) {
|
|||
}
|
||||
})
|
||||
|
||||
appl.def('remove_background_image', function(url, resource) {
|
||||
appl.def('remove_image', function(url, resource, notification, payload) {
|
||||
var data = {}
|
||||
data[resource] = {remove_background_image: true}
|
||||
appl.notify('Removing background image...')
|
||||
data[resource] = payload
|
||||
appl.notify(notification)
|
||||
appl.def('loading', true)
|
||||
$.ajax({
|
||||
type: 'put',
|
||||
|
|
|
@ -99,10 +99,10 @@ const init = params$ => {
|
|||
(ev, params) => {
|
||||
if(!parent) return
|
||||
if(params.redirect) parent.postMessage(`commitchange:redirect:${params.redirect}`, '*')
|
||||
else if(params.mode !== 'embedded'){
|
||||
else if(params.mode !== 'embedded'){
|
||||
parent.postMessage('commitchange:close', '*');
|
||||
} else {
|
||||
if (window.parent) {window.parent.postMessage('commitchange:close', '*');};
|
||||
if (window.parent) {window.parent.postMessage('commitchange:close', '*');};
|
||||
}
|
||||
}
|
||||
, state.clickFinish$, state.params$ )
|
||||
|
|
|
@ -63,7 +63,7 @@ Config.schema do
|
|||
|
||||
required(:aws).schema do
|
||||
# the region your AWS bucket is in
|
||||
required(:region).filled(:str?)
|
||||
optional(:region).filled(:str?)
|
||||
|
||||
# the name of your aws bucket
|
||||
required(:bucket).filled(:str?)
|
||||
|
@ -295,4 +295,3 @@ Settings.reload!
|
|||
|
||||
# Initialize the rails application
|
||||
Commitchange::Application.initialize!
|
||||
|
||||
|
|
|
@ -8,4 +8,3 @@ AWS.config({
|
|||
|
||||
s3 = AWS::S3.new
|
||||
S3Bucket = s3.buckets[Settings.aws.bucket]
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ CarrierWave.configure do |config|
|
|||
config.storage = :aws
|
||||
config.aws_bucket = Settings.aws.bucket
|
||||
config.aws_acl = :public_read
|
||||
config.asset_host = Settings.image&.host || "https://s3-#{Settings.aws.region}.amazonaws.com/#{Settings.aws.bucket}"
|
||||
config.asset_host = Settings.image&.host || "https://#{Settings.aws.bucket}.s3.amazonaws.com"
|
||||
config.aws_authenticated_url_expiration = 60 * 60 * 24 * 365
|
||||
config.aws_credentials = {
|
||||
access_key_id: Settings.aws.access_key_id,
|
||||
|
|
|
@ -144,6 +144,7 @@ Commitchange::Application.routes.draw do
|
|||
get(:activities, {on: :member})
|
||||
put(:soft_delete, {on: :member})
|
||||
get(:name_and_id, {on: :collection})
|
||||
post :create_from_template, on: :collection
|
||||
resources(:campaign_gift_options, {only: [:index, :show, :create, :update, :destroy]}) do
|
||||
put(:update_order, {on: :collection})
|
||||
end
|
||||
|
|
|
@ -20,7 +20,6 @@ default:
|
|||
aws:
|
||||
access_key_id: <%= ENV['AWS_ACCESS_KEY'] %>
|
||||
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
|
||||
region: test
|
||||
bucket: <%= ENV['S3_BUCKET_NAME'] %>
|
||||
|
||||
mailer:
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddParentCampaignIdToCampaigns < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :campaigns, :parent_campaign_id, :integer
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddReasonForSupportingToCampaigns < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :campaigns, :reason_for_supporting, :text
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddDefaultReasonForSupportingToCampaigns < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :campaigns, :default_reason_for_supporting, :text
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddBannerImageToCampaigns < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :campaigns, :banner_image, :string
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddIndexParentCampaignIdToCampaign < ActiveRecord::Migration
|
||||
def change
|
||||
add_index :campaigns, :parent_campaign_id
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@
|
|||
--
|
||||
|
||||
-- Dumped from database version 9.6.5
|
||||
-- Dumped by pg_dump version 9.6.10
|
||||
-- Dumped by pg_dump version 9.6.11
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
|
@ -392,7 +392,11 @@ CREATE TABLE public.campaigns (
|
|||
hide_custom_amounts boolean,
|
||||
show_recurring_amount boolean DEFAULT false,
|
||||
end_datetime timestamp without time zone,
|
||||
external_identifier character varying(255)
|
||||
external_identifier character varying(255),
|
||||
parent_campaign_id integer,
|
||||
reason_for_supporting text,
|
||||
default_reason_for_supporting text,
|
||||
banner_image character varying(255)
|
||||
);
|
||||
|
||||
|
||||
|
@ -3143,6 +3147,13 @@ CREATE INDEX index_activities_on_supporter_id ON public.activities USING btree (
|
|||
CREATE INDEX index_campaign_gifts_on_campaign_gift_option_id ON public.campaign_gifts USING btree (campaign_gift_option_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_campaigns_on_parent_campaign_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_campaigns_on_parent_campaign_id ON public.campaigns USING btree (parent_campaign_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_cards_on_id_and_holder_type_and_holder_id_and_inactive; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -4337,6 +4348,18 @@ INSERT INTO schema_migrations (version) VALUES ('20180608205049');
|
|||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180608212658');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165400');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165401');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165402');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165403');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165404');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180703165405');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180713213748');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20180713215825');
|
||||
|
@ -4347,3 +4370,5 @@ INSERT INTO schema_migrations (version) VALUES ('20181002160627');
|
|||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20181003212559');
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES ('20181120182105');
|
||||
|
||||
|
|
39
lib/create/create_peer_to_peer_campaign.rb
Normal file
39
lib/create/create_peer_to_peer_campaign.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
||||
module CreatePeerToPeerCampaign
|
||||
def self.create(campaign_params, profile_id)
|
||||
begin
|
||||
parent_campaign = Campaign.find(campaign_params[:parent_campaign_id])
|
||||
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
return { errors: { parent_campaign_id: 'not found' } }.as_json
|
||||
end
|
||||
|
||||
p2p_params = campaign_params.except(:nonprofit_id, :summary,:goal_amount)
|
||||
p2p_params.merge!(parent_campaign.child_params)
|
||||
|
||||
profile = Profile.find(profile_id)
|
||||
base_slug = Format::Url.convert_to_slug "#{p2p_params[:name]}-#{profile.name}"
|
||||
algo = SlugP2pCampaignNamingAlgorithm.new(p2p_params[:nonprofit_id])
|
||||
p2p_params[:slug] = algo.create_copy_name(base_slug)
|
||||
|
||||
campaign = Campaign.create(p2p_params)
|
||||
|
||||
campaign.published = true
|
||||
campaign.profile = profile
|
||||
campaign.save
|
||||
|
||||
campaign.update_attribute(:main_image, parent_campaign.main_image) unless !parent_campaign.main_image rescue AWS::S3::Errors::NoSuchKey
|
||||
campaign.update_attribute(:background_image, parent_campaign.background_image) unless !parent_campaign.background_image rescue AWS::S3::Errors::NoSuchKey
|
||||
campaign.update_attribute(:banner_image, parent_campaign.banner_image) unless !parent_campaign.banner_image rescue AWS::S3::Errors::NoSuchKey
|
||||
|
||||
return { errors: campaign.errors.messages }.as_json unless campaign.errors.empty?
|
||||
|
||||
gift_option_params = []
|
||||
parent_campaign.campaign_gift_options.each do |option|
|
||||
excluded_for_peer_to_peer = %w(id campaign_id created_at updated_at)
|
||||
campaign.campaign_gift_options.create option.attributes.except(*excluded_for_peer_to_peer)
|
||||
end
|
||||
|
||||
campaign.as_json
|
||||
end
|
||||
end
|
|
@ -2,21 +2,31 @@
|
|||
module QueryCampaignMetrics
|
||||
|
||||
def self.on_donations(campaign_id)
|
||||
Qx.select(
|
||||
campaign = Campaign.find(campaign_id)
|
||||
|
||||
result = Qx.select(
|
||||
"COALESCE(COUNT(DISTINCT donations.id), 0) AS supporters_count",
|
||||
"COALESCE(SUM(payments.gross_amount), 0) AS total_raised",
|
||||
"campaigns.goal_amount",
|
||||
"campaigns.show_total_count",
|
||||
"campaigns.show_total_raised")
|
||||
"COALESCE(SUM(payments.gross_amount), 0) AS total_raised"
|
||||
)
|
||||
.from("campaigns")
|
||||
.left_join(
|
||||
.join(
|
||||
["donations", "donations.campaign_id=campaigns.id"],
|
||||
["payments", "payments.donation_id=donations.id"]
|
||||
)
|
||||
.where("campaigns.id=$id", id: campaign_id)
|
||||
.group_by('campaigns.id')
|
||||
.where("campaigns.id IN (#{QueryCampaigns
|
||||
.get_campaign_and_children(campaign_id)
|
||||
.parse
|
||||
})")
|
||||
.execute
|
||||
.last
|
||||
|
||||
return {
|
||||
'supporters_count' => result['supporters_count'],
|
||||
'total_raised'=> result['total_raised'],
|
||||
'goal_amount'=> campaign.goal_amount,
|
||||
'show_total_count'=> campaign.show_total_count,
|
||||
'show_total_raised'=> campaign.show_total_raised
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ module QueryCampaigns
|
|||
["payments AS onetime", "onetime.id=payments.id AND onetime.kind='Donation'"],
|
||||
["payments AS offsite", "offsite.id=payments.id AND offsite.kind='OffsitePayment'"],
|
||||
["payments AS recurring", "recurring.id=payments.id AND recurring.kind='RecurringDonation'"])
|
||||
.where("donations.campaign_id" => campaign_id)
|
||||
.where("donations.campaign_id IN (#{QueryCampaigns.get_campaign_and_children(campaign_id).parse})")
|
||||
end
|
||||
|
||||
|
||||
|
@ -62,15 +62,25 @@ module QueryCampaigns
|
|||
|
||||
|
||||
def self.name_and_id(npo_id)
|
||||
Psql.execute(
|
||||
Qexpr.new.select(
|
||||
"campaigns.name",
|
||||
"campaigns.id")
|
||||
.from("campaigns")
|
||||
.where("campaigns.nonprofit_id=$id", id: npo_id)
|
||||
.where("campaigns.deleted='f' OR campaigns.deleted IS NULL")
|
||||
.order_by("campaigns.name ASC")
|
||||
)
|
||||
|
||||
np = Nonprofit.find(npo_id)
|
||||
campaigns = np.campaigns.not_deleted.includes(:profile).order('campaigns.name ASC')
|
||||
output = campaigns.map do |i|
|
||||
{
|
||||
'name' => i.name,
|
||||
'id' => i.id,
|
||||
'isChildCampaign' => i.child_campaign?,
|
||||
'creator' => i.profile&.name || "user ##{i.profile.id}"
|
||||
}
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def self.get_campaign_and_children(campaign_id)
|
||||
Qx.select("id")
|
||||
.from('campaigns')
|
||||
.where("campaigns.id = $id OR campaigns.parent_campaign_id=$id",
|
||||
id: campaign_id)
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ module QueryDonations
|
|||
.join(:supporters, "supporters.id=donations.supporter_id")
|
||||
.left_outer_join(:campaign_gifts, "campaign_gifts.donation_id=donations.id")
|
||||
.left_outer_join(:campaign_gift_options, "campaign_gift_options.id=campaign_gifts.campaign_gift_option_id")
|
||||
.where("donations.campaign_id=$id", id: campaign_id)
|
||||
.where("donations.campaign_id IN (#{QueryCampaigns
|
||||
.get_campaign_and_children(campaign_id)
|
||||
.parse})")
|
||||
.group_by("donations.id", "supporters.id")
|
||||
.order_by("donations.date")
|
||||
)
|
||||
|
@ -27,7 +29,9 @@ module QueryDonations
|
|||
|
||||
def self.for_campaign_activities(id)
|
||||
QueryDonations.activities_expression(['donations.recurring'])
|
||||
.where('donations.campaign_id=$id', id: id)
|
||||
.where("donations.campaign_id IN (#{QueryCampaigns
|
||||
.get_campaign_and_children(id)
|
||||
.parse})")
|
||||
.execute
|
||||
end
|
||||
|
||||
|
|
|
@ -140,7 +140,8 @@ module QueryPayments
|
|||
inner_donation_search = inner_donation_search.where('donations.event_id=$id', id: query[:event_id])
|
||||
end
|
||||
if (query[:campaign_id].present?)
|
||||
inner_donation_search = inner_donation_search.where('donations.campaign_id=$id', id: query[:campaign_id])
|
||||
campaign_search = campaign_and_child_query_as_raw_string
|
||||
inner_donation_search = inner_donation_search.where("donations.campaign_id IN (#{campaign_search})", id: query[:campaign_id])
|
||||
end
|
||||
expr = Qexpr.new.select('payments.id').from('payments')
|
||||
.left_outer_join('supporters', "supporters.id=payments.supporter_id")
|
||||
|
@ -191,9 +192,10 @@ module QueryPayments
|
|||
expr = expr.where('payments.kind IN ($kinds)', kinds: query[:donation_type].split(','))
|
||||
end
|
||||
if query[:campaign_id].present?
|
||||
campaign_search = campaign_and_child_query_as_raw_string
|
||||
expr = expr
|
||||
.left_outer_join("campaigns", "campaigns.id=donations.campaign_id" )
|
||||
.where("campaigns.id=$id", id: query[:campaign_id])
|
||||
.where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id])
|
||||
end
|
||||
if query[:event_id].present?
|
||||
tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by("payment_id").as("tix")
|
||||
|
@ -224,7 +226,8 @@ module QueryPayments
|
|||
inner_donation_search = inner_donation_search.where('donations.event_id=$id', id: query[:event_id])
|
||||
end
|
||||
if (query[:campaign_id].present?)
|
||||
inner_donation_search = inner_donation_search.where('donations.campaign_id=$id', id: query[:campaign_id])
|
||||
campaign_search = campaign_and_child_query_as_raw_string
|
||||
inner_donation_search = inner_donation_search.where("donations.campaign_id IN (#{campaign_search})", id: query[:campaign_id])
|
||||
end
|
||||
expr = Qexpr.new.select('payments.id').from('payments')
|
||||
.left_outer_join('supporters', "supporters.id=payments.supporter_id")
|
||||
|
@ -278,9 +281,10 @@ module QueryPayments
|
|||
expr = expr.where('payments.kind IN ($kinds)', kinds: query[:donation_type].split(','))
|
||||
end
|
||||
if query[:campaign_id].present?
|
||||
campaign_search = campaign_and_child_query_as_raw_string
|
||||
expr = expr
|
||||
.left_outer_join("campaigns", "campaigns.id=donations.campaign_id" )
|
||||
.where("campaigns.id=$id", id: query[:campaign_id])
|
||||
.where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id])
|
||||
end
|
||||
if query[:event_id].present?
|
||||
tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by("payment_id").as("tix")
|
||||
|
@ -446,4 +450,8 @@ module QueryPayments
|
|||
|
||||
}
|
||||
end
|
||||
|
||||
def self.campaign_and_child_query_as_raw_string
|
||||
"SELECT c_temp.id from campaigns c_temp where c_temp.id=$id OR c_temp.parent_campaign_id=$id"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,9 @@ module QuerySupporters
|
|||
.left_outer_join('campaign_gifts', 'donations.id=campaign_gifts.donation_id')
|
||||
.left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id')
|
||||
.where("supporters.nonprofit_id=$id", id: np_id)
|
||||
.where("donations.campaign_id=$id", id: campaign_id)
|
||||
.where("donations.campaign_id IN (#{QueryCampaigns
|
||||
.get_campaign_and_children(campaign_id)
|
||||
.parse})")
|
||||
.group_by('supporters.id')
|
||||
.order_by('MAX(donations.date) DESC')
|
||||
|
||||
|
@ -253,7 +255,9 @@ module QuerySupporters
|
|||
expr = expr.and_where("tags.ids @> ARRAY[$tag_ids]", tag_ids: tag_ids)
|
||||
end
|
||||
if query[:campaign_id].present?
|
||||
expr = expr.add_join("donations", "donations.supporter_id=supporters.id AND donations.campaign_id=#{query[:campaign_id].to_i}")
|
||||
expr = expr.add_join("donations", "donations.supporter_id=supporters.id AND donations.campaign_id IN (#{QueryCampaigns
|
||||
.get_campaign_and_children(query[:campaign_id].to_i)
|
||||
.parse})")
|
||||
end
|
||||
|
||||
if query[:event_id].present?
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 5.1 KiB |
78
spec/lib/create/create_peer_to_peer_campaign_spec.rb
Normal file
78
spec/lib/create/create_peer_to_peer_campaign_spec.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
||||
require 'rails_helper'
|
||||
|
||||
describe CreatePeerToPeerCampaign do
|
||||
describe '.create' do
|
||||
let!(:profile) { force_create(:profile, :user => force_create(:user)) }
|
||||
let!(:parent_campaign) { force_create(:campaign, name: 'Parent campaign') }
|
||||
|
||||
context 'on success' do
|
||||
it 'returns a hash' do
|
||||
campaign_params = { name: 'Child campaign', parent_campaign_id: parent_campaign.id, goal_amount_dollars: "1000" }
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
result = CreatePeerToPeerCampaign.create(campaign_params, profile.id)
|
||||
|
||||
expect(result).to be_kind_of Hash
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns created peer-to-peer campaign' do
|
||||
campaign_params = { name: 'Child campaign', parent_campaign_id: parent_campaign.id, goal_amount_dollars: "1000" }
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
result = CreatePeerToPeerCampaign.create(campaign_params, profile.id)
|
||||
|
||||
expect(result).not_to include 'errors'
|
||||
expect(result['parent_campaign_id']).to eq parent_campaign.id
|
||||
expect(result['created_at']).to eq 'Sun, 05 Apr 2020 00:00:00 UTC +00:00'
|
||||
end
|
||||
end
|
||||
|
||||
it 'assigns proper slug' do
|
||||
campaign_params = { name: 'Child campaign', parent_campaign_id: parent_campaign.id, goal_amount_dollars: "1000" }
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
result = CreatePeerToPeerCampaign.create(campaign_params, profile.id)
|
||||
|
||||
expect(result).not_to include 'errors'
|
||||
expect(result['slug']).to eq 'child-campaign_000'
|
||||
end
|
||||
end
|
||||
|
||||
it 'saves campaign' do
|
||||
campaign_params = { name: 'Child campaign', parent_campaign_id: parent_campaign.id, goal_amount_dollars: "1000" }
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
expect { CreatePeerToPeerCampaign.create(campaign_params, profile.id) }.to change(Campaign, :count).by 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on failure' do
|
||||
it "returns an error if parent campaign can't be found" do
|
||||
campaign_params = {}
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
result = CreatePeerToPeerCampaign.create(campaign_params, profile.id)
|
||||
|
||||
expect(result).to be_kind_of Hash
|
||||
expect(result['errors']['parent_campaign_id']).to eq 'not found'
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a list of error messages for attribute validation' do
|
||||
campaign_params = { parent_campaign_id: parent_campaign.id }
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
result = CreatePeerToPeerCampaign.create(campaign_params, profile.id)
|
||||
|
||||
expect(result).to be_kind_of Hash
|
||||
expect(result).to include 'errors'
|
||||
expect(result['errors']['goal_amount']).to match ["can't be blank", 'is not a number']
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't save campaign" do
|
||||
campaign_params = {}
|
||||
Timecop.freeze(2020, 4, 5) do
|
||||
expect { CreatePeerToPeerCampaign.create(campaign_params, profile.id) }.not_to change(Campaign, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -114,7 +114,8 @@ describe InsertDuplicate do
|
|||
expect(result.attributes.with_indifferent_access).to eq(common_result_attributes.merge(
|
||||
{
|
||||
id: result.id,
|
||||
end_datetime: nil
|
||||
end_datetime: nil,
|
||||
banner_image: nil
|
||||
}).with_indifferent_access)
|
||||
validate_cgo(result)
|
||||
end
|
||||
|
|
81
spec/lib/query/query_campaign_metrics_spec.rb
Normal file
81
spec/lib/query/query_campaign_metrics_spec.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
|
||||
require 'rails_helper'
|
||||
|
||||
|
||||
describe QueryCampaignMetrics do
|
||||
|
||||
describe 'calculates your metrics plus children' do
|
||||
let(:nonprofit) {force_create(:nonprofit)}
|
||||
let(:campaign) {force_create(:campaign, nonprofit:nonprofit, show_total_count:false, show_total_raised: false, goal_amount: 16000)}
|
||||
let(:campaign_child) {force_create(:campaign, nonprofit:nonprofit, parent_campaign:campaign, show_total_count:true, show_total_raised: true, goal_amount: 8000)}
|
||||
|
||||
let(:campaign_child_2) {force_create(:campaign, nonprofit:nonprofit, parent_campaign:campaign, show_total_count:true, show_total_raised: true, goal_amount: 4000 )}
|
||||
|
||||
let(:donation) { force_create(:donation, campaign: campaign, amount: 1000)}
|
||||
let(:payment) { force_create(:payment, donation: donation, gross_amount:1000)}
|
||||
|
||||
let(:donation2) { force_create(:donation, campaign: campaign, amount: 2000)}
|
||||
let(:payment2) { force_create(:payment, donation: donation2, gross_amount:2000)}
|
||||
|
||||
|
||||
let(:donation3) { force_create(:donation, campaign: campaign_child, amount: 4000)}
|
||||
let(:payment3) { force_create(:payment, donation: donation3, gross_amount:4000)}
|
||||
|
||||
let(:donation4) { force_create(:donation, campaign: campaign_child_2, amount: 8000)}
|
||||
let(:payment4) { force_create(:payment, donation: donation4, gross_amount:8000)}
|
||||
|
||||
|
||||
|
||||
let(:payments) do
|
||||
payment
|
||||
payment2
|
||||
payment3
|
||||
payment4
|
||||
end
|
||||
let (:campaign_metric) do
|
||||
|
||||
|
||||
payments
|
||||
|
||||
QueryCampaignMetrics.on_donations(campaign.id)
|
||||
|
||||
end
|
||||
|
||||
let(:campaign_child_metric) do
|
||||
payments
|
||||
QueryCampaignMetrics.on_donations(campaign_child.id)
|
||||
end
|
||||
|
||||
let(:campaign_child_2_metric) do
|
||||
payments
|
||||
QueryCampaignMetrics.on_donations(campaign_child_2.id)
|
||||
end
|
||||
|
||||
it 'campaign metric is valid' do
|
||||
expect(campaign_metric['supporters_count']).to eq 4
|
||||
expect(campaign_metric['total_raised']).to eq 15000
|
||||
expect(campaign_metric['goal_amount']).to eq 16000
|
||||
expect(campaign_metric['show_total_count']).to eq false
|
||||
expect(campaign_metric['show_total_raised']).to eq false
|
||||
end
|
||||
|
||||
it 'campaign child metric is valid' do
|
||||
expect(campaign_child_metric['supporters_count']).to eq 1
|
||||
expect(campaign_child_metric['total_raised']).to eq 4000
|
||||
expect(campaign_child_metric['goal_amount']).to eq 8000
|
||||
expect(campaign_child_metric['show_total_count']).to eq true
|
||||
expect(campaign_child_metric['show_total_raised']).to eq true
|
||||
end
|
||||
|
||||
|
||||
it 'campaign child 2metric is valid' do
|
||||
expect(campaign_child_2_metric['supporters_count']).to eq 1
|
||||
expect(campaign_child_2_metric['total_raised']).to eq 8000
|
||||
expect(campaign_child_2_metric['goal_amount']).to eq 4000
|
||||
expect(campaign_child_2_metric['show_total_count']).to eq true
|
||||
expect(campaign_child_2_metric['show_total_raised']).to eq true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -46,20 +46,8 @@ describe SlugP2pCampaignNamingAlgorithm do
|
|||
expect(algo.create_copy_name(@name2)).to eq short_slug_copy_today_plus_1
|
||||
end
|
||||
|
||||
it 'errors when at max copies' do
|
||||
@name = short_slug
|
||||
@copy_base = copy_base
|
||||
campaign
|
||||
campaigns_at_max_copies
|
||||
|
||||
expect{ algo.create_copy_name(@name) }.to(raise_error{|error|
|
||||
expect(error).to be_a ArgumentError
|
||||
})
|
||||
campaigns_at_max_copies.each {|i|
|
||||
expect {algo.create_copy_name(i.slug)}.to(raise_error{|error|
|
||||
expect(error).to be_a ArgumentError
|
||||
})
|
||||
}
|
||||
it 'has 999 as the max_copies' do
|
||||
expect(algo.max_copies).to eq 999
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue