* @github Joomla Component Builder * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access to this file defined('_JEXEC') or die('Restricted access'); use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Registry\Registry; JLoader::register('ComponentbuilderHelper', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/helpers/componentbuilder.php'); /** * Extension - Componentbuilder Privacy Compiler plugin. * * @package ComponentbuilderPrivacyCompiler * @since 1.0.0 */ class PlgExtensionComponentbuilderPrivacyCompiler extends CMSPlugin { /** * Affects constructor behavior. If true, language files will be loaded automatically. * * @var boolean * @since 1.0.0 */ protected $autoloadLanguage = true; /** * The language string builder * * @var array */ protected $languageArray = array(); /** * The hash placeholder * * @var string */ protected $hhh = '#' . '#' . '#'; /** * The open bracket placeholder * * @var string */ protected $bbb = '[' . '[' . '['; /** * The close bracket placeholder * * @var string */ protected $ddd = ']' . ']' . ']'; /* * The line numbers Switch * * @var boolean */ protected $debugLinenr = false; /** * Global switch to see if component have need of privacy plugin to be loaded. * * @var boolean * @since 1.0.0 */ protected $loadPrivacy = false; /** * The Views Linked to Joomla Users * * @var array * @since 1.0.0 */ protected $activeViews = array(); /** * The Views permission fields * * @var array * @since 1.0.0 */ protected $permissionFields = array(); /** * The permissions core * * @var array */ protected $permissionCore = array(); /** * The permissions Builder * * @var array */ protected $permissionBuilder = array(); /** * Event Triggered in the compiler [on Before Model View Data] * * @return void * * @since 1.0 */ public function jcb_ce_onBeforeModelViewData(&$context, &$view, &$placeholders) { // add the privacy $view->params = (isset($view->params) && ComponentbuilderHelper::checkJson($view->params)) ? json_decode($view->params, true) : $view->params; if (ComponentbuilderHelper::checkArray($view->params) && isset($view->params['privacy']) && ComponentbuilderHelper::checkArray($view->params['privacy']) && isset($view->params['privacy']['activate']) && $view->params['privacy']['activate'] == 1) { // activate the load of the privacy plugin $this->loadPrivacy = true; // load the admin view details $this->activeViews[$view->id] = $view; // add permissions $view->addpermissions = (isset($view->addpermissions) && ComponentbuilderHelper::checkJson($view->addpermissions)) ? json_decode($view->addpermissions, true) : null; if (ComponentbuilderHelper::checkArray($view->addpermissions)) { $view->addpermissions = array_values($view->addpermissions); // add the new permissions $view->addpermissions[] = array('action' => 'view.privacy.delete', 'implementation' => 3, 'title' => $view->name_list . ' Privacy Delete', 'description' => ' Allows the users in this group to remove their personal data in ' . $view->name_list . ' via the Joomla privacy suite.'); $view->addpermissions[] = array('action' => 'view.privacy.access', 'implementation' => 3, 'title' => $view->name_list . ' Privacy Access', 'description' => ' Allows the users in this group to access their personal data in ' . $view->name_list . ' via the Joomla privacy suite.'); // convert back to json $view->addpermissions = json_encode($view->addpermissions, JSON_FORCE_OBJECT); } // add placeholders to view if not already set if (!isset($this->activeViews[$view->id]->placeholders)) { $this->activeViews[$view->id]->placeholders = $placeholders; } } } /** * Event Triggered in the compiler [on After Build Access Sections] * * @return void * * @since 1.0 */ public function jcb_ce_onAfterBuildAccessSections(&$context, $compiler) { // check if this component needs a privacy plugin loaded if ($this->loadPrivacy) { // get the permission builder array $this->permissionBuilder = $compiler->permissionBuilder; // get the permission core array $this->permissionCore = $compiler->permissionCore; } } /** * Event Triggered in the compiler [on Before Get Component Data] * * @return void * * @since 1.0 */ public function jcb_ce_onBeforeGetComponentData(&$context, $compiler) { // sync needed compiler properties with this plugin class $this->debugLinenr = $compiler->debugLinenr; $this->hhh = $compiler->hhh; $this->bbb = $compiler->bbb; $this->ddd = $compiler->ddd; } /** * Event Triggered in the compiler [on After Get] * * @return void * * @since 1.0 */ public function jcb_ce_onAfterGet(&$context, $compiler) { // check if this component needs a privacy plugin loaded if ($this->loadPrivacy) { $plugin = JPluginHelper::getPlugin('content', 'componentbuilderprivacytabs'); // check if this is json if (isset($plugin->params) && ComponentbuilderHelper::checkJson($plugin->params)) { // Convert the params field to an array. $registry = new Registry; $registry->loadString($plugin->params); $plugin->params = $registry->toArray(); } // now get the plugin ID if set if (isset($plugin->params['plugin']) && $plugin->params['plugin'] > 0) { // if linked it will only load it once $compiler->setJoomlaPlugin($plugin->params['plugin'], $compiler->componentData); } else { JFactory::getApplication()->enqueueMessage(JText::_('PLG_EXTENSION_COMPONENTBUILDERPRIVACYCOMPILER_YOU_DO_NOT_HAVE_A_GLOBAL_PRIVACY_PLUGIN_SETUP_SO_THE_INTEGRATION_WITH_JOOMLA_PRIVACY_SUITE_COULD_NOT_BE_BUILD'), 'Error'); $this->loadPrivacy= false; } } } /** * Event Triggered in the compiler [on Before Update Files] * * @return void * * @since 1.0 */ public function jcb_ce_onBeforeUpdateFiles(&$context, $compiler) { // check if privacy is to be loaded if ($this->loadPrivacy && ComponentbuilderHelper::checkArray($this->activeViews)) { // get compiler defaults $strictFieldExportPermissions = $compiler->strictFieldExportPermissions; $exportTextOnly = $compiler->exportTextOnly; // load the getPrivacyExport functions foreach ($this->activeViews as $id => &$view) { // set permissions based on view if (isset($view->params['privacy']['permissions'])) { $compiler->strictFieldExportPermissions = (int) $view->params['privacy']['permissions']; } // allow text only export $compiler->exportTextOnly = 1; // set view list name $viewName_list = ComponentbuilderHelper::safeString($view->name_list); // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); // load the function $compiler->fileContentDynamic[$viewName_list][$this->hhh . 'MODELEXPORTMETHOD' . $this->hhh] .= $compiler->setGetItemsModelMethod($viewName_single, $viewName_list, array('functionName' => 'getPrivacyExport', 'docDesc' => 'Method to get data during an export request.', 'type' => 'privacy')); // get the permissions building values for later if needed if ($compiler->strictFieldExportPermissions && isset($compiler->permissionFields[$viewName_single]) && ComponentbuilderHelper::checkArray($compiler->permissionFields[$viewName_single])) { $this->permissionFields[$viewName_single] = $compiler->permissionFields[$viewName_single]; } } // set compiler defaults $compiler->strictFieldExportPermissions = $strictFieldExportPermissions; $compiler->exportTextOnly = $exportTextOnly; // add helper classes $helper_strings = array('CUSTOM_HELPER_SCRIPT', 'SITE_CUSTOM_HELPER_SCRIPT', 'BOTH_CUSTOM_HELPER_SCRIPT'); $privacy_events = array('PrivacyCanRemoveData' => true, 'PrivacyExportRequest' => true, 'PrivacyRemoveData' => true); foreach ($helper_strings as $helper) { if (isset($compiler->fileContentStatic[$this->hhh . $helper . $this->hhh]) && ComponentbuilderHelper::checkString($compiler->fileContentStatic[$this->hhh . $helper . $this->hhh])) { foreach ($privacy_events as $privacy_event => &$add) { // check if the even is overwriten if (strpos($compiler->fileContentStatic[$this->hhh . $helper . $this->hhh], 'public static function on' . $privacy_event . '(') !== false) { $add = false; } } } } // add the events still needed $compiler->fileContentStatic[$this->hhh . 'BOTH_CUSTOM_HELPER_SCRIPT' . $this->hhh] .= str_replace(array_keys($compiler->placeholders), array_values($compiler->placeholders), $this->getHelperMethod($privacy_events)); } } /** * Event Triggered in the compiler [on Before Set Lang File Data] * * @return void * * @since 1.0 */ public function jcb_ce_onBeforeSetLangFileData(&$context, $compiler) { if (ComponentbuilderHelper::checkArray($this->languageArray)) { foreach($this->languageArray as $key => $string) { $compiler->setLangContent('site', $key, $string); } } } /** * get the Helper methods needed to integrate with Joomla Privacy Suite * * @param string $helperMethods The helper methods string * * @return void * * @since 1.0 */ protected function getHelperMethod(&$events) { $methods = ''; foreach ($events as $event => $add) { // check if the even should be added if ($add) { // add the event $this->{'set'.$event}($methods); } } // only add header if there was events added if (ComponentbuilderHelper::checkString($methods)) { $methods = PHP_EOL . PHP_EOL . $this->_t(1) . "//" . $this->setLine(__LINE__) . " <<<=== Privacy integration with Joomla Privacy suite ===>>>" . PHP_EOL . $methods; } return $methods; } /** * Set Privacy Can Remove Data * * @param string $methods The methods string * * @return void * */ protected function setPrivacyCanRemoveData(&$methods) { $methods .= PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Performs validation to determine if the data associated with a remove information request can be processed"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyPlugin \$plugin The plugin being processed"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyRemovalStatus \$status The status being set"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyTableRequest \$request The request record being processed"; $methods .= PHP_EOL . $this->_t(1) . " * @param JUser \$user The user account associated with this request if available"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return PrivacyRemovalStatus"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "public static function onPrivacyCanRemoveData(&\$plugin, &\$status, &\$request, &\$user)"; $methods .= PHP_EOL . $this->_t(1) . "{"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Bucket to get all reasons why removal not allowed"; $methods .= PHP_EOL . $this->_t(2) . "\$reasons = array();"; foreach ($this->activeViews as $view) { // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); // setup correct core target $coreLoad = false; if (isset($this->permissionCore[$viewName_single])) { $core = $this->permissionCore[$viewName_single]; $coreLoad = true; } // load the canDo from getActions helper method $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Check if user has permission to delete " . $view->name_list; // set the if statement based on the permission builder if ($coreLoad && isset($this->permissionBuilder[$core['core.delete']]) && ComponentbuilderHelper::checkArray($this->permissionBuilder[$core['core.delete']]) && in_array($viewName_single, $this->permissionBuilder[$core['core.delete']])) { $methods .= PHP_EOL . $this->_t(2) . "if (!\$user->authorise('" . $core['core.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "') && !\$user->authorise('" . $core['core.privacy.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } else { $methods .= PHP_EOL . $this->_t(2) . "if (!\$user->authorise('core.delete', 'com_" . $this->bbb . "component" . $this->ddd . "') && !\$user->authorise('" . $core['core.privacy.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } $methods .= PHP_EOL . $this->_t(2) . "{"; // set language key $lang_key = $view->placeholders[$this->bbb . "LANG_PREFIX" . $this->ddd] . '_PRIVACY_CANT_REMOVE_' . $view->placeholders[$this->bbb . "VIEWS" . $this->ddd]; // set language string $this->languageArray[$lang_key] = "You do not have permission to remove/delete ". $view->name_list . "."; $methods .= PHP_EOL . $this->_t(3) . "\$reasons[] = JTe" . "xt::_('" . $lang_key . "');"; $methods .= PHP_EOL . $this->_t(2) . "}"; // set language key $lang_key = $view->placeholders[$this->bbb . "LANG_PREFIX" . $this->ddd] . '_PRIVACY_CANT_REMOVE_CONTACT_SUPPORT'; } $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Check if any reasons were found not to allow removal"; $methods .= PHP_EOL . $this->_t(2) . "if (self::checkArray(\$reasons))"; $methods .= PHP_EOL . $this->_t(2) . "{"; $methods .= PHP_EOL . $this->_t(3) . "\$status->canRemove = false;"; // set language string $this->languageArray[$lang_key] = 'Please contact support for more details.'; $methods .= PHP_EOL . $this->_t(3) . "\$status->reason = implode(' ' . PHP_EOL, \$reasons) . ' ' . PHP_EOL . JTe" . "xt::_('" . $lang_key . "');"; $methods .= PHP_EOL . $this->_t(2) . "}"; $methods .= PHP_EOL . $this->_t(2) . "return \$status;"; $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; } /** * Set Privacy Export Request * * @param string $methods The methods string * * @return void * */ protected function setPrivacyExportRequest(&$methods) { $methods .= PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Processes an export request for Joomla core user data"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyPlugin \$plugin The plugin being processed"; $methods .= PHP_EOL . $this->_t(1) . " * @param DomainArray \$domains The array of domains"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyTableRequest \$request The request record being processed"; $methods .= PHP_EOL . $this->_t(1) . " * @param JUser \$user The user account associated with this request if available"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return PrivacyExportDomain[]"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "public static function onPrivacyExportRequest(&\$plugin, &\$domains, &\$request, &\$user)"; $methods .= PHP_EOL . $this->_t(1) . "{"; foreach ($this->activeViews as $view) { // set view list name $viewName_list = ComponentbuilderHelper::safeString($view->name_list); // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); // setup correct core target $coreLoad = false; if (isset($this->permissionCore[$viewName_single])) { $core = $this->permissionCore[$viewName_single]; $coreLoad = true; } // load the canDo from getActions helper method $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Check if user has permission to access " . $view->name_list; // set the if statement based on the permission builder if ($coreLoad && isset($core['core.access']) && isset($this->permissionBuilder['global'][$core['core.access']]) && ComponentbuilderHelper::checkArray($this->permissionBuilder['global'][$core['core.access']]) && in_array($viewName_single, $this->permissionBuilder['global'][$core['core.access']])) { $methods .= PHP_EOL . $this->_t(2) . "if (\$user->authorise('" . $core['core.access'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "') || \$user->authorise('" . $core['core.privacy.access'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } else { $methods .= PHP_EOL . $this->_t(2) . "if (\$user->authorise('" . $core['core.privacy.access'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } $methods .= PHP_EOL . $this->_t(2) . "{"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Get " . $view->name_single . " domain"; $methods .= PHP_EOL . $this->_t(3) . "\$domains[] = self::create" . ucfirst($viewName_list) . "Domain(\$plugin, \$user);"; $methods .= PHP_EOL . $this->_t(2) . "}"; } $methods .= PHP_EOL . $this->_t(2) . "return \$domains;"; $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; foreach ($this->activeViews as $view) { // set view list name $viewName_list = ComponentbuilderHelper::safeString($view->name_list); // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); $methods .= PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Create the domain for the " . $view->name_single; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param JTableUser \$user The JTableUser object to process"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return PrivacyExportDomain"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "protected static function create" . ucfirst($viewName_list) . "Domain(&\$plugin, &\$user)"; $methods .= PHP_EOL . $this->_t(1) . "{"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " create " . $view->name_list . " domain"; $methods .= PHP_EOL . $this->_t(2) . "\$domain = self::createDomain('" . $viewName_single . "', '" . $this->bbb . "component" . $this->ddd . "_" . $viewName_single . "_data');"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get database object"; $methods .= PHP_EOL . $this->_t(2) . "\$db = JFactory::getDbo();"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get all item ids of " . $view->name_list . " that belong to this user"; $methods .= PHP_EOL . $this->_t(2) . "\$query = \$db->getQuery(true)"; $methods .= PHP_EOL . $this->_t(3) . "->select('id')"; $methods .= PHP_EOL . $this->_t(3) . "->from(\$db->quoteName('#__" . $this->bbb . 'component' . $this->ddd . '_' . $viewName_single . "'));"; // get via custom script if (isset($view->params['privacy']['user_link']) && $view->params['privacy']['user_link'] == 3) { $methods .= PHP_EOL . str_replace(array_keys($view->placeholders), array_values($view->placeholders), $view->params['privacy']['custom_link']); } // just another field elseif (isset($view->params['privacy']['user_link']) && $view->params['privacy']['user_link'] == 2 && isset($view->params['privacy']['other_user_field'])) { // get the field name if (($field_name = $this->getFieldName($view->fields, $view->params['privacy']['other_user_field'])) !== false) { $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('" . $field_name . "') . ' = ' . \$db->quote(\$user->id));"; } else { // give a warning message (TODO) // stop any from loading $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " ==== ERROR ===== ERROR ====== (field name not found)"; $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('id') . ' = -2'; //" . $this->setLine(__LINE__) . " <-- this will never return any value. Check your [other user field] selected in the admin view privacy tab."; } } // get based on created by else { $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('created_by') . ' = ' . \$db->quote(\$user->id));"; } $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get all items for the " . $view->name_list . " domain"; $methods .= PHP_EOL . $this->_t(2) . "\$pks = \$db->setQuery(\$query)->loadColumn();"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get the " . $view->name_list . " model"; $methods .= PHP_EOL . $this->_t(2) . "\$model = self::getModel('" . $viewName_list . "', JPATH_ADMINISTRATOR . '/components/com_" . $this->bbb . "component" . $this->ddd . "');"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Get all item details of " . $view->name_list . " that belong to this user"; $methods .= PHP_EOL . $this->_t(2) . "\$items = \$model->getPrivacyExport(\$pks, \$user);"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " check if we have items since permissions could block the request"; $methods .= PHP_EOL . $this->_t(2) . "if (self::checkArray(\$items))"; $methods .= PHP_EOL . $this->_t(2) . "{"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Remove " . $view->name_single . " default columns"; $methods .= PHP_EOL . $this->_t(3) . "foreach (array('params', 'asset_id', 'checked_out', 'checked_out_time', 'created', 'created_by', 'modified', 'modified_by', 'published', 'ordering', 'access', 'version', 'hits') as \$column)"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "\$items = ArrayHelper::dropColumn(\$items, \$column);"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " load the items into the domain object"; $methods .= PHP_EOL . $this->_t(3) . "foreach (\$items as \$item)"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "\$domain->addItem(self::createItemFromArray(\$item, \$item['id']));"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . $this->_t(2) . "}"; $methods .= PHP_EOL . $this->_t(2) . "return \$domain;"; $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; } // we must add these helper methods $methods .= PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Create a new domain object"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param string \$name The domain's name"; $methods .= PHP_EOL . $this->_t(1) . " * @param string \$description The domain's description"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return PrivacyExportDomain"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @since 3.9.0"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "protected static function createDomain(\$name, \$description = '')"; $methods .= PHP_EOL . $this->_t(1) . "{"; $methods .= PHP_EOL . $this->_t(2) . "\$domain = new PrivacyExportDomain;"; $methods .= PHP_EOL . $this->_t(2) . "\$domain->name = \$name;"; $methods .= PHP_EOL . $this->_t(2) . "\$domain->description = \$description;"; $methods .= PHP_EOL . PHP_EOL . $this->_t(2) . "return \$domain;"; $methods .= PHP_EOL . $this->_t(1) . "}"; $methods .= PHP_EOL . PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Create an item object for an array"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param array \$data The array data to convert"; $methods .= PHP_EOL . $this->_t(1) . " * @param integer|null \$itemId The ID of this item"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return PrivacyExportItem"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @since 3.9.0"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "protected static function createItemFromArray(array \$data, \$itemId = null)"; $methods .= PHP_EOL . $this->_t(1) . "{"; $methods .= PHP_EOL . $this->_t(2) . "\$item = new PrivacyExportItem;"; $methods .= PHP_EOL . $this->_t(2) . "\$item->id = \$itemId;"; $methods .= PHP_EOL . PHP_EOL . $this->_t(2) . "foreach (\$data as \$key => \$value)"; $methods .= PHP_EOL . $this->_t(2) . "{"; $methods .= PHP_EOL . $this->_t(3) . "if (is_object(\$value))"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "\$value = (array) \$value;"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . PHP_EOL . $this->_t(3) . "if (is_array(\$value))"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "\$value = print_r(\$value, true);"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . PHP_EOL . $this->_t(3) . "\$field = new PrivacyExportField;"; $methods .= PHP_EOL . $this->_t(3) . "\$field->name = \$key;"; $methods .= PHP_EOL . $this->_t(3) . "\$field->value = \$value;"; $methods .= PHP_EOL . PHP_EOL . $this->_t(3) . "\$item->addField(\$field);"; $methods .= PHP_EOL . $this->_t(2) . "}"; $methods .= PHP_EOL . PHP_EOL . $this->_t(2) . "return \$item;"; $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; } /** * get the field name * * @param array $fields The fields array * @param int $id The field id * * @return string The field name * */ protected function getFieldName(&$fields, $id) { foreach ($fields as $field) { if ($field['field'] == $id) { return $field['base_name']; } } return false; } /** * Set Privacy Remove Data * * @param string $methods The methods string * * @return void * */ protected function setPrivacyRemoveData(&$methods) { $methods .= PHP_EOL . $this->_t(1) . "/**"; $methods .= PHP_EOL . $this->_t(1) . " * Removes the data associated with a remove information request"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param PrivacyTableRequest \$request The request record being processed"; $methods .= PHP_EOL . $this->_t(1) . " * @param JUser \$user The user account associated with this request if available"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return void"; $methods .= PHP_EOL . $this->_t(1) . " */"; $methods .= PHP_EOL . $this->_t(1) . "public static function onPrivacyRemoveData(&\$plugin, &\$request, &\$user)"; $methods .= PHP_EOL . $this->_t(1) . "{"; foreach ($this->activeViews as $view) { // set the anonymize switch $anonymize = false; if (isset($view->params['privacy']['anonymize']) && $view->params['privacy']['anonymize'] == 1 && isset($view->params['privacy']['anonymize_fields']) && ComponentbuilderHelper::checkArray($view->params['privacy']['anonymize_fields'], true)) { // Anonymize the data $anonymize = true; } // set view list name $viewName_list = ComponentbuilderHelper::safeString($view->name_list); // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); // setup correct core target $coreLoad = false; if (isset($this->permissionCore[$viewName_single])) { $core = $this->permissionCore[$viewName_single]; $coreLoad = true; } // load the canDo from getActions helper method $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " Check if user has permission to delet " . $view->name_list; // set the if statement based on the permission builder if ($coreLoad && isset($this->permissionBuilder[$core['core.delete']]) && ComponentbuilderHelper::checkArray($this->permissionBuilder[$core['core.delete']]) && in_array($viewName_single, $this->permissionBuilder[$core['core.delete']])) { $methods .= PHP_EOL . $this->_t(2) . "if (\$user->authorise('" . $core['core.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "') || \$user->authorise('" . $core['core.privacy.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } else { $methods .= PHP_EOL . $this->_t(2) . "if (\$user->authorise('core.delete', 'com_" . $this->bbb . "component" . $this->ddd . "') || \$user->authorise('" . $core['core.privacy.delete'] . "', 'com_" . $this->bbb . "component" . $this->ddd . "'))"; } $methods .= PHP_EOL . $this->_t(2) . "{"; // check if this is a plain delete, or it is a Anonymize if ($anonymize) { // anonymize the data $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Anonymize " . $view->name_single . " data"; $methods .= PHP_EOL . $this->_t(3) . "self::anonymize" . ucfirst($viewName_list) . "Data(\$plugin, \$user);"; } else { // just dump, delete the rows $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Remove " . $view->name_single . " data"; $methods .= PHP_EOL . $this->_t(3) . "self::remove" . ucfirst($viewName_list) . "Data(\$plugin, \$user);"; } $methods .= PHP_EOL . $this->_t(2) . "}"; } $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; foreach ($this->activeViews as $view) { // set the anonymize switch $anonymize = false; if (isset($view->params['privacy']['anonymize']) && $view->params['privacy']['anonymize'] == 1 && isset($view->params['privacy']['anonymize_fields']) && ComponentbuilderHelper::checkArray($view->params['privacy']['anonymize_fields'], true)) { // Anonymize the data $anonymize = true; } // set view list name $viewName_list = ComponentbuilderHelper::safeString($view->name_list); // set view single name $viewName_single = ComponentbuilderHelper::safeString($view->name_single); $methods .= PHP_EOL . $this->_t(1) . "/**"; // check if this is a plain delete, or it is a Anonymize if ($anonymize) { // Anonymize the data $methods .= PHP_EOL . $this->_t(1) . " * Anonymize the " . $view->name_single . " data"; } else { // Delete the rows $methods .= PHP_EOL . $this->_t(1) . " * Remove the " . $view->name_single . " data"; } $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @param JTableUser \$user The JTableUser object to process"; $methods .= PHP_EOL . $this->_t(1) . " *"; $methods .= PHP_EOL . $this->_t(1) . " * @return void"; $methods .= PHP_EOL . $this->_t(1) . " */"; // check if this is a plain delete, or it is a Anonymize if ($anonymize) { // Anonymize the data $methods .= PHP_EOL . $this->_t(1) . "protected static function anonymize" . ucfirst($viewName_list) . "Data(&\$plugin, &\$user)"; } else { // Delete the rows $methods .= PHP_EOL . $this->_t(1) . "protected static function remove" . ucfirst($viewName_list) . "Data(&\$plugin, &\$user)"; } $methods .= PHP_EOL . $this->_t(1) . "{"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get database object"; $methods .= PHP_EOL . $this->_t(2) . "\$db = JFactory::getDbo();"; $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get all item ids of " . $view->name_list . " that belong to this user"; $methods .= PHP_EOL . $this->_t(2) . "\$query = \$db->getQuery(true)"; $methods .= PHP_EOL . $this->_t(3) . "->select('id')"; $methods .= PHP_EOL . $this->_t(3) . "->from(\$db->quoteName('#__" . $this->bbb . 'component' . $this->ddd . '_' . $viewName_single . "'));"; // get via custom script if (isset($view->params['privacy']['user_link']) && $view->params['privacy']['user_link'] == 3) { $methods .= PHP_EOL . str_replace(array_keys($view->placeholders), array_values($view->placeholders), $view->params['privacy']['custom_link']); } // just another field elseif (isset($view->params['privacy']['user_link']) && $view->params['privacy']['user_link'] == 2 && isset($view->params['privacy']['other_user_field'])) { // get the field name if (($field_name = $this->getFieldName($view->fields, $view->params['privacy']['other_user_field'])) !== false) { $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('" . $field_name . "') . ' = ' . \$db->quote(\$user->id));"; } else { // give a warning message (TODO) // stop any from loading $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " ==== ERROR ===== ERROR ====== (field name not found)"; $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('id') . ' = -2'; //" . $this->setLine(__LINE__) . " <-- this will never return any value. Check your [other user field] selected in the admin view privacy tab."; } } // get based on created by else { $methods .= PHP_EOL . $this->_t(2) . "\$query->where(\$db->quoteName('created_by') . ' = ' . \$db->quote(\$user->id));"; } $methods .= PHP_EOL . $this->_t(2) . "//" . $this->setLine(__LINE__) . " get all items for the " . $view->name_list . " table that belong to this user"; $methods .= PHP_EOL . $this->_t(2) . "\$pks = \$db->setQuery(\$query)->loadColumn();"; $methods .= PHP_EOL .PHP_EOL . $this->_t(2) . "if (self::checkArray(\$pks))"; $methods .= PHP_EOL . $this->_t(2) . "{"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " get the " . $viewName_single . " model"; $methods .= PHP_EOL . $this->_t(3) . "\$model = self::getModel('" . $viewName_single . "', JPATH_ADMINISTRATOR . '/components/com_" . $this->bbb . "component" . $this->ddd . "');"; // check if this is a plain delete, or it is a Anonymize if ($anonymize) { // build the pseudoanonymised data array $_data_bucket = array(); $_random_bucket = array(); $_permission_bucket = array(); foreach ($view->params['privacy']['anonymize_fields'] as $row) { if (($field_name = $this->getFieldName($view->fields, $row['field'])) !== false) { if ('RANDOM' === $row['value']) { $_random_bucket[$field_name] = 8; // (TODO) make the size dynamic } $_data_bucket[] = PHP_EOL . $this->_t(4) . "'" . $field_name . "' => '" . $row['value'] ."'"; $_permission_bucket[$field_name] = $field_name; } } // Anonymize the data $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " this is the pseudoanonymised data array for " . $view->name_list; $methods .= PHP_EOL . $this->_t(3) . "\$pseudoanonymisedData = array("; $methods .= implode(',', $_data_bucket); $methods .= PHP_EOL . $this->_t(3) . ");"; // add the permissional removal of values the user has not right to view or access $hasPermissional = false; if (isset($this->permissionFields[$viewName_single]) && ComponentbuilderHelper::checkArray($this->permissionFields[$viewName_single])) { foreach ($this->permissionFields[$viewName_single] as $fieldName => $permission_options) { if (!$hasPermissional && isset($_permission_bucket[$fieldName])) { foreach($permission_options as $permission_option => $fieldType) { if (!$hasPermissional) { switch ($permission_option) { case 'access': case 'view': case 'edit': $hasPermissional = true; break; } } } } } // add the notes and get the global switch if ($hasPermissional) { $methods .= PHP_EOL . PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Get global permissional control activation. (default is inactive)"; $methods .= PHP_EOL . $this->_t(3) . "\$strict_permission_per_field = JComponentHelper::getParams('com_" . $this->bbb . "component" . $this->ddd . "')->get('strict_permission_per_field', 0);"; $methods .= PHP_EOL . $this->_t(3) . "if(\$strict_permission_per_field)"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "//" . $this->setLine(__LINE__) . " remove all fields that is not permitted to be changed"; foreach ($this->permissionFields[$viewName_single] as $fieldName => $permission_options) { if (isset($_permission_bucket[$fieldName])) { $methods .= PHP_EOL . $this->_t(4) . "if ("; $_permission_if = array(); foreach ($permission_options as $perm_key => $field_typrew) { $_permission_if[] = "!\$user->authorise('" . $viewName_single . "." . $perm_key . "." . $fieldName . "', 'com_" . $this->bbb . "component" . $this->ddd . "')"; } $methods .= implode(' || ', $_permission_if); $methods .= ")"; $methods .= PHP_EOL . $this->_t(4) . "{"; $methods .= PHP_EOL . $this->_t(5) . "unset(\$pseudoanonymisedData['". $fieldName . "']);"; $methods .= PHP_EOL . $this->_t(4) . "}"; } } $methods .= PHP_EOL . $this->_t(3) . "}"; } } $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " get the " . $view->name_list . " table"; $methods .= PHP_EOL . $this->_t(3) . "\$table = \$model->getTable();"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " check that we still have pseudoanonymised data for " . $view->name_list . " set"; $methods .= PHP_EOL . $this->_t(3) . "if (!self::checkArray(\$pseudoanonymisedData))"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "//" . $this->setLine(__LINE__) . " still archive all items"; $methods .= PHP_EOL . $this->_t(4) . "\$table->publish(\$pks, 2);"; $methods .= PHP_EOL . $this->_t(4) . "return false;"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Iterate the items to anonimize each one."; $methods .= PHP_EOL . $this->_t(3) . "foreach (\$pks as \$i => \$pk)"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "\$table->reset();"; $methods .= PHP_EOL . $this->_t(4) . "\$pseudoanonymisedData['id'] = \$pk;"; if (ComponentbuilderHelper::checkArray($_random_bucket)) { foreach ($_random_bucket as $fieldName => $size) { $methods .= PHP_EOL . $this->_t(4) . "if (isset(\$pseudoanonymisedData['" . $fieldName . "']))"; $methods .= PHP_EOL . $this->_t(4) . "{"; $methods .= PHP_EOL . $this->_t(5) . "\$pseudoanonymisedData['" . $fieldName . "'] = self::randomkey(" . (int) $size . ");"; $methods .= PHP_EOL . $this->_t(4) . "}"; } } $methods .= PHP_EOL . PHP_EOL . $this->_t(4) . "if (\$table->bind(\$pseudoanonymisedData))"; $methods .= PHP_EOL . $this->_t(4) . "{"; $methods .= PHP_EOL . $this->_t(5) . "\$table->store();"; $methods .= PHP_EOL . $this->_t(4) . "}"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " archive all items"; $methods .= PHP_EOL . $this->_t(3) . "\$table->publish(\$pks, 2);"; } else { // Delete the rows $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " get the " . $view->name_list . " table"; $methods .= PHP_EOL . $this->_t(3) . "\$table = \$model->getTable();"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Iterate the items to delete each one."; $methods .= PHP_EOL . $this->_t(3) . "foreach (\$pks as \$i => \$pk)"; $methods .= PHP_EOL . $this->_t(3) . "{"; $methods .= PHP_EOL . $this->_t(4) . "if (\$table->load(\$pk))"; $methods .= PHP_EOL . $this->_t(4) . "{"; $methods .= PHP_EOL . $this->_t(5) . "\$table->delete(\$pk);"; $methods .= PHP_EOL . $this->_t(4) . "}"; $methods .= PHP_EOL . $this->_t(3) . "}"; $methods .= PHP_EOL . $this->_t(3) . "//" . $this->setLine(__LINE__) . " Clear the component's cache"; $methods .= PHP_EOL . $this->_t(3) . "\$model->cleanCache();"; } $methods .= PHP_EOL . $this->_t(2) . "}"; $methods .= PHP_EOL . $this->_t(1) . "}" . PHP_EOL; } } /** * Set the line number in comments * * @param int $nr The line number * * @return void * */ protected function setLine($nr) { if ($this->debugLinenr) { return ' [Plugin-Privacy-Compiler ' . $nr . ']'; } return ''; } /** * Set the tab/space * * @param int $nr The number of tag/space * * @return string * */ protected function _t($nr) { // use global method for conformity return ComponentbuilderHelper::_t($nr); } }