mirror of
https://github.com/standardebooks/web.git
synced 2025-07-06 23:00:28 -04:00
Add Db::QueryBool() and some code style updates
This commit is contained in:
parent
854ec6b9df
commit
44901cf3e2
9 changed files with 100 additions and 77 deletions
18
lib/Db.php
18
lib/Db.php
|
@ -54,4 +54,22 @@ class Db{
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single boolean value for the first column database query result.
|
||||
*
|
||||
* This is useful for queries that return a boolean as a result, like `select exists()`.
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $args
|
||||
*/
|
||||
public static function QueryBool(string $query, array $args = []): bool{
|
||||
$result = $GLOBALS['DbConnection']->Query($query, $args);
|
||||
|
||||
if(sizeof($result) > 0){
|
||||
return (bool)current((array)$result[0]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
15
lib/Exceptions/MultiSelectMethodNotFoundException.php
Normal file
15
lib/Exceptions/MultiSelectMethodNotFoundException.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?
|
||||
namespace Exceptions;
|
||||
|
||||
class MultiSelectMethodNotFoundException extends AppException{
|
||||
public function __construct(string $class = ''){
|
||||
if($class != ''){
|
||||
$this->message = 'Multi table select attempted, but class ' . $class . ' doesn\'t have a FromMultiTableRow() method.';
|
||||
}
|
||||
else{
|
||||
$this->message = 'Multi table select attempted, but the class doesn\'t have a FromMultiTableRow() method.';
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
|
@ -2,11 +2,10 @@
|
|||
<?
|
||||
require_once('/standardebooks.org/web/lib/Core.php');
|
||||
|
||||
// Delete unconfirmed newsletter subscribers who are more than a week old
|
||||
// Delete unconfirmed newsletter subscribers who are more than a week old.
|
||||
Db::Query('
|
||||
DELETE
|
||||
from NewsletterSubscriptions
|
||||
where IsConfirmed = false
|
||||
and datediff(utc_timestamp(), Created) >= 7
|
||||
');
|
||||
?>
|
||||
|
|
|
@ -41,7 +41,7 @@ function CreateOpdsCollectionFeed(string $name, string $url, string $description
|
|||
$collator = collator_create('en_US'); // Used for sorting letters with diacritics like in author names
|
||||
usort($collections, function($a, $b) use($collator){ return $collator->compare($a['sortedname'], $b['sortedname']); });
|
||||
|
||||
// Create the collections navigation document
|
||||
// Create the collections navigation document.
|
||||
$collectionNavigationEntries = [];
|
||||
foreach($collections as $collection){
|
||||
$entry = new OpdsNavigationEntry($collection['name'], str_replace('%s', $collection['name'], $description), $url . '/' . $collection['id'], $now, 'subsection', 'navigation');
|
||||
|
@ -52,7 +52,7 @@ function CreateOpdsCollectionFeed(string $name, string $url, string $description
|
|||
$collectionsFeed->Subtitle = 'Browse Standard Ebooks by ' . $name . '.';
|
||||
SaveFeed($collectionsFeed, $force, null, null, $now);
|
||||
|
||||
// Now generate each individual collection feed
|
||||
// Now generate each individual collection feed.
|
||||
foreach($collectionNavigationEntries as $collectionNavigationEntry){
|
||||
$id = basename($collectionNavigationEntry->Id);
|
||||
usort($ebooks[$id], 'SortByUpdatedDesc');
|
||||
|
@ -88,7 +88,7 @@ foreach($dirs as $dir){
|
|||
}
|
||||
}
|
||||
|
||||
// Iterate over all ebooks to build the various feeds
|
||||
// Iterate over all ebooks to build the various feeds.
|
||||
foreach(Library::GetEbooksFromFilesystem($webRoot) as $ebook){
|
||||
$allEbooks[] = $ebook;
|
||||
$newestEbooks[] = $ebook;
|
||||
|
@ -116,7 +116,7 @@ $newestEbooks = array_slice($newestEbooks, 0, $ebooksPerNewestEbooksFeed);
|
|||
|
||||
$now = new DateTimeImmutable();
|
||||
|
||||
// Create OPDS feeds
|
||||
// Create OPDS feeds.
|
||||
$opdsRootEntries = [
|
||||
new OpdsNavigationEntry(
|
||||
'Newest Standard Ebooks',
|
||||
|
@ -159,44 +159,44 @@ $opdsRootEntries = [
|
|||
$opdsRoot = new OpdsNavigationFeed('Standard Ebooks', 'The Standard Ebooks catalog.', '/feeds/opds', $webRoot . '/feeds/opds/index.xml', $opdsRootEntries, null);
|
||||
SaveFeed($opdsRoot, $force, null, null, $now);
|
||||
|
||||
// Create the Subjects feeds
|
||||
// Create the Subjects feeds.
|
||||
CreateOpdsCollectionFeed('subject', '/feeds/opds/subjects', 'Standard Ebooks in the “%s” subject, most-recently-released first.', $subjects, $ebooksBySubject, $now, $webRoot, $opdsRoot, $force);
|
||||
|
||||
// Create the Collections feeds
|
||||
// Create the Collections feeds.
|
||||
CreateOpdsCollectionFeed('collection', '/feeds/opds/collections', 'Standard Ebooks in the “%s” collection, most-recently-released first.', $collections, $ebooksByCollection, $now, $webRoot, $opdsRoot, $force);
|
||||
|
||||
// Create the Author feeds
|
||||
// Create the Author feeds.
|
||||
CreateOpdsCollectionFeed('author', '/feeds/opds/authors', 'Standard Ebooks by %s, most-recently-released first.', $authors, $ebooksByAuthor, $now, $webRoot, $opdsRoot, $force);
|
||||
|
||||
// Create the All feed
|
||||
// Create the All feed.
|
||||
$allFeed = new OpdsAcquisitionFeed('All Standard Ebooks', 'All Standard Ebooks, most-recently-updated first. This is a Complete Acquisition Feed as defined in OPDS 1.2 §2.5.', '/feeds/opds/all', $webRoot . '/feeds/opds/all.xml', $allEbooks, $opdsRoot, true);
|
||||
SaveFeed($allFeed, $force, null, null, $now);
|
||||
|
||||
// Create the Newest feed
|
||||
// Create the Newest feed.
|
||||
$newestFeed = new OpdsAcquisitionFeed('Newest Standard Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/opds/new-releases', $webRoot . '/feeds/opds/new-releases.xml', $newestEbooks, $opdsRoot);
|
||||
SaveFeed($newestFeed, $force, null, null, $now);
|
||||
|
||||
|
||||
|
||||
// Create RSS/Atom feeds
|
||||
// Create RSS/Atom feeds.
|
||||
|
||||
// Create the RSS All feed
|
||||
// Create the RSS All feed.
|
||||
$allRssFeed = new RssFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/rss/all', $webRoot . '/feeds/rss/all.xml', $allEbooks);
|
||||
SaveFeed($allRssFeed, $force, null, null);
|
||||
|
||||
// Create the RSS Newest feed
|
||||
// Create the RSS Newest feed.
|
||||
$newestRssFeed = new RssFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/rss/new-releases', $webRoot . '/feeds/rss/new-releases.xml', $newestEbooks);
|
||||
SaveFeed($newestRssFeed, $force, null, null);
|
||||
|
||||
// Create the Atom All feed
|
||||
// Create the Atom All feed.
|
||||
$allAtomFeed = new AtomFeed('Standard Ebooks - All Ebooks', 'All Standard Ebooks, most-recently-released first.', '/feeds/atom/all', $webRoot . '/feeds/atom/all.xml', $allEbooks);
|
||||
SaveFeed($allAtomFeed, $force, null, null, $now);
|
||||
|
||||
// Create the Atom Newest feed
|
||||
// Create the Atom Newest feed.
|
||||
$newestAtomFeed = new AtomFeed('Standard Ebooks - Newest Ebooks', 'The ' . number_format($ebooksPerNewestEbooksFeed) . ' latest Standard Ebooks, most-recently-released first.', '/feeds/atom/new-releases', $webRoot . '/feeds/atom/new-releases.xml', $newestEbooks);
|
||||
SaveFeed($newestAtomFeed, $force, null, null, $now);
|
||||
|
||||
// Generate each individual subject feed
|
||||
// Generate each individual subject feed.
|
||||
foreach($ebooksBySubject as $subject => $ebooks){
|
||||
usort($ebooks, 'SortByUpdatedDesc');
|
||||
|
||||
|
@ -210,7 +210,7 @@ foreach($ebooksBySubject as $subject => $ebooks){
|
|||
SaveFeed($subjectAtomFeed, $force, $subjects[$subject]['name'], $subjects[$subject]['sortedname'], $now);
|
||||
}
|
||||
|
||||
// Generate each individual collection feed
|
||||
// Generate each individual collection feed.
|
||||
foreach($ebooksByCollection as $collection => $ebooks){
|
||||
usort($ebooks, 'SortByUpdatedDesc');
|
||||
|
||||
|
@ -226,7 +226,7 @@ foreach($ebooksByCollection as $collection => $ebooks){
|
|||
SaveFeed($collectionAtomFeed, $force, $collections[$collection]['name'], $collections[$collection]['sortedname'], $now);
|
||||
}
|
||||
|
||||
// Generate each individual author feed
|
||||
// Generate each individual author feed.
|
||||
foreach($ebooksByAuthor as $collection => $ebooks){
|
||||
usort($ebooks, 'SortByUpdatedDesc');
|
||||
|
||||
|
@ -240,8 +240,8 @@ foreach($ebooksByAuthor as $collection => $ebooks){
|
|||
SaveFeed($collectionAtomFeed, $force, $authors[$collection]['name'], $authors[$collection]['sortedname'], $now);
|
||||
}
|
||||
|
||||
// Set ownership and permissions
|
||||
// We don't use PHP's built in chown/chmod chmod can't accept strings
|
||||
// Set ownership and permissions.
|
||||
// We don't use PHP's built in chown/chmod chmod can't accept strings.
|
||||
// The `chmod +X` command, with a capital X, makes only matched directories executable.
|
||||
exec('sudo chown --preserve-root --recursive se:committers ' . escapeshellarg($webRoot) . '/feeds/*/*.xml');
|
||||
exec('sudo chown --preserve-root --recursive se:committers ' . escapeshellarg($webRoot) . '/feeds/*/*/*.xml');
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#!/usr/bin/php
|
||||
<?
|
||||
// Note: This script must be run as a user with a $HOME directory,
|
||||
// otherwise Firefox won't be able to start with a profile.
|
||||
// Note: This script must be run as a user with a $HOME directory, otherwise Firefox won't be able to start with a profile.
|
||||
|
||||
// FA is unreliable in the email notifications it sends. They are often missing.
|
||||
// This script gets a list of FA transactions directly from their website.
|
||||
// It tracks the last transaction it saw in a temp file and won't go past that.
|
||||
// If there is no temp file, it gets all transactions from today, and writes the temp file with the last transaction it saw.
|
||||
// Any transactions that the script finds and which don't already exist, are added to the database as pending payments.
|
||||
// After that, the /scripts/process-pending-payments script will pick them up and do accounting/patron logic.
|
||||
/**
|
||||
* FA is unreliable in the email notifications it sends. They are often missing.
|
||||
* This script gets a list of FA transactions directly from their website.
|
||||
* It tracks the last transaction it saw in a temp file and won't go past that.
|
||||
* If there is no temp file, it gets all transactions from today, and writes the temp file with the last transaction it saw.
|
||||
* Any transactions that the script finds and which don't already exist, are added to the database as pending payments.
|
||||
* After that, the `/scripts/process-pending-payments` script will pick them up and do accounting/patron logic.
|
||||
*/
|
||||
|
||||
use Facebook\WebDriver\WebDriverBy;
|
||||
use Facebook\WebDriver\WebDriverExpectedCondition;
|
||||
|
@ -25,10 +26,10 @@ use function Safe\set_time_limit;
|
|||
|
||||
require_once('/standardebooks.org/web/lib/Core.php');
|
||||
|
||||
// Disable script timeout because Selenium is very slow
|
||||
// Disable script timeout because Selenium is very slow.
|
||||
set_time_limit(0);
|
||||
|
||||
// Initialize the Selenium driver
|
||||
// Initialize the Selenium driver.
|
||||
putenv('WEBDRIVER_FIREFOX_DRIVER=' . SITE_ROOT . '/config/selenium/geckodriver-0.31.0');
|
||||
|
||||
$firefoxOptions = new FirefoxOptions();
|
||||
|
@ -53,7 +54,7 @@ $faItemsPerPage = 20; // How many items are on a full page of FA results?
|
|||
// If /tmp/last-fa-donation doesn't exist, get all transactions from today and create the file.
|
||||
|
||||
function InsertTransaction(string $transactionId): bool{
|
||||
$exists = Db::QueryInt('SELECT exists(
|
||||
$exists = Db::QueryBool('SELECT exists(
|
||||
select *
|
||||
from
|
||||
( select 1
|
||||
|
@ -104,7 +105,7 @@ try{
|
|||
|
||||
while($getMoreTransactions){
|
||||
if($page > 5){
|
||||
// Safety valve for runaway logic
|
||||
// Safety valve for runaway logic.
|
||||
throw new Exception('Error: went past page 5 of Fractured Atlas results.');
|
||||
}
|
||||
|
||||
|
@ -113,24 +114,24 @@ try{
|
|||
$driver->get('https://fundraising.fracturedatlas.org/admin/general_support/donations?page=' . $page);
|
||||
|
||||
// Check if we need to log in to FA.
|
||||
// Wait until the <body> element is visible, then check the current URL
|
||||
// Wait until the <body> element is visible, then check the current URL.
|
||||
$driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('/html/body')));
|
||||
if(stripos($driver->getCurrentUrl(), 'auth0.com')){
|
||||
$log->Write('Logging in to Fractured Atlas ...');
|
||||
|
||||
// We were redirected to the login page, so try to log in
|
||||
// We were redirected to the login page, so try to log in.
|
||||
$emailField = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//input[@type="email"]')));
|
||||
$passwordField = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//input[@type="password"]')));
|
||||
$submitButton = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//button[@type="submit"]')));
|
||||
|
||||
// Fill out and submit the form
|
||||
// Fill out and submit the form.
|
||||
$emailField->sendKeys($faUsername);
|
||||
$passwordField->sendKeys($faPassword);
|
||||
$submitButton->click();
|
||||
}
|
||||
|
||||
// Wait until the page finishes loading.
|
||||
// We have to expand the row before we can select its contents, so click the 'expand' button once it's visible
|
||||
// We have to expand the row before we can select its contents, so click the 'expand' button once it's visible.
|
||||
try{
|
||||
$toggleButton = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//button[contains(@class, "button-toggle")]')));
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ try{
|
|||
continue;
|
||||
}
|
||||
|
||||
// If the last seen transaction ID is null, get everything from today
|
||||
// If the last seen transaction ID is null, get everything from today.
|
||||
if($lastSeenTransactionId === null){
|
||||
$elements = $driver->findElements(WebDriverBy::xpath('//td[preceding-sibling::th[normalize-space(.) = "ID"]][parent::tr[preceding-sibling::tr[./td[normalize-space(.) = "' . $today . '"]]]]'));
|
||||
|
||||
|
@ -166,8 +167,8 @@ try{
|
|||
}
|
||||
}
|
||||
else{
|
||||
// Last seen transaction ID is not null, get everything from that ID
|
||||
// Get a list of transaction IDs on the page
|
||||
// Last seen transaction ID is not null, get everything from that ID.
|
||||
// Get a list of transaction IDs on the page.
|
||||
$elements = $driver->findElements(WebDriverBy::xpath('//td[preceding-sibling::th[normalize-space(.) = "ID"]]'));
|
||||
for($i = 0; $i < sizeof($elements); $i++){
|
||||
$td = $elements[$i];
|
||||
|
@ -218,4 +219,3 @@ catch(Exception $ex){
|
|||
finally{
|
||||
$driver?->quit();
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -18,5 +18,3 @@ if(sizeof($pendingPayments) > 0){
|
|||
$em->TextBody = Template::EmailAdminUnprocessedDonationsText();
|
||||
$em->Send();
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/php
|
||||
<?
|
||||
// Note: This script must be run as a user with a $HOME directory,
|
||||
// otherwise Firefox won't be able to start with a profile.
|
||||
// Note: This script must be run as a user with a $HOME directory, otherwise Firefox won't be able to start with a profile.
|
||||
|
||||
use Facebook\WebDriver\WebDriverBy;
|
||||
use Facebook\WebDriver\WebDriverExpectedCondition;
|
||||
|
@ -17,21 +16,21 @@ use function Safe\set_time_limit;
|
|||
|
||||
require_once('/standardebooks.org/web/lib/Core.php');
|
||||
|
||||
// Disable script timeout because Selenium is very slow
|
||||
// Disable script timeout because Selenium is very slow.
|
||||
set_time_limit(0);
|
||||
|
||||
// Initialize the Selenium driver
|
||||
putenv('WEBDRIVER_FIREFOX_DRIVER=' . SITE_ROOT . '/config/selenium/geckodriver-0.31.0');
|
||||
|
||||
$firefoxOptions = new FirefoxOptions();
|
||||
$firefoxOptions->addArguments(['-headless']); // WARNING: Only one dash!
|
||||
$firefoxOptions->addArguments(['-headless']); // **Warning**: Only one dash!
|
||||
|
||||
$capabilities = DesiredCapabilities::firefox();
|
||||
$capabilities->setCapability(FirefoxOptions::CAPABILITY, $firefoxOptions);
|
||||
|
||||
$driver = null;
|
||||
$log = new Log(DONATIONS_LOG_FILE_PATH);
|
||||
$lastMonth = (new DateTimeImmutable())->sub(new DateInterval('P45D')); // 45 days, a 15 day grace period before Patrons Circle members are dropped off
|
||||
$lastMonth = (new DateTimeImmutable())->sub(new DateInterval('P45D')); // 45 days, a 15 day grace period before Patrons Circle members are dropped off.
|
||||
$lastYear = (new DateTimeImmutable())->sub(new DateInterval('P1Y'));
|
||||
$faUsername = get_cfg_var('se.secrets.fractured_atlas.username');
|
||||
$faPassword = get_cfg_var('se.secrets.fractured_atlas.password');
|
||||
|
@ -61,7 +60,7 @@ Db::Query('
|
|||
Db::Query('commit');
|
||||
|
||||
if(sizeof($pendingPayments) == 0){
|
||||
// Don't start the very slow Selenium driver if we have nothing to process
|
||||
// Don't start the very slow Selenium driver if we have nothing to process.
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -73,13 +72,13 @@ try{
|
|||
if($pendingPayment->Processor == PaymentProcessorType::FracturedAtlas){
|
||||
$log->Write('Processing donation ' . $pendingPayment->TransactionId . ' ...');
|
||||
|
||||
if(Db::QueryInt('
|
||||
if(Db::QueryBool('
|
||||
SELECT exists(
|
||||
select *
|
||||
from Payments
|
||||
where TransactionId = ?
|
||||
)
|
||||
', [$pendingPayment->TransactionId]) > 0){
|
||||
', [$pendingPayment->TransactionId])){
|
||||
$log->Write('Donation already exists in database.');
|
||||
continue;
|
||||
}
|
||||
|
@ -87,24 +86,24 @@ try{
|
|||
$driver->get('https://fundraising.fracturedatlas.org/admin/donations?query=' . $pendingPayment->TransactionId);
|
||||
|
||||
// Check if we need to log in to FA.
|
||||
// Wait until the <body> element is visible, then check the current URL
|
||||
// Wait until the <body> element is visible, then check the current URL.
|
||||
$driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('/html/body')));
|
||||
if(stripos($driver->getCurrentUrl(), 'auth0.com')){
|
||||
$log->Write('Logging in to Fractured Atlas ...');
|
||||
|
||||
// We were redirected to the login page, so try to log in
|
||||
// We were redirected to the login page, so try to log in.
|
||||
$emailField = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//input[@type="email"]')));
|
||||
$passwordField = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//input[@type="password"]')));
|
||||
$submitButton = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//button[@type="submit"]')));
|
||||
|
||||
// Fill out and submit the form
|
||||
// Fill out and submit the form.
|
||||
$emailField->sendKeys($faUsername);
|
||||
$passwordField->sendKeys($faPassword);
|
||||
$submitButton->click();
|
||||
}
|
||||
|
||||
// Wait until the page finishes loading.
|
||||
// We have to expand the row before we can select its contents, so click the 'expand' button once it's visible
|
||||
// We have to expand the row before we can select its contents, so click the 'expand' button once it's visible.
|
||||
try{
|
||||
$toggleButton = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//button[contains(@class, "button-toggle")]')));
|
||||
}
|
||||
|
@ -116,8 +115,7 @@ try{
|
|||
|
||||
// Our target row is now visible, extract the data!
|
||||
|
||||
// In the FA donations table, there is a header row, and an expandable details row. The header row tells us if the donation is recurring,
|
||||
// and the details row has the rest of the information
|
||||
// In the FA donations table, there is a header row, and an expandable details row. The header row tells us if the donation is recurring, and the details row has the rest of the information.
|
||||
$detailsRow = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//tr[starts-with(@id, "expanded") and contains(@id, "' . $pendingPayment->TransactionId . '")]')));
|
||||
|
||||
$headerRow = $driver->findElement(WebDriverBy::xpath('//tr[not(starts-with(@id, "expanded")) and contains(@id, "' . $pendingPayment->TransactionId . '")]'));
|
||||
|
@ -145,13 +143,13 @@ try{
|
|||
$payment->IsMatchingDonation = true;
|
||||
}
|
||||
|
||||
// We can get here via an AOGF donation that is anonymous
|
||||
// We can get here via an AOGF donation that is anonymous.
|
||||
if($payment->User->Email == 'Not provided' || $payment->User->Email == ''){
|
||||
$payment->User = null;
|
||||
}
|
||||
}
|
||||
catch(Exception){
|
||||
// Anonymous donations don't have these elements present and will throw an exception
|
||||
// Anonymous donations don't have these elements present and will throw an exception.
|
||||
$payment->User = null;
|
||||
}
|
||||
|
||||
|
@ -171,12 +169,12 @@ try{
|
|||
$payment->User = null;
|
||||
}
|
||||
|
||||
// All set - create the payment
|
||||
// All set - create the payment.
|
||||
try{
|
||||
$payment->Create();
|
||||
}
|
||||
catch(Exceptions\PaymentExistsException){
|
||||
// Payment already exists, just continue
|
||||
// Payment already exists, just continue.
|
||||
$log->Write('Donation already in database.');
|
||||
continue;
|
||||
}
|
||||
|
@ -202,15 +200,15 @@ try{
|
|||
// This payment is eligible for the Patrons Circle!
|
||||
if($payment->User !== null){
|
||||
// Are we already a patron?
|
||||
if(Db::QueryInt('
|
||||
if(!Db::QueryBool('
|
||||
SELECT exists(
|
||||
select *
|
||||
from Patrons
|
||||
where UserId = ?
|
||||
and Ended is null
|
||||
)
|
||||
', [$payment->UserId]) == 0){
|
||||
// Not a patron yet, add them to the Patrons Circle
|
||||
', [$payment->UserId])){
|
||||
// Not a patron yet, add them to the Patrons Circle.
|
||||
|
||||
$patron = new Patron();
|
||||
$patron->UserId = $payment->UserId;
|
||||
|
@ -259,7 +257,7 @@ try{
|
|||
}
|
||||
}
|
||||
else{
|
||||
// Not eligible to be a patron; send a thank you email anyway, but only if this is a non-recurring donation, or if it's their very first recurring donation
|
||||
// Not eligible to be a patron; send a thank you email anyway, but only if this is a non-recurring donation, or if it's their very first recurring. donation
|
||||
if($payment->User !== null){
|
||||
$previousPaymentCount = Db::QueryInt('
|
||||
SELECT count(*)
|
||||
|
@ -268,7 +266,7 @@ try{
|
|||
and IsRecurring = true
|
||||
', [$payment->UserId]);
|
||||
|
||||
// We just added a payment to the system, so if this is their very first recurring payment, we expect the count to be exactly 1
|
||||
// We just added a payment to the system, so if this is their very first recurring payment, we expect the count to be exactly 1.
|
||||
if(!$payment->IsRecurring || $previousPaymentCount == 1){
|
||||
$log->Write('Sending thank you email to non-patron donor.');
|
||||
$em = new Email();
|
||||
|
@ -310,4 +308,3 @@ catch(Exception $ex){
|
|||
finally{
|
||||
$driver->quit();
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -8,8 +8,7 @@ use function Safe\file_get_contents;
|
|||
use function Safe\preg_match_all;
|
||||
use function Safe\shell_exec;
|
||||
|
||||
// Get a list of payments that are within 1 year / 45 days of today, and deactivate Patrons Circle members
|
||||
// who aren't in that list.
|
||||
// Get a list of payments that are within 1 year / 45 days of today, and deactivate Patrons Circle members who aren't in that list.
|
||||
// We give a 15 day grace period to Patrons Circle members because sometimes FA can be delayed in charging.
|
||||
|
||||
$now = new DateTimeImmutable();
|
||||
|
@ -35,8 +34,7 @@ $expiredPatrons = Db::Query('
|
|||
if(sizeof($expiredPatrons) > 0){
|
||||
$ebooksThisYear = 0;
|
||||
|
||||
// We can't use the Library class to get ebooks because this script is typically run via cron or CLI,
|
||||
// which doesn't have access PHP-FMP's APCu cache.
|
||||
// We can't use the Library class to get ebooks because this script is typically run via cron or CLI, which doesn't have access PHP-FMP's APCu cache.
|
||||
foreach(explode("\n", trim(shell_exec('find ' . EBOOKS_DIST_PATH . ' -name "content.opf"'))) as $filename){
|
||||
$metadata = file_get_contents($filename);
|
||||
|
||||
|
@ -66,7 +64,7 @@ if(sizeof($expiredPatrons) > 0){
|
|||
where UserId = ?
|
||||
', [$patron->UserId]);
|
||||
|
||||
// Email the patron to notify them their term has ended
|
||||
// Email the patron to notify them their term has ended.
|
||||
// Is the patron a recurring subscriber?
|
||||
$lastPayment = Db::Query('
|
||||
SELECT *
|
||||
|
@ -85,12 +83,12 @@ if(sizeof($expiredPatrons) > 0){
|
|||
$em->Subject = 'Will you still help us make free, beautiful digital literature?';
|
||||
|
||||
if($lastPayment[0]->IsRecurring){
|
||||
// Email recurring donors who have lapsed
|
||||
// Email recurring donors who have lapsed.
|
||||
$em->Body = Template::EmailPatronsCircleRecurringCompleted();
|
||||
$em->TextBody = Template::EmailPatronsCircleRecurringCompletedText();
|
||||
}
|
||||
else{
|
||||
// Email one time donors who have expired after one year
|
||||
// Email one time donors who have expired after one year.
|
||||
$em->Body = Template::EmailPatronsCircleCompleted(['ebooksThisYear' => $ebooksThisYear]);
|
||||
$em->TextBody = Template::EmailPatronsCircleCompletedText(['ebooksThisYear' => $ebooksThisYear]);
|
||||
}
|
||||
|
@ -99,5 +97,3 @@ if(sizeof($expiredPatrons) > 0){
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -24,7 +24,7 @@ try{
|
|||
// Certain user agents may bypass login entirely
|
||||
$isUserAgentAllowed = false;
|
||||
if(isset($_SERVER['HTTP_USER_AGENT'])){
|
||||
$isUserAgentAllowed = Db::QueryInt('
|
||||
$isUserAgentAllowed = Db::QueryBool('
|
||||
SELECT exists(
|
||||
select *
|
||||
from FeedUserAgents
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue