first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset name="default" label="COM_PRIVACY_CONFIRM_REQUEST_FIELDSET_LABEL">
<field
name="confirm_token"
type="text"
label="COM_PRIVACY_FIELD_CONFIRM_CONFIRM_TOKEN_LABEL"
description="COM_PRIVACY_FIELD_CONFIRM_CONFIRM_TOKEN_DESC"
filter="alnum"
required="true"
size="32"
/>
</fieldset>
</form>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset name="default" label="COM_PRIVACY_REMIND_REQUEST_FIELDSET_LABEL">
<field
name="email"
type="text"
label="JGLOBAL_EMAIL"
description="COM_PRIVACY_FIELD_CONFIRM_EMAIL_DESC"
validate="email"
required="true"
size="30"
/>
<field
name="remind_token"
type="text"
label="COM_PRIVACY_FIELD_REMIND_CONFIRM_TOKEN_LABEL"
description="COM_PRIVACY_FIELD_REMIND_CONFIRM_TOKEN_DESC"
filter="alnum"
required="true"
size="32"
/>
</fieldset>
</form>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset name="default">
<field
name="request_type"
type="list"
label="COM_PRIVACY_FIELD_REQUEST_TYPE_LABEL"
description="COM_PRIVACY_FIELD_REQUEST_TYPE_DESC"
filter="string"
default="export"
validate="options"
>
<option value="export">COM_PRIVACY_REQUEST_TYPE_EXPORT</option>
<option value="remove">COM_PRIVACY_REQUEST_TYPE_REMOVE</option>
</field>
</fieldset>
</form>

View File

@ -0,0 +1,58 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Controller;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Privacy Controller
*
* @since 3.9.0
*/
class DisplayController extends BaseController
{
/**
* Method to display a view.
*
* @param boolean $cachable If true, the view output will be cached
* @param array $urlparams An array of safe URL parameters and their variable types.
* @see \Joomla\CMS\Filter\InputFilter::clean() for valid values.
*
* @return $this
*
* @since 3.9.0
*/
public function display($cachable = false, $urlparams = [])
{
$view = $this->input->get('view', $this->default_view);
// Submitting information requests and confirmation through the frontend is restricted to authenticated users at this time
if (\in_array($view, ['confirm', 'request']) && $this->app->getIdentity()->guest) {
$this->setRedirect(
Route::_('index.php?option=com_users&view=login&return=' . base64_encode('index.php?option=com_privacy&view=' . $view), false)
);
return $this;
}
// Set a Referrer-Policy header for views which require it
if (\in_array($view, ['confirm', 'remind'])) {
$this->app->setHeader('Referrer-Policy', 'no-referrer', true);
}
return parent::display($cachable, $urlparams);
}
}

View File

@ -0,0 +1,175 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Privacy\Site\Model\ConfirmModel;
use Joomla\Component\Privacy\Site\Model\RemindModel;
use Joomla\Component\Privacy\Site\Model\RequestModel;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Request action controller class.
*
* @since 3.9.0
*/
class RequestController extends BaseController
{
/**
* Method to confirm the information request.
*
* @return boolean
*
* @since 3.9.0
*/
public function confirm()
{
// Check the request token.
$this->checkToken('post');
/** @var ConfirmModel $model */
$model = $this->getModel('Confirm', 'Site');
$data = $this->input->post->get('jform', [], 'array');
$return = $model->confirmRequest($data);
// Check for a hard error.
if ($return instanceof \Exception) {
// Get the error message to display.
if ($this->app->get('error_reporting')) {
$message = $return->getMessage();
} else {
$message = Text::_('COM_PRIVACY_ERROR_CONFIRMING_REQUEST');
}
// Go back to the confirm form.
$this->setRedirect(Route::_('index.php?option=com_privacy&view=confirm', false), $message, 'error');
return false;
}
if ($return === false) {
// Confirm failed.
// Go back to the confirm form.
$message = Text::sprintf('COM_PRIVACY_ERROR_CONFIRMING_REQUEST_FAILED', $model->getError());
$this->setRedirect(Route::_('index.php?option=com_privacy&view=confirm', false), $message, 'notice');
return false;
}
// Confirm succeeded.
$this->setRedirect(Route::_(Uri::root()), Text::_('COM_PRIVACY_CONFIRM_REQUEST_SUCCEEDED'), 'info');
return true;
}
/**
* Method to submit an information request.
*
* @return boolean
*
* @since 3.9.0
*/
public function submit()
{
// Check the request token.
$this->checkToken('post');
/** @var RequestModel $model */
$model = $this->getModel('Request', 'Site');
$data = $this->input->post->get('jform', [], 'array');
$return = $model->createRequest($data);
// Check for a hard error.
if ($return instanceof \Exception) {
// Get the error message to display.
if ($this->app->get('error_reporting')) {
$message = $return->getMessage();
} else {
$message = Text::_('COM_PRIVACY_ERROR_CREATING_REQUEST');
}
// Go back to the confirm form.
$this->setRedirect(Route::_('index.php?option=com_privacy&view=request', false), $message, 'error');
return false;
}
if ($return === false) {
// Confirm failed.
// Go back to the confirm form.
$message = Text::sprintf('COM_PRIVACY_ERROR_CREATING_REQUEST_FAILED', $model->getError());
$this->setRedirect(Route::_('index.php?option=com_privacy&view=request', false), $message, 'notice');
return false;
}
// Confirm succeeded.
$this->setRedirect(Route::_(Uri::root()), Text::_('COM_PRIVACY_CREATE_REQUEST_SUCCEEDED'), 'info');
return true;
}
/**
* Method to extend the privacy consent.
*
* @return boolean
*
* @since 3.9.0
*/
public function remind()
{
// Check the request token.
$this->checkToken('post');
/** @var RemindModel $model */
$model = $this->getModel('Remind', 'Site');
$data = $this->input->post->get('jform', [], 'array');
$return = $model->remindRequest($data);
// Check for a hard error.
if ($return instanceof \Exception) {
// Get the error message to display.
if ($this->app->get('error_reporting')) {
$message = $return->getMessage();
} else {
$message = Text::_('COM_PRIVACY_ERROR_REMIND_REQUEST');
}
// Go back to the confirm form.
$this->setRedirect(Route::_('index.php?option=com_privacy&view=remind', false), $message, 'error');
return false;
}
if ($return === false) {
// Confirm failed.
// Go back to the confirm form.
$message = Text::sprintf('COM_PRIVACY_ERROR_CONFIRMING_REMIND_FAILED', $model->getError());
$this->setRedirect(Route::_('index.php?option=com_privacy&view=remind', false), $message, 'notice');
return false;
}
// Confirm succeeded.
$this->setRedirect(Route::_(Uri::root()), Text::_('COM_PRIVACY_CONFIRM_REMIND_SUCCEEDED'), 'info');
return true;
}
}

View File

@ -0,0 +1,234 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Model;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Table\Table;
use Joomla\CMS\User\UserHelper;
use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel;
use Joomla\Component\Messages\Administrator\Model\MessageModel;
use Joomla\Component\Privacy\Administrator\Table\RequestTable;
use Joomla\Database\Exception\ExecutionFailureException;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Request confirmation model class.
*
* @since 3.9.0
*/
class ConfirmModel extends AdminModel
{
/**
* Confirms the information request.
*
* @param array $data The data expected for the form.
*
* @return mixed Exception | boolean
*
* @since 3.9.0
*/
public function confirmRequest($data)
{
// Get the form.
$form = $this->getForm();
// Check for an error.
if ($form instanceof \Exception) {
return $form;
}
// Filter and validate the form data.
$data = $form->filter($data);
$return = $form->validate($data);
// Check for an error.
if ($return instanceof \Exception) {
return $return;
}
// Check the validation results.
if ($return === false) {
// Get the validation messages from the form.
foreach ($form->getErrors() as $formError) {
$this->setError($formError->getMessage());
}
return false;
}
// Get the user email address
$email = $this->getCurrentUser()->email;
// Search for the information request
/** @var RequestTable $table */
$table = $this->getTable();
if (!$table->load(['email' => $email, 'status' => 0])) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_PENDING_REQUESTS'));
return false;
}
// A request can only be confirmed if it is in a pending status and has a confirmation token
if ($table->status != '0' || !$table->confirm_token || $table->confirm_token_created_at === null) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_PENDING_REQUESTS'));
return false;
}
// A request can only be confirmed if the token is less than 24 hours old
$confirmTokenCreatedAt = new Date($table->confirm_token_created_at);
$confirmTokenCreatedAt->add(new \DateInterval('P1D'));
$now = new Date('now');
if ($now > $confirmTokenCreatedAt) {
// Invalidate the request
$table->status = -1;
$table->confirm_token = '';
$table->confirm_token_created_at = null;
try {
$table->store();
} catch (ExecutionFailureException $exception) {
// The error will be logged in the database API, we just need to catch it here to not let things fatal out
}
$this->setError(Text::_('COM_PRIVACY_ERROR_CONFIRM_TOKEN_EXPIRED'));
return false;
}
// Verify the token
if (!UserHelper::verifyPassword($data['confirm_token'], $table->confirm_token)) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_PENDING_REQUESTS'));
return false;
}
// Everything is good to go, transition the request to confirmed
$saved = $this->save(
[
'id' => $table->id,
'status' => 1,
'confirm_token' => '',
]
);
if (!$saved) {
// Error was set by the save method
return false;
}
// Push a notification to the site's super users, deliberately ignoring if this process fails so the below message goes out
/** @var MessageModel $messageModel */
$messageModel = Factory::getApplication()->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator');
$messageModel->notifySuperUsers(
Text::_('COM_PRIVACY_ADMIN_NOTIFICATION_USER_CONFIRMED_REQUEST_SUBJECT'),
Text::sprintf('COM_PRIVACY_ADMIN_NOTIFICATION_USER_CONFIRMED_REQUEST_MESSAGE', $table->email)
);
$message = [
'action' => 'request-confirmed',
'subjectemail' => $table->email,
'id' => $table->id,
'itemlink' => 'index.php?option=com_privacy&view=request&id=' . $table->id,
];
$this->getActionlogModel()->addLog([$message], 'COM_PRIVACY_ACTION_LOG_CONFIRMED_REQUEST', 'com_privacy.request');
return true;
}
/**
* Method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form|boolean A Form object on success, false on failure
*
* @since 3.9.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_privacy.confirm', 'confirm', ['control' => 'jform']);
if (empty($form)) {
return false;
}
$input = Factory::getApplication()->getInput();
if ($input->getMethod() === 'GET') {
$form->setValue('confirm_token', '', $input->get->getAlnum('confirm_token'));
}
return $form;
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @return Table A Table object
*
* @since 3.9.0
* @throws \Exception
*/
public function getTable($name = 'Request', $prefix = 'Administrator', $options = [])
{
return parent::getTable($name, $prefix, $options);
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 3.9.0
*/
protected function populateState()
{
// Get the application object.
$params = Factory::getApplication()->getParams('com_privacy');
// Load the parameters.
$this->setState('params', $params);
}
/**
* Method to fetch an instance of the action log model.
*
* @return ActionlogModel
*
* @since 4.0.0
*/
private function getActionlogModel(): ActionlogModel
{
return Factory::getApplication()->bootComponent('com_actionlogs')
->getMVCFactory()->createModel('Actionlog', 'Administrator', ['ignore_request' => true]);
}
}

View File

@ -0,0 +1,191 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\String\PunycodeHelper;
use Joomla\CMS\Table\Table;
use Joomla\CMS\User\UserHelper;
use Joomla\Component\Privacy\Administrator\Table\ConsentTable;
use Joomla\Database\Exception\ExecutionFailureException;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Remind confirmation model class.
*
* @since 3.9.0
*/
class RemindModel extends AdminModel
{
/**
* Confirms the remind request.
*
* @param array $data The data expected for the form.
*
* @return mixed \Exception | JException | boolean
*
* @since 3.9.0
*/
public function remindRequest($data)
{
// Get the form.
$form = $this->getForm();
$data['email'] = PunycodeHelper::emailToPunycode($data['email']);
// Check for an error.
if ($form instanceof \Exception) {
return $form;
}
// Filter and validate the form data.
$data = $form->filter($data);
$return = $form->validate($data);
// Check for an error.
if ($return instanceof \Exception) {
return $return;
}
// Check the validation results.
if ($return === false) {
// Get the validation messages from the form.
foreach ($form->getErrors() as $formError) {
$this->setError($formError->getMessage());
}
return false;
}
/** @var ConsentTable $table */
$table = $this->getTable();
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['r.id', 'r.user_id', 'r.token']));
$query->from($db->quoteName('#__privacy_consents', 'r'));
$query->join(
'LEFT',
$db->quoteName('#__users', 'u'),
$db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id')
);
$query->where($db->quoteName('u.email') . ' = :email')
->bind(':email', $data['email']);
$query->where($db->quoteName('r.remind') . ' = 1');
$db->setQuery($query);
try {
$remind = $db->loadObject();
} catch (ExecutionFailureException $e) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_PENDING_REMIND'));
return false;
}
if (!$remind) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_PENDING_REMIND'));
return false;
}
// Verify the token
if (!UserHelper::verifyPassword($data['remind_token'], $remind->token)) {
$this->setError(Text::_('COM_PRIVACY_ERROR_NO_REMIND_REQUESTS'));
return false;
}
// Everything is good to go, transition the request to extended
$saved = $this->save(
[
'id' => $remind->id,
'remind' => 0,
'token' => '',
'created' => Factory::getDate()->toSql(),
]
);
if (!$saved) {
// Error was set by the save method
return false;
}
return true;
}
/**
* Method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form|boolean A Form object on success, false on failure
*
* @since 3.9.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_privacy.remind', 'remind', ['control' => 'jform']);
if (empty($form)) {
return false;
}
$input = Factory::getApplication()->getInput();
if ($input->getMethod() === 'GET') {
$form->setValue('remind_token', '', $input->get->getAlnum('remind_token'));
}
return $form;
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @return Table A Table object
*
* @throws \Exception
* @since 3.9.0
*/
public function getTable($name = 'Consent', $prefix = 'Administrator', $options = [])
{
return parent::getTable($name, $prefix, $options);
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 3.9.0
*/
protected function populateState()
{
// Get the application object.
$params = Factory::getApplication()->getParams('com_privacy');
// Load the parameters.
$this->setState('params', $params);
}
}

View File

@ -0,0 +1,262 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Model;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Mail\Exception\MailDisabledException;
use Joomla\CMS\Mail\MailTemplate;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\UserHelper;
use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel;
use Joomla\Component\Messages\Administrator\Model\MessageModel;
use Joomla\Component\Privacy\Administrator\Table\RequestTable;
use Joomla\Database\Exception\ExecutionFailureException;
use PHPMailer\PHPMailer\Exception as phpmailerException;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Request model class.
*
* @since 3.9.0
*/
class RequestModel extends AdminModel
{
/**
* Creates an information request.
*
* @param array $data The data expected for the form.
*
* @return mixed Exception | boolean
*
* @since 3.9.0
*/
public function createRequest($data)
{
$app = Factory::getApplication();
// Creating requests requires the site's email sending be enabled
if (!$app->get('mailonline', 1)) {
$this->setError(Text::_('COM_PRIVACY_ERROR_CANNOT_CREATE_REQUEST_WHEN_SENDMAIL_DISABLED'));
return false;
}
// Get the form.
$form = $this->getForm();
// Check for an error.
if ($form instanceof \Exception) {
return $form;
}
// Filter and validate the form data.
$data = $form->filter($data);
$return = $form->validate($data);
// Check for an error.
if ($return instanceof \Exception) {
return $return;
}
// Check the validation results.
if ($return === false) {
// Get the validation messages from the form.
foreach ($form->getErrors() as $formError) {
$this->setError($formError->getMessage());
}
return false;
}
$data['email'] = $this->getCurrentUser()->email;
// Search for an open information request matching the email and type
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('COUNT(id)')
->from($db->quoteName('#__privacy_requests'))
->where($db->quoteName('email') . ' = :email')
->where($db->quoteName('request_type') . ' = :requesttype')
->whereIn($db->quoteName('status'), [0, 1])
->bind(':email', $data['email'])
->bind(':requesttype', $data['request_type']);
try {
$result = (int) $db->setQuery($query)->loadResult();
} catch (ExecutionFailureException $exception) {
// Can't check for existing requests, so don't create a new one
$this->setError(Text::_('COM_PRIVACY_ERROR_CHECKING_FOR_EXISTING_REQUESTS'));
return false;
}
if ($result > 0) {
$this->setError(Text::_('COM_PRIVACY_ERROR_PENDING_REQUEST_OPEN'));
return false;
}
// Everything is good to go, create the request
$token = ApplicationHelper::getHash(UserHelper::genRandomPassword());
$hashedToken = UserHelper::hashPassword($token);
$data['confirm_token'] = $hashedToken;
$data['confirm_token_created_at'] = Factory::getDate()->toSql();
if (!$this->save($data)) {
// The save function will set the error message, so just return here
return false;
}
// Push a notification to the site's super users, deliberately ignoring if this process fails so the below message goes out
/** @var MessageModel $messageModel */
$messageModel = $app->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator');
$messageModel->notifySuperUsers(
Text::_('COM_PRIVACY_ADMIN_NOTIFICATION_USER_CREATED_REQUEST_SUBJECT'),
Text::sprintf('COM_PRIVACY_ADMIN_NOTIFICATION_USER_CREATED_REQUEST_MESSAGE', $data['email'])
);
// The mailer can be set to either throw Exceptions or return boolean false, account for both
try {
$linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE;
$templateData = [
'sitename' => $app->get('sitename'),
'url' => Uri::root(),
'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=confirm&confirm_token=' . $token, false, $linkMode, true),
'formurl' => Route::link('site', 'index.php?option=com_privacy&view=confirm', false, $linkMode, true),
'token' => $token,
];
switch ($data['request_type']) {
case 'export':
$mailer = new MailTemplate('com_privacy.notification.export', $app->getLanguage()->getTag());
break;
case 'remove':
$mailer = new MailTemplate('com_privacy.notification.remove', $app->getLanguage()->getTag());
break;
default:
$this->setError(Text::_('COM_PRIVACY_ERROR_UNKNOWN_REQUEST_TYPE'));
return false;
}
$mailer->addTemplateData($templateData);
$mailer->addRecipient($data['email']);
$mailer->send();
/** @var RequestTable $table */
$table = $this->getTable();
if (!$table->load($this->getState($this->getName() . '.id'))) {
$this->setError($table->getError());
return false;
}
// Log the request's creation
$message = [
'action' => 'request-created',
'requesttype' => $table->request_type,
'subjectemail' => $table->email,
'id' => $table->id,
'itemlink' => 'index.php?option=com_privacy&view=request&id=' . $table->id,
];
$this->getActionlogModel()->addLog([$message], 'COM_PRIVACY_ACTION_LOG_CREATED_REQUEST', 'com_privacy.request');
// The email sent and the record is saved, everything is good to go from here
return true;
} catch (MailDisabledException | phpmailerException $exception) {
$this->setError($exception->getMessage());
return false;
}
}
/**
* Method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form|boolean A Form object on success, false on failure
*
* @since 3.9.0
*/
public function getForm($data = [], $loadData = true)
{
return $this->loadForm('com_privacy.request', 'request', ['control' => 'jform']);
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @return Table A Table object
*
* @throws \Exception
* @since 3.9.0
*/
public function getTable($name = 'Request', $prefix = 'Administrator', $options = [])
{
return parent::getTable($name, $prefix, $options);
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 3.9.0
*/
protected function populateState()
{
// Get the application object.
$params = Factory::getApplication()->getParams('com_privacy');
// Load the parameters.
$this->setState('params', $params);
}
/**
* Method to fetch an instance of the action log model.
*
* @return ActionlogModel
*
* @since 4.0.0
*/
private function getActionlogModel(): ActionlogModel
{
return Factory::getApplication()->bootComponent('com_actionlogs')
->getMVCFactory()->createModel('Actionlog', 'Administrator', ['ignore_request' => true]);
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\Service;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Menu\AbstractMenu;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Routing class from com_privacy
*
* @since 3.9.0
*/
class Router extends RouterView
{
/**
* Privacy Component router constructor
*
* @param CMSApplication $app The application object
* @param AbstractMenu $menu The menu object to work with
*
* @since 3.9.0
*/
public function __construct($app = null, $menu = null)
{
$this->registerView(new RouterViewConfiguration('confirm'));
$this->registerView(new RouterViewConfiguration('request'));
$this->registerView(new RouterViewConfiguration('remind'));
parent::__construct($app, $menu);
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
}
}

View File

@ -0,0 +1,123 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\View\Confirm;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Request confirmation view class
*
* @since 3.9.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The form object
*
* @var Form
* @since 3.9.0
*/
protected $form;
/**
* The CSS class suffix to append to the view container
*
* @var string
* @since 3.9.0
*/
protected $pageclass_sfx;
/**
* The view parameters
*
* @var Registry
* @since 3.9.0
*/
protected $params;
/**
* The state information
*
* @var \Joomla\Registry\Registry
* @since 3.9.0
*/
protected $state;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @see BaseHtmlView::loadTemplate()
* @since 3.9.0
* @throws \Exception
*/
public function display($tpl = null)
{
// Initialise variables.
$this->form = $this->get('Form');
$this->state = $this->get('State');
$this->params = $this->state->params;
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($this->params->get('pageclass_sfx', ''), ENT_COMPAT, 'UTF-8');
$this->prepareDocument();
parent::display($tpl);
}
/**
* Prepares the document.
*
* @return void
*
* @since 3.9.0
*/
protected function prepareDocument()
{
// Because the application sets a default page title,
// we need to get it from the menu item itself
$menu = Factory::getApplication()->getMenu()->getActive();
if ($menu) {
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
} else {
$this->params->def('page_heading', Text::_('COM_PRIVACY_VIEW_CONFIRM_PAGE_TITLE'));
}
$this->setDocumentTitle($this->params->get('page_title', ''));
if ($this->params->get('menu-meta_description')) {
$this->getDocument()->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('robots')) {
$this->getDocument()->setMetaData('robots', $this->params->get('robots'));
}
}
}

View File

@ -0,0 +1,123 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\View\Remind;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Remind confirmation view class
*
* @since 3.9.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The form object
*
* @var Form
* @since 3.9.0
*/
protected $form;
/**
* The CSS class suffix to append to the view container
*
* @var string
* @since 3.9.0
*/
protected $pageclass_sfx;
/**
* The view parameters
*
* @var Registry
* @since 3.9.0
*/
protected $params;
/**
* The state information
*
* @var \Joomla\Registry\Registry
* @since 3.9.0
*/
protected $state;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @see BaseHtmlView::loadTemplate()
* @since 3.9.0
* @throws \Exception
*/
public function display($tpl = null)
{
// Initialise variables.
$this->form = $this->get('Form');
$this->state = $this->get('State');
$this->params = $this->state->params;
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($this->params->get('pageclass_sfx', ''), ENT_COMPAT, 'UTF-8');
$this->prepareDocument();
parent::display($tpl);
}
/**
* Prepares the document.
*
* @return void
*
* @since 3.9.0
*/
protected function prepareDocument()
{
// Because the application sets a default page title,
// we need to get it from the menu item itself
$menu = Factory::getApplication()->getMenu()->getActive();
if ($menu) {
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
} else {
$this->params->def('page_heading', Text::_('COM_PRIVACY_VIEW_REMIND_PAGE_TITLE'));
}
$this->setDocumentTitle($this->params->get('page_title', ''));
if ($this->params->get('menu-meta_description')) {
$this->getDocument()->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('robots')) {
$this->getDocument()->setMetaData('robots', $this->params->get('robots'));
}
}
}

View File

@ -0,0 +1,132 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Privacy\Site\View\Request;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Request view class
*
* @since 3.9.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The form object
*
* @var Form
* @since 3.9.0
*/
protected $form;
/**
* The CSS class suffix to append to the view container
*
* @var string
* @since 3.9.0
*/
protected $pageclass_sfx;
/**
* The view parameters
*
* @var Registry
* @since 3.9.0
*/
protected $params;
/**
* Flag indicating the site supports sending email
*
* @var boolean
* @since 3.9.0
*/
protected $sendMailEnabled;
/**
* The state information
*
* @var \Joomla\Registry\Registry
* @since 3.9.0
*/
protected $state;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @see BaseHtmlView::loadTemplate()
* @since 3.9.0
* @throws \Exception
*/
public function display($tpl = null)
{
// Initialise variables.
$this->form = $this->get('Form');
$this->state = $this->get('State');
$this->params = $this->state->params;
$this->sendMailEnabled = (bool) Factory::getApplication()->get('mailonline', 1);
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($this->params->get('pageclass_sfx', ''), ENT_COMPAT, 'UTF-8');
$this->prepareDocument();
parent::display($tpl);
}
/**
* Prepares the document.
*
* @return void
*
* @since 3.9.0
*/
protected function prepareDocument()
{
// Because the application sets a default page title,
// we need to get it from the menu item itself
$menu = Factory::getApplication()->getMenu()->getActive();
if ($menu) {
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
} else {
$this->params->def('page_heading', Text::_('COM_PRIVACY_VIEW_REQUEST_PAGE_TITLE'));
}
$this->setDocumentTitle($this->params->get('page_title', ''));
if ($this->params->get('menu-meta_description')) {
$this->getDocument()->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('robots')) {
$this->getDocument()->setMetaData('robots', $this->params->get('robots'));
}
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
/** @var \Joomla\Component\Privacy\Site\View\Confirm\HtmlView $this */
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
?>
<div class="request-confirm<?php echo $this->pageclass_sfx; ?>">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
</div>
<?php endif; ?>
<form action="<?php echo Route::_('index.php?option=com_privacy&task=request.confirm'); ?>" method="post" class="form-validate form-horizontal well">
<?php foreach ($this->form->getFieldsets() as $fieldset) : ?>
<fieldset>
<?php if (!empty($fieldset->label)) : ?>
<legend><?php echo Text::_($fieldset->label); ?></legend>
<?php endif; ?>
<?php echo $this->form->renderFieldset($fieldset->name); ?>
</fieldset>
<?php endforeach; ?>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-primary validate">
<?php echo Text::_('JSUBMIT'); ?>
</button>
</div>
</div>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
</div>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_PRIVACY_CONFIRM_VIEW_DEFAULT_TITLE" option="COM_PRIVACY_CONFIRM_VIEW_DEFAULT_OPTION">
<help
key="Menu_Item:_Confirm_Request"
/>
<message>
<![CDATA[COM_PRIVACY_CONFIRM_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,51 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
/** @var \Joomla\Component\Privacy\Site\View\Remind\HtmlView $this */
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
?>
<div class="remind-confirm<?php echo $this->pageclass_sfx; ?>">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
</div>
<?php endif; ?>
<form action="<?php echo Route::_('index.php?option=com_privacy&task=request.remind'); ?>" method="post" class="form-validate form-horizontal well">
<?php foreach ($this->form->getFieldsets() as $fieldset) : ?>
<fieldset>
<?php if (!empty($fieldset->label)) : ?>
<legend><?php echo Text::_($fieldset->label); ?></legend>
<?php endif; ?>
<?php echo $this->form->renderFieldset($fieldset->name); ?>
</fieldset>
<?php endforeach; ?>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-primary validate">
<?php echo Text::_('JSUBMIT'); ?>
</button>
</div>
</div>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
</div>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_PRIVACY_REMIND_VIEW_DEFAULT_TITLE" option="COM_PRIVACY_REMIND_VIEW_DEFAULT_OPTION">
<help
key="Menu_Item:_Extend_Consent"
/>
<message>
<![CDATA[COM_PRIVACY_REMIND_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,58 @@
<?php
/**
* @package Joomla.Site
* @subpackage com_privacy
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
/** @var \Joomla\Component\Privacy\Site\View\Request\HtmlView $this */
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
?>
<div class="request-form<?php echo $this->pageclass_sfx; ?>">
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
</div>
<?php endif; ?>
<?php if ($this->sendMailEnabled) : ?>
<form action="<?php echo Route::_('index.php?option=com_privacy&task=request.submit'); ?>" method="post" class="form-validate form-horizontal well">
<?php foreach ($this->form->getFieldsets() as $fieldset) : ?>
<fieldset>
<?php if (!empty($fieldset->label)) : ?>
<legend><?php echo Text::_($fieldset->label); ?></legend>
<?php endif; ?>
<?php echo $this->form->renderFieldset($fieldset->name); ?>
</fieldset>
<?php endforeach; ?>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-primary validate">
<?php echo Text::_('JSUBMIT'); ?>
</button>
</div>
</div>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php else : ?>
<div class="alert alert-warning">
<span class="icon-exclamation-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('WARNING'); ?></span>
<?php echo Text::_('COM_PRIVACY_WARNING_CANNOT_CREATE_REQUEST_WHEN_SENDMAIL_DISABLED'); ?>
</div>
<?php endif; ?>
</div>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_PRIVACY_REQUEST_VIEW_DEFAULT_TITLE" option="COM_PRIVACY_REQUEST_VIEW_DEFAULT_OPTION">
<help
key="Menu_Item:_Create_Request"
/>
<message>
<![CDATA[COM_PRIVACY_REQUEST_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>