By Christopher Ambler on Wednesday, 18 November 2020
Posted in General Issues
Replies 15
Likes 0
Views 766
Votes 0
I've created two plans. A free one, and one with a one week trial and then monthly recurring. That works well. I assigned a menu item with no plan and no group (I'm not using groups) with an alias of "subscribe" - that works, too.

So when I go to https://www.thechipwitch.com/subscribe I'm presented with the two plans.

I select the free one, and it shows me a screen where the price is free and a button to register, as I'm not logged in when I'm doing this. I click the button and am presented with my registration screen.

I register, am redirected to the "thankyou" layout, and the email to confirm my new account arrives. Looking in the backend, the user has been created, is awaiting activation, and is in PayPlans as well with the free plan assigned.

So far, all works as expected.

BUT!

Now I go back to /subscribe, am presented with the two plans, and pick the free one again. Note that I've not yet confirmed my account, nor have I logged in. I'm still a guest!

Instead of being taken to the registration screen, I'm taken to checkout! It directs me to https://www.thechipwitch.com/subscribe/checkout/352R7N12R7Y3?skipInvoice=1 and that's the invoice number on my previous time through! And this time, instead of being the "free" page, it's giving the output for billing details!

Is the invoice number being stored in an uncleared session variable, perhaps? Because at this point, the system shouldn't treat me any differently than a fresh user. It should have no way of knowing the invoice number previously assigned.

This is very confusing!

(Note, if you use the links above, I've skinned the theme myself (using overrides in my template). No core code modified, of course.
Is it possible for you to provide us with the back end and FTP access to your site to check on this issue?
·
Wednesday, 18 November 2020 19:08
·
0 Likes
·
0 Votes
·
0 Comments
·
I'm not sure how back-end will help you. This is a stock install.

Right now, I'm looking at session storage as the problem. Here's what happens:

A user comes to the site and decides they want to sign up. They click on the register link and the Joomla registration code gets involved. In includes/registration/abstract.php there is an onAfterRoute that says, "if this is the register link and there is no planId, redirect the user to the plan selection. When the user picks a plan, this plan is stored in a session variable and the user is directed to the registration page, also with the planId as part of the request. So it's in two places. That's fine, onAfterRoute fires, and since there is a planId, the user is sent to the registration page.

But let's say the user then decides they want to do something else, or they want a different plan, so they go do that and then come back to register. They click on the register link again and are NOT presented with plans! The session variable is still there, so they're sent straight to registration with the plan pre-selected for them. There's no way to reset this other than waiting for the session variable to expire.

It would appear that the invoice number issue is this same issue. Once that invoice number is put in the session variable, the user can't get to a page to make a change. The session variable will always cause behavior that isn't wanted.

Even after completing the process, the plan session variable sticks around. If a user picks a plan, registers, and then wants to do it again, perhaps to create a second account), they can't! The plan is still in the session variable, and if they click on "register," they're taken straight to the registration page without the ability to pick a new plan.

My initial feeling is that this use of session variables is a bad idea. If a user doesn't follow the proper flow, with the plan and invoice IDs passed as input variables, tough for them! They should be taken back to the point where they have to pick them. It's the user's fault for abandoning the flow. Indeed, they may have done this on purpose. Keeping the data around in the session is just asking for problems.

Looking at the session, after completing the whole PayPlans flow, I still see:


[__payplans] => stdClass Object
(
[REGISTRATION_NEW_USER_ID] => 0
[PP_INVOICE_KEY] => 352R7N12R7U3
[PP_CHECKOUT_REGISTRATION] =>
)
·
Thursday, 19 November 2020 01:30
·
0 Likes
·
0 Votes
·
0 Comments
·
To further show the issue, I started the process of registering and then closed my browser. I then re-opened the browser and went back to my site. This keeps the session intact, as you know. Sure enough, here's the session upon return:


[__payplans] => stdClass Object
(
[REGISTRATION_NEW_USER_ID] => 0
[PP_INVOICE_KEY] => 352R7N12R7U0
[PP_CHECKOUT_REGISTRATION] =>
[REGISTRATION_PLAN_ID] => 1
[REGISTRATION_NEW_USER] =>
[REGISTRATION_USER_ID] => 0
)


Not only is the invoice key still there from the previous attempt, but the plan ID is set as well. This makes it IMPOSSIBLE to pick a new plan or start over. Worse, it gives no indication that PayPlans thinks it's just picking up where I left off (which isn't what is wanted at all).

So once again - the use of session variables is a bad idea. It locks in the flow and makes it almost impossible to account for users who bounce around before finishing.
·
Thursday, 19 November 2020 04:28
·
0 Likes
·
0 Votes
·
0 Comments
·
.
·
Thursday, 19 November 2020 04:30
·
0 Likes
·
0 Votes
·
0 Comments
·
Also, in going through this issue, and looking at redirects, I note that you add this:

&fromPayplans=1


to the joomla registration URL but never use it. This is in includes/registration/adapters/joomla.php's constructor when you create the URL. Can you add to your work ticket for using JRoute on this URL, also perhaps removing this? Keeping the URL clean would be good

Here's how my constructor looks right now:


public function __construct()
{
$this->url = JRoute::_('index.php?option=com_users&view=registration');

parent::__construct();
}
·
Thursday, 19 November 2020 04:52
·
0 Likes
·
0 Votes
·
0 Comments
·
I rewrote a bunch of this to test it, and my basic idea was to remove the session variables entirely. Unfortunately, this fails because you have to maintain some state through the Joomla registration and indicate to the adapter when it's done. But keeping things in the session the whole way through is what breaks this.

So what I did was refactor to pass the plan ID and invoice key around on the query string right until it's time to do the registration, and only THEN put them in the session and then clear them immediately after consuming them on return from registration (caught in the onAfterRoute, looking to see that a new user ID has been created).

At this point, then, the only issue is what happens to a user that picks a plan, gets the registration page after the plan/invoice IDs are put in session, and then they leave. When they come back, if the session variables are still there, and they hit the register link, the onAfterRoute is going to see them and take the user directly to the registration page. They may not expect this - nor may they want it, as they might want to restart.

The solution there is a gap timer, then. When putting the IDs away, a timestamp is put away as well. When onAfterRoute is hit, if the IDs are there and the new user ID isn't there (meaning onAfterSave wasn't called, so they didn't register), if that timer is more than ten seconds old, clear the IDs and send the user to the plan selection page again. If the new user ID is there, don't do that.

Now it all works as expected and no users will be confused about the flow!

All constrained to a "newjoomla.php" adapter
·
Thursday, 19 November 2020 07:42
·
0 Likes
·
0 Votes
·
0 Comments
·
NOW - Are we having fun yet?

My changes to make this all work are limited to joomla.php and abstract.php - so I could make a "newjoomla.php" registration adapter, override those functions in abstract.php that need changes, and drop it in the registrations/adapters folder and it would work.

One problem: you've marked a number of methods as final in abstract.php, which prevents overriding them, but I can just derive from PayPlans directly and implement the functions from scratch.

I've done that and it works. One file.

But then I'm presuming, as we mentioned, that on an upgrade my new adapter would be erased. But would it? On upgrade, do you really clear out the adapters directory of any files not in your manifest? Or would it be left there? If so... just dropping my new adapter in there would work, no?
·
Thursday, 19 November 2020 08:01
·
0 Likes
·
0 Votes
·
0 Comments
·
Hey Christopher,

Thank you for sharing the detailed information on this issue.
Yes, if you do change in core adapter, it will be lost on the payplans update.
Can you share us with file , so we can see what changes you made and will look into this.

Looking forward to your response.
·
Thursday, 19 November 2020 13:21
·
0 Likes
·
0 Votes
·
0 Comments
·
Will do. It's late here now, so I'll clean up my code tomorrow and let you guys see.

It's comprised of a new registration adapter, and its attendant display in themes/wireframe/registration - though I'm thinking that may not be necessary in core since that can go in the template override. Indeed, I could also spoof the namespace to load "joomla" instead of the name I gave my adapter.

And I wasn't talking about changing YOUR core adapter. I was asking if I put a NEW adapter in there that I wrote, it would seem to me that it wouldn't get deleted. Only core files get overwritten, but the installer doesn't delete anything. So I think that might be safe!
·
Thursday, 19 November 2020 15:10
·
0 Likes
·
0 Votes
·
0 Comments
·
Hey Christopher,

And I wasn't talking about changing YOUR core adapter. I was asking if I put a NEW adapter in there that I wrote, it would seem to me that it wouldn't get deleted. Only core files get overwritten, but the installer doesn't delete anything. So I think that might be safe!
Yes, if you write a new adapter it will be lost on PayPlans update.

Will do. It's late here now, so I'll clean up my code tomorrow and let you guys see.
Ok, Keep us updated.

Thank you for understanding!
·
Thursday, 19 November 2020 15:25
·
0 Likes
·
0 Votes
·
0 Comments
·
Okay, here is my registration adapter. Please note that I've called it "joomlastaq" (because my LLC is "Sleestaq" so I postfix most of my name spaces with "staq" :-))

This will require the appropriate entry in wireframe for the registration button, of course.


<?php
/**
* @package Sleestaq Joomla Registration Adapter for PayPlans
* @copyright Copyright (C) 2020 Sleestaq, LLC. All rights reserved.
* @license GNU/GPL, see LICENSE.php
* @version 1.0.0
*
* PayPlans is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
*
* See COPYRIGHT.php for copyright notices and details.
*/

use Joomla\Registry\Registry;

defined('_JEXEC') or die('Unauthorized Access');

class PPRegistrationJoomlastaq extends PayPlans
{
public string $type = 'joomlastaq';
public string $url;

private array $templateVars = array();
private string $sessionKey;

protected PPSession $session;

/**
* PPRegistrationJoomlaStaq constructor.
*
* @noinspection PhpUndefinedMethodInspection (session is called magically)
*
* @throws Exception
*
* @since 1.0.0
*/
public function __construct()
{
$urlBuild = JRoute::_('index.php?option=com_users&view=registration');

if (!$urlBuild) {

throw new Exception("JRoute Failure!");
}

$this->url = $urlBuild;

$this->session = PP::session();

parent::__construct();
}

/**
* Build the URL to hit the registration endpoint. Note that we add the plan_id and invoice_key to
* the URL. This lets us check for them in onAfterRoute and use them exclusively. If there is anything
* in the session object but not on the query string, we presume that the user is trying to hit the
* registration endpoint directly, and we don't like that.
*
* This function is called by our html() method which, in turn, is called by the theme when it wants to
* create a link or button to the Joomla registration component. The register view is going to call back
* into here to set the plan ID into the session and then call our redirect() function. Our redirect function
* will then call into Joomla's registration endpoint with the Plan ID and Invoice Key on the query string and,
* as outlined above, trigger our onAfterRoute to verify that this is, indeed, the case.
*
* To summarize:
*
* 1. Call Below creates a URL sufficient to call com_payplans/register with plan ID and invoice key
* on query string
* 2. That view then uses our adapter to call into Joomla's registration interface and
* 3. Our onAfterRoute validates the query string is intact
*
* @param PPInvoice $invoice
*
* @return array|string|string[]|null
*
* @since 1.0.0
*/
protected function getRegistrationUrl(PPInvoice $invoice)
{
$plan = $invoice->getPlan();

return PPR::_('index.php?option=com_payplans&view=register&plan_id=' . $plan->getId() . '&invoice_key=' . $invoice->getKey());
}

/**
* @param PPInvoice $invoice
*
* @return string
*
* @since 1.0.0
*/
public function html(PPInvoice $invoice): string
{
$this->set('url', $this->getRegistrationUrl($invoice));
$this->set('userId', $this->getNewUserId());

return $this->display('default');
}

/**
* Determines if this is currently the registration url.
*
* For Joomla, that's com_users and the registration view with
* no task. The "no task" part is important because we don't want
* to fire on the "complete" (or any other) task.
*
* @return bool
*
* @since 1.0.0
*/
protected function isRegistrationUrl(): bool
{
$option = (string)$this->input->get('option', '', 'default');
$view = (string)$this->input->get('view', '', 'default');
$task = (string)$this->input->get('task', '', 'default');

return ($option === 'com_users' && $view === 'registration' && !$task);
}

/**
* Provides assistance to the payment app to output contents.
* This is similar to the display method in views
*
* @return string
*
* @var string $namespace
*
* @since 1.0.0
*
* @noinspection PhpUndefinedMethodInspection (themes called magically)
*/
public function display(string $namespace = 'default'): string
{
$this->processData();

$namespace = 'site/registration/' . $this->type . '/' . $namespace;

$theme = PP::themes();
$theme->setVars($this->templateVars);

return $theme->output($namespace);
}

/**
* Retrieves the com_user's global configuration
*
* @return Registry
*
* @since 1.0.0
*/
protected function getJoomlaUsersParams(): Registry
{
static $params = null;

if (is_null($params)) {

$params = JComponentHelper::getParams('com_users');
}

return $params;
}

/**
* Resets the session data. Note that we only want to reset the invoice key and the
* plan ID. If we have set a user ID, we want that sticking around.
*
* @return void
*
* @since 1.0.0
*/
protected function resetSessionVars(): void
{
$this->session->set('PP_INVOICE_KEY', '');
$this->session->set('REGISTRATION_PLAN_ID', 0);

$this->session->set('REGISTRATION_EXPIRE', 0);
}

/**
* @param $id
*
* @return void
*
* @since 1.0.0
*/
public function setNewUserId($id): void
{
$this->session->set('REGISTRATION_NEW_USER_ID', $id);
}

/**
* @return int
*
* @since 1.0.0
*/
public function getNewUserId(): int
{
return (int)$this->session->get('REGISTRATION_NEW_USER_ID', 0);
}

/**
* The idea here is that when the user starts the process and the invoice key and plan ID are
* put in the session, they have ten minutes (600 seconds) to complete the process. If they leave
* and come back after ten minutes, they will be treated as new
*
* @var int
*
* @since 1.0.0
*/
protected static int $expireTimeInSeconds = 600;

/**
* Sets the invoice key in session so it can be tracked later
*
* @param string $key
*
* @return void
*
* @since 1.0.0
*/
public function setInvoiceKey(string $key): void
{
$this->session->set('PP_INVOICE_KEY', $key);
$this->session->set('REGISTRATION_EXPIRE', time() + self::$expireTimeInSeconds);
}

/**
* Retrieves the invoice id from the query or session
*
* @param bool $returnAnyway
*
* @return false|int
*
* @since 1.0.0
*/
public function getInvoiceId(bool $returnAnyway)
{
$key = $this->input->get('invoice_key', '', 'default');

if (empty($key)) {

$key = $this->session->get('PP_INVOICE_KEY', '');

if (!empty($key)) {

$expire = (int)$this->session->get('REGISTRATION_EXPIRE', 0);

if ($expire < time()) {

// There was a session variable, but it's since expired. So we clear
// it out and act as if it wasn't there.

$this->resetSessionVars();

$key = $returnAnyway ? $key : '';
}
}
}

if (empty($key)) {

return false;
}

return PP::getIdFromKey($key);
}

/**
* Sets the plan id into the session so that it can be tracked later
*
* @param int $planId
*
* @return void
*
* @since 1.0.0
*/
public function setPlanId(int $planId): void
{
$this->session->set('REGISTRATION_PLAN_ID', $planId);
$this->session->set('REGISTRATION_EXPIRE', time() + self::$expireTimeInSeconds);
}

/**
* Retrieves the plan id from the query or session
*
* @return int
*
* @since 1.0.0
*/
public function getPlanId(): int
{
$planId = (int)$this->input->get('plan_id', 0, 'int');

if ($planId === 0) {

$planId = (int)$this->session->get('REGISTRATION_PLAN_ID', 0);

if ($planId) {

$expire = (int)$this->session->get('REGISTRATION_EXPIRE', 0);

if ($expire < time()) {

// There was a session variable, but it's since expired. So we clear
// it out and act as if it wasn't there.

$this->resetSessionVars();

$planId = 00;
}
}
}

return $planId;
}

/**
* Determines whether need to update the current new registering user status
* Implemented by child
*
* @param $invoice
* @param $userId
*
* @return void
*
* @since 1.0.0
*/
protected function updateNewUserPublishState($invoice, $userId): void
{
// Do Nothing
}

/**
* Set the session key
*
* @param $key
*
* @return void
*
* @since 1.0.0
*/
public function setSessionKey($key): void
{
$this->sessionKey = $key;
}

/**
* Set the data stored from the previous session
*
* @return void
*
* @since 1.0.0
*/
public function processData(): void
{
$default = array(
'register_name' => '',
'register_username' => '',
'register_email' => '',
'address' => '',
'city' => '',
'state' => '',
'zip' => '',
'country' => ''
);

// Get data from previous session
$data = $this->session->get($this->sessionKey, '', 'payplans', true);

if ($data) {

$mapping = array('name', 'username', 'email', 'password', 'password2');

foreach ($mapping as $key) {

if (isset($data[$key])) {

// We do not want to display the password back to the form

if ($key === 'password' || $key === 'password2') {

unset($data[$key]);

continue;
}

$newKey = 'register_' . $key;
$data[$newKey] = $data[$key];

unset($data[$key]);
}
}
} else {

$data = $default;
}

$this->set('data', $data);
}

/**
* Provides assistance to the payment app to set variables which can be extracted with the @display method
*
* @param $key
* @param $value
*
* @return void
*
* @since 1.0.0
*/
public function set($key, $value): void
{
$this->templateVars[$key] = $value;
}

/**
* Redirects the request to the respective registration page
*
* @return void
*
* @since 1.0.0
*/
public function redirect(): void
{
$invoice_key = $this->input->get('invoice_key', 0, 'default');
$plan_id = $this->input->get('plan_id', 0, 'int');

$redirectTo = $this->url . "?plan_id=$plan_id&invoice_key=$invoice_key";

$this->app->redirect($redirectTo);

$this->app->close();
}

/**
* Redirects user to the checkout page
*
* @param $id
*
* @return void
*
* @since 1.0.0
*
* @noinspection PhpUndefinedMethodInspection (invoice called magically)
*/
public function redirectToCheckout($id): void
{
$invoice = PP::invoice($id);
$invoiceKey = $invoice->getKey();

// Directly go to thanks page for free invoice

if ($this->config->get('skip_free_invoices') && $invoice->isFree()) {

$redirect = PPR::_('index.php?option=com_payplans&task=checkout.confirm&invoice_key=' . $invoiceKey . '&app_id=0', false);

$this->app->redirect($redirect);

} else {

$redirect = PPR::_('index.php?option=com_payplans&view=checkout&invoice_key=' . $invoiceKey, false);

$this->app->redirect($redirect);
}

$this->app->close();
}

/**
* Redirects user to the plan page
*
* @return void
*
* @since 1.0.0
*/
public function redirectToPlans(): void
{
$redirect = PPR::_('index.php?option=com_payplans&view=plan', false);

$this->app->redirect($redirect);

$this->app->close();
}

/**
* Determines if the registration requires activation
*
* @return bool
*
* @since 1.0.0
*/
public function requireActivation(): bool
{
$usersConfig = $this->getJoomlaUsersParams();
$registrationType = (int)$usersConfig->get('useractivation');

return $registrationType === 1;
}

/**
* Determines if the registration requires admin for approval
*
* @return bool
*
* @since 1.0.0
*/
public function requireAdminActivation(): bool
{
$usersConfig = $this->getJoomlaUsersParams();
$registrationType = (int)$usersConfig->get('useractivation');

return $registrationType === 2;
}

/**
* Determines if the registration requires activation
*
* @return bool
*
* @since 1.0.0
*/
public function requireUserActivation(): bool
{
$usersConfig = $this->getJoomlaUsersParams();
$registrationType = (int)$usersConfig->get('useractivation');

return $registrationType === 1;
}

// TRIGGERS
//
// Methods called from external forces

/**
* Triggered by registration plugin
*
* @return bool
*
* @since 1.0.0
*/
public function onAfterDispatch(): bool
{
// Do Nothing

return true;
}

/**
* Called by view=register to allow adapters to run it's queries before redirecting
*
* @since 1.0.0
*/
public function beforeStartRedirection(): void
{
// Nothing to do
}

/**
* If new user user is registered then set user id in session for the registration
*
* @param $user
* @param $isnew
*
* @return bool
*
* @since 1.0.0
*
* @noinspection PhpUnusedParameterInspection
*/
public function onBeforeStoreUser($user, $isnew): bool
{
// Do Nothing

return true;
}

/**
* Whenever a new user account is created on the site, the respective plugins would trigger this
*
* @param $user
* @param $isnew
* @param $success
* @param $msg
*
* @return bool
*
* @since 1.0.0
*
* @noinspection PhpUnusedParameterInspection
*/
public function onAfterStoreUser($user, $isnew, $success, $msg): bool
{
$this->setNewUserId($user['id']);

return true;
}

/**
* Triggered by registration plugin
*
* @since 1.0.0
*/
public function onPayplansAccessCheck(): void
{
// Do Nothing
}

/**
* This is where the magic happens! onAfterRoute is responsible for listening for
* events where the user wants to do registration actions and routing them appropriately,
* especially ensuring hitting the main Joomla registration endpoint is not done manually
* without considering plans.
*
* This is being called by the system plugin to perform checks on:
*
* - Registration without plan
* - Should Start Registration and complete registration
*
* @return bool
*
* @since 1.0.0
*
* @noinspection PhpUndefinedMethodInspection (invoice is called magically)
*/
public function onAfterRoute(): bool
{
// We should check if this plugin is enabled before redirecting user to plan page

if (!JPluginHelper::isEnabled('payplans', 'registration')) {

return false;
}

$option = (string)$this->input->get('option', '', 'default');
$task = (string)$this->input->get('task', '', 'default');

if (($option === 'com_users' || $option === 'com_payplans') && ($task !== 'trigger')) {

// We are interested in hits to com_users or com_payplans. We wish to ignore task=trigger
// though, as we don't care when cron calls in.

$view = (string)$this->input->get('view', '', 'default');
$layout = (string)$this->input->get('layout', '', 'default');

if ($option === 'com_users' && $view === 'registration' && $layout === 'complete') {

// This is the registration complete page, meaning the user has finished registering. At
// this point, we have the new user's ID because onAfterStoreUser was called and we put it
// in our session. But we'll make sure, of course.
//
// NOTE! The user may not be logged-in yet, if activation hasn't happened. So we need to get
// the userId from the session.

$userId = $this->getNewUserId();
$invoiceId = $this->getInvoiceId(true);

if (!$userId || !$invoiceId) {

// The odd case where this page is hit but we didn't actually register anyone. Likely a bot
// doing page scraping.

return false;
}

// Now link up the PayPlans information with the user that was created.

$invoice = PP::invoice($invoiceId);
$order = $invoice->getOrder();
$subscription = $order->getSubscription();

$invoice->user_id = $userId;
$invoice->save();

$order->buyer_id = $userId;
$order->save();

$subscription->user_id = $userId;
$subscription->save();

$this->updateNewUserPublishState($invoice, $userId);

// We're all done. Remove the Plan ID and Invoice ID from the session

$this->resetSessionVars();

// Now the user will either be sent to checkout or thanks. Checkout if they selected
// a plan that costs money, or thanks if they selected a free plan.
//
// If the user abandons at this point on a paid plan, they'll have a valid account
// (presuming they activated) but an unpaid plan. This is cool, as the registration
// portion of the flow is complete as far as this adapter is concerned.

$this->redirectToCheckout($invoiceId);

} else {

// We've been triggered, and we're interested in this route.

$isRegistrationUrl = $this->isRegistrationUrl();
$planId = $this->getPlanId();

/*
JFactory::getMailer()->sendMail(
'info@thechipwitch.com', 'Tester', 'tester@concellation.com', 'Test Spew',
"OAR Says Hook Time\n\n" . print_r($this->my, true) .
"\n\n" . print_r($this->input, true) . "\n\n" .
"iru = " . print_r($isRegistrationUrl, true) . ", Plan ID = $planId");*/

// Now check and see if this is a call to the base registration URL without
// a plan selected. If so, send the user to pick a plan.

if ($isRegistrationUrl && $planId) {

// The user is going directly to the registration URL, and there is a plan ID. But is
// there really? Because if they're hitting the registration URL, the plan ID and invoice
// key should also (or only) be on the query string! Let's find out!

$pTest = (int)$this->input->get('plan_id', 0, 'int');
$kTest = $this->input->get('invoice_key', '', 'default');

if (!$pTest || !$kTest) {

// Well! They're trying something funny! Let's clear anything in the session and send
// them to the plan selection page.

$this->session->set('REGISTRATION_NEW_USER_ID', 0);

$this->resetSessionVars();

$redirect = PPR::_('index.php?option=com_payplans&view=plan');

PP::redirect($redirect);
}
}

if ($isRegistrationUrl && !$planId) {

// Since there was no planId in the query string or in the session, this is a brand new
// shopping trip. Thus, make sure we don't have a new user ID laying around from a previous
// usage and clear out any session vars. We're starting from scratch.

$this->session->set('REGISTRATION_NEW_USER_ID', 0);

$this->resetSessionVars();

$redirect = PPR::_('index.php?option=com_payplans&view=plan');

PP::redirect($redirect);
}
}
}

return true;
}
}


·
Friday, 20 November 2020 08:43
·
0 Likes
·
0 Votes
·
0 Comments
·
NOTES:

I extended PayPlans because your abstract marked some functions that needed alteration as final. So I just cloned it, merged it in, and derived from the next class up.

html() sets $userId for the view. In my version of the override for wireframe, I check this value. If it's not set, I display the "register" button with the url. But if it is set, I say, "It seems you have already created an account and the activation email has been sent. Please click on the confirmation link in that email and log in before proceeding to purchase any other subscriptions." -- This is how I like it, but you can do what you want with this, of course.

To avoid having to make a new entry for this in wireframe, I could, of course, change line 139 from

$namespace = 'site/registration/' . $this->type . '/' . $namespace;

to

$namespace = 'site/registration/joomla/' . $namespace;

If I did that, this would be the ONLY file needed.
·
Friday, 20 November 2020 08:44
·
0 Likes
·
0 Votes
·
0 Comments
·
Hey Christopher,

Thank you for sharing detailed information on this.
We will look into this and see how we can do this.

For now, always take a backup of your changes before update payplans.

Thank you for understanding!
·
Friday, 20 November 2020 12:57
·
0 Likes
·
0 Votes
·
0 Comments
·
No problem! I hope my adapter gives you guys some ideas. I'm going to make a script that runs after running the Joomla updater on your package that puts my adapter back in every time. No worries.

If you can make a way for 3rd parties to have their own registration adapters, that would be awesome. Just the one PHP file is needed, as all theme files can go in the template without fear of being overwritten.

This was fun to develop - I'm sure I'll find a bug or two in the weeks to come
·
Friday, 20 November 2020 13:02
·
0 Likes
·
0 Votes
·
0 Comments
·
Hey Christopher,

If you can make a way for 3rd parties to have their own registration adapters, that would be awesome. Just the one PHP file is needed, as all theme files can go in the template without fear of being overwritten.
We will look into this.

This was fun to develop - I'm sure I'll find a bug or two in the weeks to come
Keep us update then.
·
Friday, 20 November 2020 13:12
·
0 Likes
·
0 Votes
·
0 Comments
·
View Full Post