Base code for campaigns and users

master
Sven Slootweg 12 years ago
parent 17f7ea929c
commit 6f94521d51

@ -20,6 +20,8 @@
"port": 11211
},
"class_map": {
"user": "User",
"campaign": "Campaign"
},
"components": [
"router",

@ -0,0 +1,55 @@
<?php
/*
* ReDonate is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if(!isset($_APP)) { die("Unauthorized."); }
class Campaign extends CPHPDatabaseRecordClass
{
public $table_name = "campaigns";
public $fill_query = "SELECT * FROM campaigns WHERE `Id` = :Id";
public $verify_query = "SELECT * FROM campaigns WHERE `Id` = :Id";
public $prototype = array(
'string' => array(
'Name' => "Name",
'UrlName' => "UrlName"
),
'numeric' => array(
'OwnerId' => "UserId"
),
'boolean' => array(
'AllowOneTime' => "AllowOneTime"
),
'user' => array(
'Owner' => "Owner"
)
);
public static function CheckIfUrlNameExists($urlname)
{
try
{
$result = Campaign::FindByUrlName($urlname);
return true;
}
catch (NotFoundException $e)
{
return false;
}
}
public static function FindByUrlName($urlname)
{
return self::CreateFromQuery("SELECT * FROM campaigns WHERE `UrlName` = :UrlName", array(':UrlName' => $urlname), 0, true);
}
}

@ -0,0 +1,155 @@
<?php
/*
* Cryto Team is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if(!isset($_APP)) { die("Unauthorized."); }
class User extends CPHPDatabaseRecordClass
{
public $table_name = "users";
public $fill_query = "SELECT * FROM users WHERE `Id` = :Id";
public $verify_query = "SELECT * FROM users WHERE `Id` = :Id";
public $prototype = array(
'string' => array(
'Username' => "Username",
'DisplayName' => "DisplayName",
'EmailAddress' => "EmailAddress",
'Hash' => "Hash",
'Salt' => "Salt",
'ActivationKey' => "ActivationKey"
),
'boolean' => array(
'IsAdmin' => "Admin",
'IsActivated' => "Activated"
)
);
public function GenerateSalt()
{
$this->uSalt = random_string(10);
}
public function GenerateHash()
{
if(!empty($this->uSalt))
{
if(!empty($this->uPassword))
{
$this->uHash = $this->CreateHash($this->uPassword);
}
else
{
throw new Exception("User object is missing a password.");
}
}
else
{
throw new Exception("User object is missing a salt.");
}
}
public function CreateHash($input)
{
global $cphp_config;
$hash = crypt($input, "$5\$rounds=50000\${$this->uSalt}{$cphp_config->salt}$");
$parts = explode("$", $hash);
return $parts[4];
}
public function VerifyPassword($password)
{
if($this->CreateHash($password) == $this->sHash)
{
return true;
}
else
{
return false;
}
}
public function Authenticate()
{
$_SESSION['user_id'] = $this->sId;
$_SESSION['logout_key'] = random_string(32);
$_SESSION['is_admin'] = $this->sIsAdmin;
NewTemplater::SetGlobalVariable("logged-in", true);
$this->SetGlobalVariables();
}
public function SetGlobalVariables()
{
NewTemplater::SetGlobalVariable("my-displayname", $this->sDisplayName);
}
public static function CheckIfEmailValid($email)
{
return preg_match("/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i", $email);
}
public static function CheckIfEmailExists($email)
{
try
{
$result = User::FindByEmail($email);
return true;
}
catch (NotFoundException $e)
{
return false;
}
}
public static function CheckIfUsernameExists($username)
{
try
{
$result = User::FindByUsername($username);
return true;
}
catch (NotFoundException $e)
{
return false;
}
}
public static function CheckIfDisplayNameExists($displayname)
{
try
{
$result = User::FindByDisplayName($displayname);
return true;
}
catch (NotFoundException $e)
{
return false;
}
}
public static function FindByEmail($email)
{
return self::CreateFromQuery("SELECT * FROM users WHERE `EmailAddress` = :EmailAddress", array(':EmailAddress' => $email), 0);
}
public static function FindByUsername($username)
{
return self::CreateFromQuery("SELECT * FROM users WHERE `Username` = :Username", array(':Username' => $username), 0);
}
public static function FindByDisplayName($displayname)
{
return self::CreateFromQuery("SELECT * FROM users WHERE `DisplayName` = :DisplayName", array(':DisplayName' => $displayname), 0);
}
}

@ -11,17 +11,4 @@
* licensing text.
*/
$_CPHP = true;
$_CPHP_CONFIG = "../config.json";
require("cphp/base.php");
if(!empty($_GET['type']))
{
$can_once = true;
}
else
{
$can_once = false;
}
echo(NewTemplater::Render("landing", $locale->strings, array('can-donate-once' => $can_once)));
require("rewrite.php");

@ -0,0 +1,26 @@
<?php
/*
* ReDonate is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
if(!isset($_APP)) { die("Unauthorized."); }
try
{
$sCampaign = Campaign::FindByUrlName($router->uParameters[1]);
$sPageTitle = "Contribute to {$sCampaign->sName}";
$sPageContents = NewTemplater::Render("landing", $locale->strings, array("can-donate-once" => true, "project-name" => $sCampaign->sName));
}
catch (NotFoundException $e)
{
$sPageContents = NewTemplater::Render("404", $locale->strings, array());
}

@ -0,0 +1,46 @@
<?php
/*
* ReDonate is more free software. It is licensed under the WTFPL, which
* allows you to do pretty much anything with it, without having to
* ask permission. Commercial use is allowed, and no attribution is
* required. We do politely request that you share your modifications
* to benefit other developers, but you are under no enforced
* obligation to do so :)
*
* Please read the accompanying LICENSE document for the full WTFPL
* licensing text.
*/
$_CPHP = true;
$_CPHP_CONFIG = "../config.json";
require("cphp/base.php");
$_APP = true;
function __autoload($class_name)
{
global $_APP;
$class_name = str_replace("\\", "/", strtolower($class_name));
require_once("classes/{$class_name}.php");
}
$sPageTitle = "";
$sPageContents = "";
$router = new CPHPRouter();
$router->allow_slash = true;
$router->ignore_query = true;
$router->routes = array(
0 => array(
"^/$" => "modules/index.php",
"^/register/$" => "modules/register.php",
"^/login/$" => "modules/login.php",
"^/campaign/([a-zA-Z0-9-]+)" => "modules/landing.php",
"^/campaign/([a-zA-Z0-9-]+)/subscribe" => "modules/subscribe.php",
)
);
$router->RouteRequest();
echo(NewTemplater::Render("layout", $locale->strings, array("contents" => $sPageContents, "title" => $sPageTitle)));

@ -0,0 +1,196 @@
body
{
padding: 0px;
margin: 0px;
background-color: #E5EFD7;
font-family: Lato, sans-serif;
}
.clear
{
clear: both;
}
.wrapper
{
width: 960px;
margin: 0px auto;
}
.header h1
{
color: #7AAA3E;
font-family: Roboto, sans-serif;
font-weight: 500;
font-size: 46px;
margin: 8px 0px;
}
.header h1 .highlight
{
color: #587532;
}
.main
{
background-color: #FCFFF7;
box-shadow: 0px 0px 10px 1px #b0b0b0;
-webkit-box-shadow: 0px 0px 10px 1px #b0b0b0;
-moz-box-shadow: 0px 0px 10px 1px #b0b0b0;
-o-box-shadow: 0px 0px 10px 1px #b0b0b0;
-ms-box-shadow: 0px 0px 10px 1px #b0b0b0;
padding: 16px 20px;
}
.main h2
{
font-size: 32px;
margin: 0px 0px 6px 0px;
color: #3B4A28;
}
.main .details, .main .subscribe
{
margin-top: 16px;
width: 46%;
}
.main .details
{
float: left;
padding-right: 4%;
text-align: justify;
border-right: 1px solid silver;
}
.main .subscribe
{
float: right;
padding-left: 3%;
}
.main h3
{
margin: 0px;
}
.main .subscribe h3
{
margin-bottom: 32px;
}
.main .leader
{
font-size: 18px;
}
.main .more
{
margin-top: 16px;
border-top: 1px solid silver;
padding-top: 32px;
text-align: justify;
}
.main .more .wrapper
{
width: 730px;
margin: 0px auto;
}
.main h3.section
{
margin-top: 16px;
border-top: 1px solid silver;
padding-top: 16px;
}
.subscribe p
{
font-size: 18px;
}
#field_currency
{
text-align: right;
border: 1px solid #6CA825;
border-radius: 1px;
background-color: transparent;
font-size: 18px;
}
#field_amount, #field_email
{
border: 1px solid #6CA825;
border-radius: 1px;
background-color: transparent;
font-size: 18px;
padding: 1px 5px;
}
#field_amount
{
width: 55px;
}
#field_email
{
margin-top: 6px;
width: 335px;
}
.green-button
{
margin-top: 0px;
background: #66CC00;
border-top: 1px solid #529d26;
border-right: 1px solid #2c5615;
border-bottom: 1px solid #1d390e;
border-left: 1px solid #2c5615;
border-radius: 4px;
box-shadow: inset 0 1px 10px 1px #8eff4b,
0px 1px 0 #2c5713,
0 6px 0px #305e14,
0 8px 4px 1px #111111;
color: #fff;
font: bold 20px/1 "helvetica neue",
helvetica,
arial,
sans-serif;
margin-bottom: 15px;
padding: 10px 15px 12px 15px;
text-align: center;
text-shadow: 0px -1px 1px #1e2d4d;
-webkit-background-clip: padding-box; }
.green-button:hover
{
box-shadow: inset 0 0px 20px 1px #bdff87,
0px 1px 0 #324d1d,
0 6px 0px #37531f,
0 8px 4px 1px #111111;
cursor: pointer; }
.green-button:active
{
box-shadow: inset 0 1px 10px 1px #a5df76,
0 1px 0 #203213,
0 2px 0 #223413,
0 4px 3px 0 #111111;
margin-top: 5px;
margin-bottom: 10px;
}
.green-button:disabled
{
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
filter: grayscale(100%);
}
p.pledge
{
padding-left: 15px;
padding-top: 10px;
}

@ -0,0 +1,15 @@
<h2>Page not found.</h2>
<p>
<strong>Sorry, we could not find the page you requested.</strong> But while you're here anyway, why not introduce ourselves?
</p>
<p>
ReDonate is recurring contributions, done right. Set up a campaign, and accept monthly donations from contributors that wish
to support you, without <em>ever</em> automatically charging an account. ReDonate is friendly to you, <em>and</em> your
donors.
</p>
<p>
Gotten curious? <a href="/">Visit our homepage to learn more...</a>
</p>

@ -1,296 +1,85 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href='http://fonts.googleapis.com/css?family=Lato:400,700|Cantarell:400,700|Roboto:500' rel='stylesheet' type='text/css'>
<style>
body
{
padding: 0px;
margin: 0px;
background-color: #E5EFD7;
font-family: Lato, sans-serif;
}
.clear
{
clear: both;
}
.wrapper
{
width: 960px;
margin: 0px auto;
}
.header h1
{
color: #7AAA3E;
font-family: Roboto, sans-serif;
font-weight: 500;
font-size: 46px;
margin: 8px 0px;
}
.header h1 .highlight
{
color: #587532;
}
.main
{
background-color: #FCFFF7;
box-shadow: 0px 0px 10px 1px #b0b0b0;
-webkit-box-shadow: 0px 0px 10px 1px #b0b0b0;
-moz-box-shadow: 0px 0px 10px 1px #b0b0b0;
-o-box-shadow: 0px 0px 10px 1px #b0b0b0;
-ms-box-shadow: 0px 0px 10px 1px #b0b0b0;
padding: 16px 20px;
}
.main h2
{
font-size: 32px;
margin: 0px 0px 6px 0px;
color: #3B4A28;
}
.main .details, .main .subscribe
{
margin-top: 16px;
width: 46%;
}
.main .details
{
float: left;
padding-right: 4%;
text-align: justify;
border-right: 1px solid silver;
}
.main .subscribe
{
float: right;
padding-left: 3%;
}
.main h3
{
margin: 0px;
}
.main .subscribe h3
{
margin-bottom: 32px;
}
.main .leader
{
font-size: 18px;
}
.main .more
{
margin-top: 16px;
border-top: 1px solid silver;
padding-top: 32px;
text-align: justify;
}
.main .more .wrapper
{
width: 730px;
margin: 0px auto;
}
.subscribe p
{
font-size: 18px;
}
#field_currency
{
text-align: right;
border: 1px solid #6CA825;
border-radius: 1px;
background-color: transparent;
font-size: 18px;
}
#field_amount, #field_email
{
border: 1px solid #6CA825;
border-radius: 1px;
background-color: transparent;
font-size: 18px;
padding: 1px 5px;
}
#field_amount
{
width: 55px;
}
#field_email
{
margin-top: 6px;
width: 335px;
}
/* Thanks, Ari! */
.green-button
{
margin-top: 0px;
background: #66CC00;
border-top: 1px solid #529d26;
border-right: 1px solid #2c5615;
border-bottom: 1px solid #1d390e;
border-left: 1px solid #2c5615;
border-radius: 4px;
box-shadow: inset 0 1px 10px 1px #8eff4b,
0px 1px 0 #2c5713,
0 6px 0px #305e14,
0 8px 4px 1px #111111;
color: #fff;
font: bold 20px/1 "helvetica neue",
helvetica,
arial,
sans-serif;
margin-bottom: 15px;
padding: 10px 15px 12px 15px;
text-align: center;
text-shadow: 0px -1px 1px #1e2d4d;
-webkit-background-clip: padding-box; }
<h2>Contribute to {%?project-name} monthly, no automatic charges.</h2>
.green-button:hover
{
box-shadow: inset 0 0px 20px 1px #bdff87,
0px 1px 0 #324d1d,
0 6px 0px #37531f,
0 8px 4px 1px #111111;
cursor: pointer; }
.green-button:active
{
box-shadow: inset 0 1px 10px 1px #a5df76,
0 1px 0 #203213,
0 2px 0 #223413,
0 4px 3px 0 #111111;
margin-top: 5px;
margin-bottom: 10px;
}
.green-button:disabled
{
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
filter: grayscale(100%);
}
p.pledge
{
padding-left: 15px;
padding-top: 10px;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="header">
<h1>
Re<span class="highlight">Donate</span>
</h1>
</div>
<div class="main">
<h2>Contribute to AnonNews monthly, no automatic charges.</h2>
<div class="details">
<h3>How does it work?</h3>
<p class="leader">
Most recurring services - even charities! - will automatically charge your account every month.
ReDonate is different.
</p>
<p>
When you subscribe to a ReDonate campaign, you only have to tell us how much you want to donate, and provide
your e-mail address. Every month, we will e-mail you to remind you of your pledge. The e-mail you receive will
include a list of payment methods, and an unsubscribe link.
</p>
<p>
What this means in simple terms is that you'll <strong>never</strong> be automatically charged for a donation -
you can decide to cancel your subscription at any time, without hassle, and without "oops, I forgot to cancel"
moments!
</p>
{%if can-donate-once == true}
<h3>Why use ReDonate?</h3>
<p>
Many donors wish to donate to a cause at regular intervals, but do not want to have their account charged automatically.
To take away the hassle of having to remember to donate every month, ReDonate was born. No need to remember a
donation schedule, and no automatic charges either!
</p>
{%/if}
</div>
<div class="subscribe">
<h3>Subscribe to a recurring donation</h3>
<p>
My e-mail address is...
<input type="text" id="field_email" placeholder="you@provider.com">
</p>
<p>
... and I'd like to pledge
<select id="field_currency">
<option value="usd">$</option>
<option value="eur">€</option>
<option value="btc">BTC</option>
</select>
<input type="text" id="field_amount" value="5.00">
a month.
</p>
<p class="pledge">
<button class="green-button" id="button_subscribe">Pledge!</button>
</p>
{%if can-donate-once == true}
<h3 class="section">One-off donation</h3>
<p>
<img src="/static/images/paypal.png">
<img src="/static/images/bitcoin.png" style="margin-left: 16px;">
</p>
{%/if}
</div>
<div class="clear"></div>
<div class="more">
<div class="wrapper">
{%if can-donate-once == false}
<h3>Why use ReDonate?</h3>
<p>
Many donors wish to donate to a cause at regular intervals, but do not want to have their account charged automatically.
To take away the hassle of having to remember to donate every month, ReDonate was born. No need to remember a
donation schedule, and no automatic charges either!
</p>
{%/if}
<h3>Is this safe?</h3>
<p class="leader">
Short answer: Yes.
</p>
<p>
The longer answer: ReDonate does not actually process any payments. You'll be using the same payment processor
that you'd be using to donate otherwise - we're just here to remind you. We do not have access to any of your
accounts, nor can we control where donations go.
</p>
<p>
We also have no reason to sell or otherwise misuse your data. ReDonate is an entirely non-profit project. It's
supported by the <a href="http://cryto.net/">Cryto Coding Collective</a>, a collective of developers that has
been running off <a href="http://cryto.net/donate">donations</a> for nearly two years now. We won't use your
e-mail address for anything other than sending you reminders, and every e-mail includes an unsubscribe link.
</p>
</div>
</div>
</div>
</div>
</body>
</html>
<div class="details">
<h3>How does it work?</h3>
<p class="leader">
Most recurring services - even charities! - will automatically charge your account every month.
ReDonate is different.
</p>
<p>
When you subscribe to a ReDonate campaign, you only have to tell us how much you want to donate, and
your e-mail address. Every month, we will e-mail you to remind you of your pledge. The e-mail you
receive will include a list of payment methods, and an unsubscribe link.
</p>
<p>
What this means in simple terms is that you'll <strong>never</strong> be automatically charged for a donation -
you can decide to cancel your subscription at any time, without hassle, and without "oops, I forgot to cancel"
moments!
</p>
{%if can-donate-once == true}
<h3>Why use ReDonate?</h3>
<p>
Many donors wish to donate to a cause at regular intervals, but do not want to have their account charged automatically.
To take away the hassle of having to remember to donate every month, ReDonate was born. No need to remember a
donation schedule, and no automatic charges either!
</p>
{%/if}
</div>
<div class="subscribe">
<h3>Subscribe to a recurring donation</h3>
<p>
My e-mail address is...
<input type="text" id="field_email" placeholder="you@provider.com">
</p>
<p>
... and I'd like to pledge
<select id="field_currency">
<option value="usd">$</option>
<option value="eur">€</option>
<option value="btc">BTC</option>
</select>
<input type="text" id="field_amount" value="5.00">
a month.
</p>
<p class="pledge">
<button class="green-button" id="button_subscribe">Pledge!</button>
</p>
{%if can-donate-once == true}
<h3 class="section">One-off donation</h3>
<p>
<img src="/static/images/paypal.png">
<img src="/static/images/bitcoin.png" style="margin-left: 16px;">
</p>
{%/if}
</div>
<div class="clear"></div>
<div class="more">
<div class="wrapper">
{%if can-donate-once == false}
<h3>Why use ReDonate?</h3>
<p>
Many donors wish to donate to a cause at regular intervals, but do not want to have their account charged automatically.
To take away the hassle of having to remember to donate every month, ReDonate was born. No need to remember a
donation schedule, and no automatic charges either!
</p>
{%/if}
<h3>Is this safe?</h3>
<p class="leader">
Short answer: Yes.
</p>
<p>
The longer answer: ReDonate does not actually process any payments. You'll be using the same payment processor
that you'd be using to donate otherwise - we're just here to remind you. We do not have access to any of your
accounts, nor can we control where donations go.
</p>
<p>
We also have no reason to sell or otherwise misuse your data. ReDonate is an entirely non-profit project. It's
supported by the <a href="http://cryto.net/">Cryto Coding Collective</a>, a collective of developers that has
been running off <a href="http://cryto.net/donate">donations</a> for nearly two years now. We won't use your
e-mail address for anything other than sending you reminders, and every e-mail includes an unsubscribe link.
</p>
</div>
</div>

@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href='http://fonts.googleapis.com/css?family=Lato:400,700|Cantarell:400,700|Roboto:500' rel='stylesheet' type='text/css'>
<link href="/static/css/style.css" rel="stylesheet">
<title>ReDonate :: {%?title}</title>
</head>
<body>
<div class="wrapper">
<div class="header">
<h1>
Re<span class="highlight">Donate</span>
</h1>
</div>
<div class="main">
{%?contents}
</div>
</div>
</body>
</html>
Loading…
Cancel
Save