From 6f94521d51d713801e80e7287f638b6748f92fcb Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sat, 16 Feb 2013 00:12:09 +0100 Subject: [PATCH] Base code for campaigns and users --- config.json => config.json.example | 2 + public_html/classes/campaign.php | 55 +++++ public_html/classes/user.php | 155 ++++++++++++ public_html/index.php | 15 +- public_html/modules/landing.php | 26 ++ public_html/rewrite.php | 46 ++++ public_html/static/css/style.css | 196 +++++++++++++++ public_html/templates/404.tpl | 15 ++ public_html/templates/landing.tpl | 379 +++++++---------------------- public_html/templates/layout.tpl | 21 ++ 10 files changed, 601 insertions(+), 309 deletions(-) rename config.json => config.json.example (91%) create mode 100644 public_html/classes/campaign.php create mode 100644 public_html/classes/user.php create mode 100644 public_html/modules/landing.php create mode 100644 public_html/rewrite.php create mode 100644 public_html/static/css/style.css create mode 100644 public_html/templates/404.tpl create mode 100644 public_html/templates/layout.tpl diff --git a/config.json b/config.json.example similarity index 91% rename from config.json rename to config.json.example index da71248..dca6c8a 100644 --- a/config.json +++ b/config.json.example @@ -20,6 +20,8 @@ "port": 11211 }, "class_map": { + "user": "User", + "campaign": "Campaign" }, "components": [ "router", diff --git a/public_html/classes/campaign.php b/public_html/classes/campaign.php new file mode 100644 index 0000000..a806d82 --- /dev/null +++ b/public_html/classes/campaign.php @@ -0,0 +1,55 @@ + 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); + } +} diff --git a/public_html/classes/user.php b/public_html/classes/user.php new file mode 100644 index 0000000..b37cc98 --- /dev/null +++ b/public_html/classes/user.php @@ -0,0 +1,155 @@ + 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); + } +} diff --git a/public_html/index.php b/public_html/index.php index 87af2cf..877885f 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -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"); diff --git a/public_html/modules/landing.php b/public_html/modules/landing.php new file mode 100644 index 0000000..7559b53 --- /dev/null +++ b/public_html/modules/landing.php @@ -0,0 +1,26 @@ +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()); +} diff --git a/public_html/rewrite.php b/public_html/rewrite.php new file mode 100644 index 0000000..a2e8f62 --- /dev/null +++ b/public_html/rewrite.php @@ -0,0 +1,46 @@ +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))); diff --git a/public_html/static/css/style.css b/public_html/static/css/style.css new file mode 100644 index 0000000..66b1f92 --- /dev/null +++ b/public_html/static/css/style.css @@ -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; +} diff --git a/public_html/templates/404.tpl b/public_html/templates/404.tpl new file mode 100644 index 0000000..9bead38 --- /dev/null +++ b/public_html/templates/404.tpl @@ -0,0 +1,15 @@ +

Page not found.

+ +

+ Sorry, we could not find the page you requested. But while you're here anyway, why not introduce ourselves? +

+ +

+ ReDonate is recurring contributions, done right. Set up a campaign, and accept monthly donations from contributors that wish + to support you, without ever automatically charging an account. ReDonate is friendly to you, and your + donors. +

+ +

+ Gotten curious? Visit our homepage to learn more... +

diff --git a/public_html/templates/landing.tpl b/public_html/templates/landing.tpl index 3b8bf79..b9179c0 100644 --- a/public_html/templates/landing.tpl +++ b/public_html/templates/landing.tpl @@ -1,296 +1,85 @@ - - - - - - - - -
-
-

- ReDonate -

-
-
-

Contribute to AnonNews monthly, no automatic charges.

- -
-

How does it work?

-

- Most recurring services - even charities! - will automatically charge your account every month. - ReDonate is different. -

-

- 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. -

-

- What this means in simple terms is that you'll never 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! -

- - {%if can-donate-once == true} -

Why use ReDonate?

-

- 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! -

- {%/if} -
- -
-
-
- {%if can-donate-once == false} -

Why use ReDonate?

-

- 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! -

- {%/if} - -

Is this safe?

-

- Short answer: Yes. -

-

- 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. -

-

- We also have no reason to sell or otherwise misuse your data. ReDonate is an entirely non-profit project. It's - supported by the Cryto Coding Collective, a collective of developers that has - been running off donations 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. -

-
-
-
-
- - +
+

How does it work?

+

+ Most recurring services - even charities! - will automatically charge your account every month. + ReDonate is different. +

+

+ 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. +

+

+ What this means in simple terms is that you'll never 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! +

+ + {%if can-donate-once == true} +

Why use ReDonate?

+

+ 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! +

+ {%/if} +
+
+

Subscribe to a recurring donation

+

+ My e-mail address is... + +

+

+ ... and I'd like to pledge + + + a month. +

+

+ +

+ + {%if can-donate-once == true} +

One-off donation

+

+ + +

+ {%/if} +
+
+
+
+ {%if can-donate-once == false} +

Why use ReDonate?

+

+ 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! +

+ {%/if} + +

Is this safe?

+

+ Short answer: Yes. +

+

+ 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. +

+

+ We also have no reason to sell or otherwise misuse your data. ReDonate is an entirely non-profit project. It's + supported by the Cryto Coding Collective, a collective of developers that has + been running off donations 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. +

+
+
diff --git a/public_html/templates/layout.tpl b/public_html/templates/layout.tpl new file mode 100644 index 0000000..5492381 --- /dev/null +++ b/public_html/templates/layout.tpl @@ -0,0 +1,21 @@ + + + + + + + ReDonate :: {%?title} + + +
+
+

+ ReDonate +

+
+
+ {%?contents} +
+
+ +