Your IP : 216.73.216.224


Current Path : /var/www/html/plugins/task/membershippro/src/Extension/
Upload File :
Current File : /var/www/html/plugins/task/membershippro/src/Extension/MembershipPro.php

<?php
/**
 * @package        Joomla
 * @subpackage     Membership Pro
 * @author         Tuan Pham Ngoc
 * @copyright      Copyright (C) 2012 - 2026 Ossolution Team
 * @license        GNU/GPL, see LICENSE.php
 */

namespace Joomla\Plugin\Task\MembershipPro\Extension;

use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent;
use Joomla\Component\Scheduler\Administrator\Task\Status as TaskStatus;
use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Event\DispatcherInterface;
use Joomla\Event\SubscriberInterface;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\Registry\Registry;

defined('_JEXEC') or die;

final class MembershipPro extends CMSPlugin implements SubscriberInterface
{
	use TaskPluginTrait;
	use DatabaseAwareTrait;

	/**
	 * @var string[]
	 *
	 * @since 4.1.0
	 */
	protected const TASKS_MAP = [
		'membershippro.deleteOldInvoicesPDF'                 => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_DELETE_OLD_INVOICES_PDF',
			'method'          => 'deleteOldInvoicesPDF',
		],
		'membershippro.deleteMemberCardsPDF'                 => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_DELETE_OLD_MEMBERS_CARD_PDF',
			'method'          => 'deleteOldMemberCardsPDF',
		],
		'membershippro.deleteOldSubscriptions'               => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_DELETE_OLD_SUBSCRIPTIONS',
			'form'            => 'delete_old_subscriptions',
			'method'          => 'deleteOldSubscriptions',
		],
		'membershippro.deleteIncompletePaymentSubscriptions' => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_DELETE_INCOMPLETE_PAYMENT_SUBSCRIPTIONS',
			'form'            => 'delete_incomplete_payments_subscriptions',
			'method'          => 'deleteIncompletePaymentSubscriptions',
		],
		'membershippro.cleanEmailLogs'                       => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_CLEAN_EMAILS_LOG',
			'form'            => 'clean_emails_log',
			'method'          => 'cleanEmailsLog',
		],
		'membershippro.offlinePaymentReminder'               => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_OFFLINE_PAYMENT_REMINDER',
			'form'            => 'offline_payment_reminder',
			'method'          => 'sendOfflinePaymentReminder',
		],
		'membershippro.sendOfflineRecurringInvoice'          => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_SEND_OFFLINE_RECURRING_INVOICE',
			'form'            => 'offline_recurring_invoice',
			'method'          => 'sendOfflineRecurringInvoice',
		],
		'membershippro.sendICPSNotification'                 => [
			'langConstPrefix' => 'PLG_TASK_MEMBERSHIPPRO_TASK_SEND_ICPS_NOTIFICATION',
			'form'            => 'icps_notification',
			'method'          => 'sendICPSNotification',
		],
	];

	/**
	 * @inheritDoc
	 *
	 * @return string[]
	 *
	 * @since 4.1.0
	 */
	public static function getSubscribedEvents(): array
	{
		return [
			'onTaskOptionsList'    => 'advertiseRoutines',
			'onExecuteTask'        => 'standardRoutineHandler',
			'onContentPrepareForm' => 'enhanceTaskItemForm',
		];
	}

	/**
	 * Constructor.
	 *
	 * @param   DispatcherInterface  $dispatcher     The dispatcher
	 * @param   array                $config         An optional associative array of configuration settings
	 * @param   string               $rootDirectory  The root directory to look for images
	 *
	 * @since   4.2.0
	 */
	public function __construct(DispatcherInterface $dispatcher, array $config)
	{
		parent::__construct($dispatcher, $config);

		$this->loadLanguage();
	}

	/**
	 * Delete old invoices PDF to save storage spaces
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function deleteOldInvoicesPDF(ExecuteTaskEvent $event): int
	{
		$path = JPATH_ROOT . '/media/com_osmembership/invoices';

		$files = Folder::files($path, '\.pdf$', false, true);

		foreach ($files as $file)
		{
			if ($this->isFileCreatedMoreThanOneDay($file))
			{
				File::delete($file);
			}
		}

		return TaskStatus::OK;
	}

	/**
	 * Delete old membercards PDF to save storage spaces
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function deleteOldMemberCardsPDF(ExecuteTaskEvent $event): int
	{
		$path = JPATH_ROOT . '/media/com_osmembership/membercards';

		$files = Folder::files($path, '\.pdf$', false, true);

		foreach ($files as $file)
		{
			if ($this->isFileCreatedMoreThanOneDay($file))
			{
				File::delete($file);
			}
		}

		return TaskStatus::OK;
	}

	/**
	 * Delete old subscription records which are older than certain number of days in database
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function deleteOldSubscriptions(ExecuteTaskEvent $event): int
	{
		$params = new Registry($event->getArgument('params'));
		$delay  = (int) $params->get('delay', 10) ?: 10;

		$delayInDays = $delay * 365;

		$db    = $this->getDatabase();
		$query = $db->getQuery(true)
			->select('id')
			->from('#__osmembership_subscribers')
			->where('TIMESTAMPDIFF(DAY, created_date, UTC_DATE()) >= ' . $delayInDays)
			->order('id');
		$db->setQuery($query);
		$ids = $db->loadColumn();

		if (count($ids))
		{
			$this->deleteSubscriptions($ids);
		}

		return TaskStatus::OK;
	}

	/**
	 * Delete old subscription records which are older than certain number of days in database
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function deleteIncompletePaymentSubscriptions(ExecuteTaskEvent $event): int
	{
		$params = new Registry($event->getArgument('params'));
		$delay  = (int) $params->get('delay', 20) ?: 20;

		$db = $this->getDatabase();

		$query = $db->getQuery(true)
			->select('id')
			->from('#__osmembership_subscribers')
			->where('published = 0')
			->where('payment_method NOT LIKE "os_offline%"')
			->where('DATEDIFF(UTC_DATE(), created_date) >= ' . $delay)
			->order('id');
		$db->setQuery($query, 0, 20);
		$ids = $db->loadColumn();

		if (count($ids))
		{
			$this->deleteSubscriptions($ids);
		}

		return TaskStatus::OK;
	}

	/**
	 * Clean email logs which are older than pre-configured number of days
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function cleanEmailsLog(ExecuteTaskEvent $event): int
	{
		$params = new Registry($event->getArgument('params'));
		$delay  = (int) $params->get('delay', 30) ?: 30;

		$db    = $this->getDatabase();
		$query = $db->getQuery(true)
			->delete('#__osmembership_emails')
			->where('DATEDIFF(UTC_DATE(), sent_at) >= ' . $delay);
		$db->setQuery($query)
			->execute();

		return TaskStatus::OK;
	}

	/**
	 * Send reminder to subscriptions use offline payment to remind them to complete payment for these subscriptions
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function sendOfflinePaymentReminder(ExecuteTaskEvent $event): int
	{
		$params              = new Registry($event->getArgument('params'));
		$bccEmails           = $params->get('bcc_emails', '');
		$numberDays          = (int) $params->get('number_days') ?: 7;
		$numberSubscriptions = (int) $params->get('number_subscribers') ?: 15;

		$db    = $this->getDatabase();
		$query = $db->getQuery(true)
			->select('*')
			->from('#__osmembership_subscribers')
			->where('published = 0')
			->where('payment_method LIKE "os_offline%"')
			->where('offline_payment_reminder_email_sent = 0')
			->where('DATEDIFF(UTC_DATE(), created_date) >= ' . $numberDays);
		$db->setQuery($query, 0, $numberSubscriptions);
		$rows = $db->loadObjectList();

		if (count($rows) > 0)
		{
			// Require library + register autoloader
			require_once JPATH_ADMINISTRATOR . '/components/com_osmembership/loader.php';

			$ids = [];

			foreach ($rows as $row)
			{
				$ids[] = $row->id;
			}

			\OSMembershipHelper::callOverridableHelperMethod(
				'Mail',
				'sendOfflinePaymentReminderEmails',
				[$rows, $bccEmails]
			);

			$query->clear()
				->update('#__osmembership_subscribers')
				->set('offline_payment_reminder_email_sent = 1')
				->whereIn('id', $ids);
			$db->setQuery($query)
				->execute();
		}

		return TaskStatus::OK;
	}

	/**
	 * Renew and send invoice for recurring subscription uses offline payment to ask users to pay for it
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function sendOfflineRecurringInvoice(ExecuteTaskEvent $event): int
	{
		$params            = new Registry($event->getArgument('params'));
		$numberSubEachTime = (int) $params->get('number_subscribers', 10) ?: 10;
		$numberDays        = (int) $params->get('number_days', 10) ?: 10;
		$published         = (int) $params->get('published', 0);

		$db    = $this->getDatabase();
		$query = $db->getQuery(true)
			->select('a.*')
			->from('#__osmembership_subscribers AS a')
			->innerJoin('#__osmembership_plans AS b  ON a.plan_id = b.id')
			->where('a.published = 1')
			->where('group_admin_id = 0')
			->where('a.offline_recurring_email_sent = 0')
			->where('b.recurring_subscription = 1')
			->where('recurring_subscription_cancelled = 0')
			->where('a.payment_method LIKE "os_offline%"')
			->where('DATEDIFF(a.to_date, NOW()) >= 0')
			->where('DATEDIFF(a.to_date, NOW()) <= ' . $numberDays);
		$db->setQuery($query, 0, $numberSubEachTime);

		try
		{
			$rows = $db->loadObjectList();
		}
		catch (\Exception $e)
		{
			$rows = [];
		}

		if ($rows === [])
		{
			return TaskStatus::OK;
		}

		// Require library + register autoloader
		require_once JPATH_ADMINISTRATOR . '/components/com_osmembership/loader.php';

		/* @var \OSMembershipModelApi $model */
		$model  = \MPFModel::getTempInstance('Api', 'OSMembershipModel');
		$config = \OSMembershipHelper::getConfig();

		foreach ($rows as $row)
		{
			// Check to see whether the user has renewed before
			$query->clear()
				->select('COUNT(*)')
				->from('#__osmembership_subscribers')
				->where('plan_id = ' . $row->plan_id)
				->where('published = 1')
				->where('id > ' . $row->id)
				->where('((user_id > 0 AND user_id = ' . (int) $row->user_id . ') OR email="' . $row->email . '")');
			$db->setQuery($query);
			$total = (int) $db->loadResult();

			if (!$total)
			{
				$data    = ['published' => $published];
				$sParams = new Registry($row->params);

				if ($sParams->get('regular_amount') > 0)
				{
					$data = array_merge($data, [
						'amount'                 => $sParams->get('regular_amount'),
						'discount_amount'        => $sParams->get('regular_discount_amount'),
						'tax_amount'             => $sParams->get('regular_tax_amount'),
						'payment_processing_fee' => $sParams->get('regular_payment_processing_fee'),
						'gross_amount'           => $sParams->get('regular_gross_amount'),
					]);
				}

				$renewedSubscription = $model->renew($row->id, $data, false);
				\OSMembershipHelperMail::sendOfflineRecurringEmail($renewedSubscription, $config);
			}

			$query->clear()
				->update('#__osmembership_subscribers')
				->set('offline_recurring_email_sent = 1')
				->where('id = ' . $row->id);
			$db->setQuery($query)
				->execute();
		}

		return TaskStatus::OK;
	}

	/**
	 * Send notification email to admin to inform them about incomplete payment subscriptions
	 *
	 * @param   ExecuteTaskEvent  $event  The onExecuteTask event
	 *
	 * @return integer  The exit code
	 */
	protected function sendICPSNotification(ExecuteTaskEvent $event): int
	{
		$params = new Registry($event->getArgument('params'));

		// Only send notification to subscriptions within the last 48 hours
		$db    = $this->getDatabase();
		$now   = $db->quote(Factory::getDate('now', $this->getApplication()->get('offset'))->toSql(true));
		$query = $db->getQuery(true)
			->select('*')
			->from('#__osmembership_subscribers')
			->where('published = 0')
			->where('icps_notified = 0')
			->where('payment_method NOT LIKE "os_offline%"')
			->where("TIMESTAMPDIFF(HOUR, created_date, $now) <= 48")
			->where("TIMESTAMPDIFF(MINUTE, created_date, $now) >= 20")
			->order('id');
		$db->setQuery($query, 0, 10);
		$rows = $db->loadObjectList();

		$subscriptions = [];
		$ids           = [];

		foreach ($rows as $row)
		{
			// Special case, without user_id and email, no way to check if he is registered again ir not
			if (!$row->user_id && !$row->email)
			{
				$subscriptions[] = $row;
				continue;
			}

			// Check to see if he has paid for the subscription in a different record
			$query->clear()
				->select('COUNT(*)')
				->from('#__osmembership_subscribers')
				->where('plan_id = ' . (int) $row->plan_id)
				->where('id > ' . $row->id)
				->where('(published = 1 OR payment_method LIKE "os_offline%")');

			if ($row->user_id)
			{
				$query->where('user_id = ' . $row->user_id);
			}
			else
			{
				$query->where('email = ' . $db->quote($row->email));
			}

			$db->setQuery($query);
			$total = $db->loadResult();

			if (!$total)
			{
				$subscriptions[] = $row;
				$ids[]           = $row->id;
			}
		}

		if (count($subscriptions) > 0)
		{
			// Require library + register autoloader
			require_once JPATH_ADMINISTRATOR . '/components/com_osmembership/loader.php';

			\OSMembershipHelper::callOverridableHelperMethod(
				'Mail',
				'sendIncompletePaymentSubscriptionsEmails',
				[$subscriptions, $params]
			);

			// Mark the notification as sent
			$query->clear()
				->update('#__osmembership_subscribers')
				->set('icps_notified = 1')
				->whereIn('id', $ids);
			$db->setQuery($query)
				->execute();
		}

		return TaskStatus::OK;
	}

	/**
	 * Method to check if the file was created more than one day ago
	 *
	 * @param   string  $file  Path to the file
	 *
	 * @return bool
	 */
	private function isFileCreatedMoreThanOneDay($file): bool
	{
		$fileCreatedTime = filemtime($file);

		if ($fileCreatedTime === false)
		{
			return false;
		}

		$timeDifference = time() - $fileCreatedTime;

		return $timeDifference > 24 * 60 * 60;
	}

	/**
	 * Delete subscriptions from given IDs
	 *
	 * @param   array  $ids
	 *
	 * @return void
	 */
	private function deleteSubscriptions(array $ids): void
	{
		// Require library + register autoloader
		require_once JPATH_ADMINISTRATOR . '/components/com_osmembership/loader.php';

		\JLoader::register(
			'OSMembershipModelSubscription',
			JPATH_ADMINISTRATOR . '/components/com_osmembership/model/subscription.php'
		);

		/* @var \OSMembershipModelSubscription $model */
		$model = \MPFModel::getTempInstance('Subscription', 'OSMembershipModel');

		$model->delete($ids);
	}
}