├── .babelrc.json
├── .gitattributes
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── Akeeba DataCompliance for Joomla 4.iml
├── aws.xml
├── blade.xml
├── codeStyles
│ └── codeStyleConfig.xml
├── composerJson.xml
├── copyright
│ ├── DataCompliance.xml
│ └── profiles_settings.xml
├── dataSources.xml
├── encodings.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── libraries
│ └── Generated_files.xml
├── markdown.xml
├── misc.xml
├── modules.xml
├── phing.xml
├── php.xml
├── scopes
│ └── Copyright.xml
├── sqldialects.xml
├── vcs.xml
├── watcherTasks.xml
└── webResources.xml
├── CHANGELOG
├── LICENSE
├── README.md
├── RELEASENOTES.html
├── assets
└── email
│ ├── 0_make_lang_strings.php
│ ├── admin_admin.html
│ ├── admin_lifecycle.html
│ ├── admin_user.html
│ ├── user_admin.html
│ ├── user_lifecycle.html
│ ├── user_user.html
│ └── user_warnlifecycle.html
├── build.xml
├── build
├── build.properties
└── templates
│ ├── language
│ └── en-GB
│ │ └── pkg_datacompliance.sys.ini
│ ├── pkg_datacompliance.xml
│ └── release.yaml
├── component
├── backend
│ ├── access.xml
│ ├── assets
│ │ └── plugin
│ │ │ └── AbstractPlugin.php
│ ├── config.xml
│ ├── forms
│ │ ├── filter_consenttrails.xml
│ │ ├── filter_exporttrails.xml
│ │ ├── filter_lifecycle.xml
│ │ ├── filter_usertrails.xml
│ │ └── filter_wipetrails.xml
│ ├── language
│ │ └── en-GB
│ │ │ ├── com_datacompliance.ini
│ │ │ └── com_datacompliance.sys.ini
│ ├── layouts
│ │ └── akeeba
│ │ │ └── datacompliance
│ │ │ └── common
│ │ │ └── user.php
│ ├── services
│ │ └── provider.php
│ ├── sql
│ │ ├── install.mysql.utf8.sql
│ │ ├── uninstall.mysql.utf8.sql
│ │ └── updates
│ │ │ └── mysql
│ │ │ └── 4.0.0-20210904.sql
│ ├── src
│ │ ├── AbstractPlugin.php
│ │ ├── CliCommand
│ │ │ ├── AccountDelete.php
│ │ │ ├── LifecycleDelete.php
│ │ │ ├── LifecycleNotify.php
│ │ │ └── MixIt
│ │ │ │ ├── ConfigureIO.php
│ │ │ │ ├── MemoryInfo.php
│ │ │ │ └── TimeInfo.php
│ │ ├── Controller
│ │ │ ├── ConsenttrailsController.php
│ │ │ ├── ControlpanelController.php
│ │ │ ├── EmailtemplatesController.php
│ │ │ ├── ExporttrailsController.php
│ │ │ ├── LifecycleController.php
│ │ │ ├── OptionsController.php
│ │ │ ├── UsertrailsController.php
│ │ │ └── WipetrailsController.php
│ │ ├── Dispatcher
│ │ │ └── Dispatcher.php
│ │ ├── Extension
│ │ │ └── DataComplianceComponent.php
│ │ ├── Field
│ │ │ └── ArticleField.php
│ │ ├── Filter
│ │ │ └── .gitinclude
│ │ ├── Helper
│ │ │ ├── CacheCleaner.php
│ │ │ ├── ComponentParams.php
│ │ │ ├── Export.php
│ │ │ ├── MailTemplateHotFix.php
│ │ │ └── TemplateEmails.php
│ │ ├── Mixin
│ │ │ ├── ControllerCacheTrait.php
│ │ │ ├── ControllerEventsTrait.php
│ │ │ ├── ControllerRegisterTasksTrait.php
│ │ │ ├── ControllerReusableModelsTrait.php
│ │ │ ├── RunPluginsTrait.php
│ │ │ ├── TableAssertionTrait.php
│ │ │ ├── TableColumnAliasTrait.php
│ │ │ ├── TableCreateModifyTrait.php
│ │ │ ├── TriggerEventTrait.php
│ │ │ ├── ViewLoadAnyTemplateTrait.php
│ │ │ └── ViewTaskBasedEventsTrait.php
│ │ ├── Model
│ │ │ ├── ConsenttrailsModel.php
│ │ │ ├── ControlpanelModel.php
│ │ │ ├── ExportModel.php
│ │ │ ├── ExporttrailsModel.php
│ │ │ ├── LifecycleModel.php
│ │ │ ├── OptionsModel.php
│ │ │ ├── StatsModel.php
│ │ │ ├── UpgradeModel.php
│ │ │ ├── UsertrailsModel.php
│ │ │ ├── WipeModel.php
│ │ │ └── WipetrailsModel.php
│ │ ├── Provider
│ │ │ └── RouterFactory.php
│ │ ├── Router
│ │ │ └── RouterFactory.php
│ │ ├── Rule
│ │ │ └── .gitinclude
│ │ ├── Service
│ │ │ └── Html
│ │ │ │ └── DataCompliance.php
│ │ ├── Table
│ │ │ ├── AbstractTable.php
│ │ │ ├── ConsenttrailsTable.php
│ │ │ ├── ExporttrailsTable.php
│ │ │ ├── GetPropertiesAwareTrait.php
│ │ │ ├── UsertrailsTable.php
│ │ │ └── WipetrailsTable.php
│ │ └── View
│ │ │ ├── Consenttrails
│ │ │ └── HtmlView.php
│ │ │ ├── Controlpanel
│ │ │ └── HtmlView.php
│ │ │ ├── Emailtemplates
│ │ │ └── HtmlView.php
│ │ │ ├── Exporttrails
│ │ │ └── HtmlView.php
│ │ │ ├── Lifecycle
│ │ │ └── HtmlView.php
│ │ │ ├── Options
│ │ │ └── HtmlView.php
│ │ │ ├── Usertrails
│ │ │ └── HtmlView.php
│ │ │ └── Wipetrails
│ │ │ └── HtmlView.php
│ └── tmpl
│ │ ├── common
│ │ └── errorhandler.php
│ │ ├── consenttrails
│ │ ├── default.php
│ │ └── emptystate.php
│ │ ├── controlpanel
│ │ ├── default.php
│ │ ├── default_icons.php
│ │ ├── default_stats.php
│ │ └── joomla_eol.php
│ │ ├── emailtemplates
│ │ └── default.php
│ │ ├── exporttrails
│ │ ├── default.php
│ │ └── emptystate.php
│ │ ├── lifecycle
│ │ ├── default.php
│ │ └── emptystate.php
│ │ ├── options
│ │ ├── default.php
│ │ └── wipe.php
│ │ ├── usertrails
│ │ ├── default.php
│ │ └── emptystate.php
│ │ └── wipetrails
│ │ ├── default.php
│ │ └── emptystate.php
├── datacompliance.xml
├── frontend
│ ├── .htaccess
│ ├── language
│ │ └── en-GB
│ │ │ └── com_datacompliance.ini
│ ├── src
│ │ ├── Controller
│ │ │ └── OptionsController.php
│ │ ├── Dispatcher
│ │ │ └── Dispatcher.php
│ │ ├── Model
│ │ │ └── OptionsModel.php
│ │ ├── Service
│ │ │ └── Router.php
│ │ └── View
│ │ │ └── Options
│ │ │ └── HtmlView.php
│ ├── tmpl
│ │ └── options
│ │ │ ├── default.php
│ │ │ ├── default.xml
│ │ │ └── wipe.php
│ └── web.config
├── media
│ ├── css
│ │ ├── backend.css
│ │ ├── backend.css.map
│ │ ├── backend.scss
│ │ ├── j5.css
│ │ ├── j5.css.map
│ │ ├── j5.scss
│ │ └── sources
│ │ │ └── _title_icon.scss
│ ├── fonts
│ │ └── Akeeba-Products.woff
│ ├── joomla.asset.json
│ └── js
│ │ ├── controlpanel.js
│ │ ├── controlpanel.min.js
│ │ ├── controlpanel.min.js.map
│ │ ├── options.js
│ │ ├── options.min.js
│ │ └── options.min.js.map
└── script.datacompliance.php
├── composer.json
├── composer.lock
├── documentation
├── datacompliance.xml
└── images
│ ├── control_panel.png
│ └── self_service_page.png
├── modules
├── admin
│ └── .gitinclude
└── site
│ └── .gitinclude
├── old
└── cli
│ └── datacompliance_audit_replay.php
└── plugins
├── console
└── datacompliance
│ ├── .htaccess
│ ├── datacompliance.xml
│ ├── language
│ ├── en-GB
│ │ ├── plg_console_datacompliance.ini
│ │ └── plg_console_datacompliance.sys.ini
│ └── index.html
│ ├── services
│ └── provider.php
│ ├── src
│ └── Extension
│ │ └── DataCompliance.php
│ └── web.config
├── datacompliance
├── ars
│ ├── .htaccess
│ ├── ars.xml
│ ├── language
│ │ └── en-GB
│ │ │ ├── plg_datacompliance_ars.ini
│ │ │ └── plg_datacompliance_ars.sys.ini
│ ├── services
│ │ └── provider.php
│ ├── src
│ │ └── Extension
│ │ │ └── ARS.php
│ └── web.config
├── ats
│ ├── .htaccess
│ ├── ats.xml
│ ├── language
│ │ └── en-GB
│ │ │ ├── plg_datacompliance_ats.ini
│ │ │ └── plg_datacompliance_ats.sys.ini
│ ├── services
│ │ └── provider.php
│ ├── src
│ │ └── Extension
│ │ │ └── ATS.php
│ └── web.config
├── email
│ ├── .htaccess
│ ├── email.xml
│ ├── language
│ │ └── en-GB
│ │ │ ├── plg_datacompliance_email.ini
│ │ │ └── plg_datacompliance_email.sys.ini
│ ├── services
│ │ └── provider.php
│ ├── src
│ │ └── Extension
│ │ │ └── Email.php
│ └── web.config
├── joomla
│ ├── .htaccess
│ ├── joomla.xml
│ ├── language
│ │ └── en-GB
│ │ │ ├── plg_datacompliance_joomla.ini
│ │ │ └── plg_datacompliance_joomla.sys.ini
│ ├── services
│ │ └── provider.php
│ ├── src
│ │ └── Extension
│ │ │ └── Joomla.php
│ └── web.config
├── loginguard
│ ├── .htaccess
│ ├── language
│ │ └── en-GB
│ │ │ ├── plg_datacompliance_loginguard.ini
│ │ │ └── plg_datacompliance_loginguard.sys.ini
│ ├── loginguard.xml
│ ├── services
│ │ └── provider.php
│ ├── src
│ │ └── Extension
│ │ │ └── LoginGuard.php
│ └── web.config
└── s3
│ ├── .htaccess
│ ├── language
│ └── en-GB
│ │ ├── plg_datacompliance_s3.ini
│ │ └── plg_datacompliance_s3.sys.ini
│ ├── s3.xml
│ ├── services
│ └── provider.php
│ ├── src
│ └── Extension
│ │ └── S3.php
│ └── web.config
├── system
└── datacompliance
│ ├── .htaccess
│ ├── datacompliance.xml
│ ├── language
│ └── en-GB
│ │ ├── plg_system_datacompliance.ini
│ │ └── plg_system_datacompliance.sys.ini
│ ├── services
│ └── provider.php
│ ├── src
│ └── Extension
│ │ └── DataCompliance.php
│ └── web.config
└── user
└── datacompliance
├── .htaccess
├── datacompliance.xml
├── datacompliance
├── datacompliance.xml
└── list.xml
├── language
└── en-GB
│ ├── plg_user_datacompliance.ini
│ └── plg_user_datacompliance.sys.ini
├── services
└── provider.php
├── src
├── Extension
│ └── DataCompliance.php
└── Field
│ └── DatacomplianceField.php
└── web.config
/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "useBuiltIns": "entry",
7 | "corejs": {
8 | "version": "3.9",
9 | "proposals": true
10 | }
11 | }
12 | ],
13 | [
14 | "minify",
15 | {
16 | "builtIns": false,
17 | "removeConsole": false
18 | }
19 | ]
20 | ],
21 | "comments": false,
22 | "ignore": [
23 | "component/media/js/*.min.js"
24 | ]
25 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text eol=lf
3 |
4 | # Explicitly declare text files you want to always be normalized and converted
5 | # to native line endings on checkout.
6 | *.php text eol=lf
7 | *.phps text eol=lf
8 | *.inc text eol=lf
9 | *.js text eol=lf
10 | *.css text eol=lf
11 | *.ini text eol=lf
12 | *.json text eol=lf
13 | *.htm text eol=lf
14 | *.html text eol=lf
15 | *.xml text eol=lf
16 | *.xslt text eol=lf
17 | *.svg text eol=lf
18 | *.txt text eol=lf
19 | *.md text eol=lf
20 | *.sh text eol=lf
21 | CHANGELOG text eol=lf
22 | README text eol=lf
23 | RELEASENOTES text eol=lf
24 |
25 | # Declare files that will always have CRLF line endings on checkout.
26 | *.sln text eol=crlf
27 |
28 | # Denote all files that are truly binary and should not be modified.
29 | *.acorn binary
30 | *.png binary
31 | *.jpg binary
32 | *.jpeg binary
33 | *.z binary
34 | *.gif binary
35 | *.jpa binary
36 | *.jps binary
37 | *.zip binary
38 | *.dll binary
39 | *.exe binary
40 | *.jar binary
41 | *.phar binary
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Package XML file (copy)
2 | /pkg_*.xml
3 |
4 | ### Node Modules
5 | /node_modules
6 |
7 | ### Component
8 | # Generated files in the component folder
9 | /component/backend/datacompliance.xml
10 |
11 | # Old component being migrated
12 | /old
13 |
14 | ### Release files
15 | /release/*
16 |
17 | ### Composer
18 | /component/backend/vendor
19 |
20 | ### Other generated files
21 | /assets/email/*.ini
22 | .DS_Store
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Akeeba DataCompliance for Joomla 4
--------------------------------------------------------------------------------
/.idea/Akeeba DataCompliance for Joomla 4.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/aws.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/composerJson.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/copyright/DataCompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/dataSources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | mysql.8
6 | true
7 | true
8 | $USER_HOME$/Sites/boot4/configuration.php
9 | com.mysql.cj.jdbc.Driver
10 | jdbc:mysql://127.0.0.1:3306/boot4
11 | $ProjectFileDir$
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/libraries/Generated_files.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/markdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/phing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | $USER_HOME$/.composer/vendor/bin/phing
14 |
15 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/scopes/Copyright.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/sqldialects.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/watcherTasks.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/.idea/webResources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RELEASENOTES.html:
--------------------------------------------------------------------------------
1 |
What's new?
2 |
3 |
4 | Workaround for Joomla! 5.2 broken mail template layout . Joomla! 5.2 introduced the new Mail Template Layout feature. Unfortunately, this feature is breaking URLs in emails by prefixing them with the site's URL even if they are absolute URLs. Something like https://www.akeeba.com
will turn into this jumbled mess https://www.example.com/https://www.akeeba.com
. This happens to all URLs in anchor (<a>
), link, and a few other HTML tags and affects all URLs which come from an email template variable – even those in mail templates of core Joomla! components such as the users component (think account activation, username reminders, password reset, etc). There was simple no QA done before the release of Joomla! 5.2. While we cannot fix Joomla! 5.2 ourselves and we cannot ask our clients to disable the Mail Template Layout feature (it is a nice feature, despite its shoddy implementation), we can work around the problems it brought with it. We added this workaround, as well as a toggle in the component's Options in case you need to disable this workaround, presumably when Joomla finally addresses this issue.
5 |
--------------------------------------------------------------------------------
/assets/email/0_make_lang_strings.php:
--------------------------------------------------------------------------------
1 | loadHTML($html);
11 |
12 | $title = $dom->getElementsByTagName('title')->item(0)->nodeValue;
13 | $bodyNode = $dom->getElementsByTagName('body')->item(0);
14 | $bodyHtml = trim(str_replace(['', ''], ['', ''], $dom->saveHTML($bodyNode)));
15 | $bodyText = str_replace("\n", '\\n', strip_tags(str_replace('', '\n' . str_repeat('-', 70), $bodyHtml)));
16 | $bodyHtml = str_replace("\n", '\\n', $bodyHtml);
17 |
18 | $title = str_replace('"', '\\"', $title);
19 | $bodyHtml = str_replace('"', '\\"', $bodyHtml);
20 | $bodyText = str_replace('"', '\\"', $bodyText);
21 |
22 | $key = strtoupper(basename($file, '.html'));
23 |
24 | echo 'COM_DATACOMPLIANCE_MAIL_' . $key . '_SUBJECT="' . $title . "\"\n";
25 | echo 'COM_DATACOMPLIANCE_MAIL_' . $key . '_BODY="' . $bodyText . "\"\n";
26 | echo 'COM_DATACOMPLIANCE_MAIL_' . $key . '_BODY_HTML="' . $bodyHtml . "\"\n";
27 | }
28 |
29 | ob_start();
30 |
31 | $di = new DirectoryIterator(__DIR__);
32 |
33 | /** @var DirectoryIterator $file */
34 | foreach ($di as $file)
35 | {
36 | if (!$file->isFile())
37 | {
38 | continue;
39 | }
40 |
41 | if ($file->getExtension() != 'html')
42 | {
43 | continue;
44 | }
45 |
46 | parseHtml($file->getPathname());
47 |
48 | echo "\n";
49 | }
50 |
51 | $contents = ob_get_clean();
52 | file_put_contents('en-GB.ini', $contents);
53 |
54 | echo $contents;
55 |
--------------------------------------------------------------------------------
/assets/email/admin_admin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | An administrator has deleted a user profile on {SITENAME}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
An administrator has deleted the user profile of user #{ID} on {sitename}.
12 |
The account deletion was performed in accordance with the provisions of the European Union''s General Data Protection Regulation (GDPR) and / or equivalent laws abroad.
13 |
The exact actions which took place on our system are as follows:
14 |
{ACTIONS}
15 |
Best regards,
16 |
The {sitename} team
17 |
18 |
You are receiving this automatic email message because a user profile on {sitename} has been deleted. Do not reply to this email, it''s sent from an unmonitored email address.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/assets/email/admin_lifecycle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Automatic deletion of a user profile on {sitename}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
The user profile of user #{ID} has been automatically deleted on {sitename} due to the account life cycle policy as defined by the Data Compliance plugins running on the site. The account was determined to be inactive.
12 |
The account deletion was performed in accordance with the provisions of the European Union's General Data Protection Regulation (GDPR) and / or equivalent laws abroad.
13 |
The exact actions which took place on our system are as follows:
14 |
{ACTIONS}
15 |
Best regards,
16 |
The {sitename} team
17 |
18 |
You are receiving this automatic email message because a user profile on {sitename} has been deleted. Do not reply to this email, it''s sent from an unmonitored email address.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/assets/email/admin_user.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | A user has deleted their user profile on {sitename}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
User #{ID} has deleted their user profile on {sitename}.
12 |
The account deletion was performed in accordance with the provisions of the European Union''s General Data Protection Regulation (GDPR) and / or equivalent laws abroad.
13 |
The exact actions which took place on our system are as follows:
14 |
{ACTIONS}
15 |
Best regards,
16 |
The {sitename} team
17 |
18 |
You are receiving this automatic email message because a user profile on {sitename} has been deleted. Do not reply to this email, it''s sent from an unmonitored email address.
19 |
20 |
21 |
--------------------------------------------------------------------------------
/assets/email/user_admin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | An administrator has deleted your user profile on {sitename}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
KEEP THIS EMAIL FOR YOUR RECORDS .
12 |
We would like to notify you that your user profile on {sitename} has been deleted by an administrator. The account deletion was performed in accordance with
13 | the provisions of the European Union''s General Data Protection Regulation (GDPR) and / or equivalent laws abroad.
14 |
The exact actions which took place on our system to delete your user account are as follows:
15 |
{ACTIONS}
16 |
Please note that this email was prepared right before the deletion took place, that''s how we were able to send it and address it to you.
17 |
Now that your account has been deleted from our system you are no longer considered a client (past, current or prospective) of ours. Furthermore, according to the law
18 | (GDPR), it''s as though you have never been our client.
19 |
This is the final communication you are receiving from us notwithstanding any response to any communication that you initiate in the future or have
20 | already initiated outside our web site such as but not limited to email, social media, electronic messaging platforms, letter, telephone, short message service (SMS
21 | a.k.a. "texts") or in person.
22 |
Thank you for having used our services!
23 |
Best regards,
24 |
The {sitename} team
25 |
26 |
You are receiving this automatic email message because your user profile on {sitename} has been deleted. Do not reply to this email, it''s sent from an unmonitored email address.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/assets/email/user_user.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | You have deleted your user profile on {sitename}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
KEEP THIS EMAIL FOR YOUR RECORDS .
12 |
We would like to notify you that your user profile on {sitename} has been deleted per your request. The account deletion was performed in accordance with the provisions
13 | of the European Union's General Data Protection Regulation (GDPR) and / or equivalent laws abroad.
14 |
The exact actions which took place on our system to delete your user account are as follows:
15 |
{ACTIONS}
16 |
Please note that this email was prepared right before the deletion took place, that's how we were able to send it and address it to you.
17 |
Now that your account has been deleted from our system you are no longer considered a client (past, current or prospective) of ours. Furthermore, according to the law
18 | (GDPR), it's as though you have never been our client.
19 |
This is the final communication you are receiving from us notwithstanding any response to any communication that you initiate in the future or have already
20 | initiated outside our web site such as but not limited to email, social media, electronic messaging platforms, letter, telephone, short message service (SMS a.k.a.
21 | "texts") or in person.
22 |
Thank you for having used our services!
23 |
Best regards,
24 |
The {sitename} team
25 |
26 |
You are receiving this automatic email message because your user profile on {sitename} has been deleted. Do not reply to this email, it's sent from an unmonitored email address.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/assets/email/user_warnlifecycle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Your user account on {sitename} will be deleted on {DELETEDATE}
6 |
7 |
8 |
9 |
10 |
Hello {NAME},
11 |
PLEASE DO NOT IGNORE THIS EMAIL.
12 |
On May 25th, 2018 the European Union''s General Data Protection Regulation (GDPR) came into effect. According to the GDPR, inactive user accounts and all their personally identifiable information must be permanently deleted. Account inactivity is determined by several factors, such as when was the last time you logged into your account on our site.
13 |
Your user account with the username “{USERNAME}” has been determined to be inactive and will be permanently deleted after {DELETEDATE} .
14 |
If you wish to prevent the deletion of your user account and all of its information you can simply log into our site before {DELETEDATE} .
15 |
If neither of these conditions are met, your account will be deleted on {DELETEDATE}. More specifically, the following actions will take place:
16 |
{ACTIONS}
17 |
After your account will have been deleted:
18 |
19 | you can no longer log in (your account has been removed )
20 | all your information has been deleted or anonymized per the actions listed above permanently and can never be retrieved or restored again
21 | according to the provisions of the law you are no longer considered a client (past, current or prospective) of ours and we cannot even check whether you have ever been a client of ours
22 |
23 |
We would like to kindly remind you that deleting your user account is the direct result of a LEGAL REQUIREMENT . We do not do this to inconvenience you. We will delete your information because we are forced to do so by the law (we have no choice). Moreover, it is ILLEGAL for us to keep a copy of your information after deleting it, therefore we will not be able to restore your deleted information even if you ask us.
24 |
Best regards,
25 |
The {sitename} team
26 |
27 |
You are receiving this automatic email message because your user profile on {sitename} has been deleted. Do not reply to this email, it''s sent from an unmonitored email address.
28 |
29 |
30 |
--------------------------------------------------------------------------------
/build/build.properties:
--------------------------------------------------------------------------------
1 | ftpdeploy.pattern.core=pkg_datacompliance-*.zip
2 | ftpdeploy.path.core=files/dev/datacompliance
3 |
4 | release.method=yaml
5 |
6 | build.component=datacompliance
7 | build.has_pro=0
8 |
--------------------------------------------------------------------------------
/build/templates/language/en-GB/pkg_datacompliance.sys.ini:
--------------------------------------------------------------------------------
1 | PKG_DATACOMPLIANCE="Akeeba Data Compliance package"
2 | PKG_DATACOMPLIANCE_XML_DESCRIPTION="Akeeba Data Compliance. A tool to facilitate GDPR conformance of your Joomla!™ sites."
--------------------------------------------------------------------------------
/build/templates/pkg_datacompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | pkg_datacompliance
10 | Nicholas K. Dionysopoulos
11 | ##DATE##
12 | datacompliance
13 | ##VERSION##
14 | https://www.akeeba.com
15 | Akeeba Ltd
16 | https://www.akeeba.com
17 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
18 | GNU GPL v3 or later
19 | PKG_DATACOMPLIANCE_XML_DESCRIPTION
20 | true
21 |
22 |
23 | com_datacompliance.zip
24 |
25 | plg_console_datacompliance.zip
26 |
27 | plg_datacompliance_email.zip
28 | plg_datacompliance_joomla.zip
29 | plg_datacompliance_ars.zip
30 | plg_datacompliance_ats.zip
31 | plg_datacompliance_loginguard.zip
32 | plg_datacompliance_s3.zip
33 |
34 | plg_system_datacompliance.zip
35 |
36 | plg_user_datacompliance.zip
37 |
38 |
39 |
40 | en-GB/pkg_datacompliance.sys.ini
41 |
42 |
43 | script.datacompliance.php
44 |
45 |
46 | https://cdn.akeeba.com/updates/pkgdatacompliance.xml
47 |
48 |
49 |
--------------------------------------------------------------------------------
/build/templates/release.yaml:
--------------------------------------------------------------------------------
1 | # Basic release information
2 | release:
3 | version: '%%VERSION%%'
4 | date: '%%DATE%%'
5 | category: 56
6 | access: 1
7 | release_notes: '%%DEFAULT_RELEASE_NOTES%%'
8 | changelog: '%%DEFAULT_CHANGELOG%%'
9 |
10 | # Akeeba Release System API connection
11 | api:
12 | type: 'joomla'
13 | endpoint: '%%API.ENDPOINT%%'
14 | connector: '%%API.CONNECTOR%%'
15 | token: '%%API.TOKEN%%'
16 | cacert: '%%CUSTOMCACERT%%'
17 |
18 | steps: [%%RELEASESTEPS%%]
19 |
20 | # File upload connections
21 | connections:
22 | s3:
23 | type: 's3'
24 | access: '%%S3.ACCESS%%'
25 | secret: '%%S3.SECRET%%'
26 | bucket: '%%S3.BUCKET%%'
27 | tls: true
28 | signature: '%%S3.SIGNATURE%%'
29 | region: '%%S3.REGION%%'
30 | directory: 'downloads/datacompliance'
31 | cdnhostname: '%%S3.CDNHOSTNAME%%'
32 | acl: 'public-read'
33 | storage_class: 'STANDARD'
34 | maximum_age: 600
35 |
36 | # Release source files configuration.
37 | files:
38 | -
39 | title: 'Akeeba DataCompliance'
40 | connection: s3
41 | source: '%%RELEASEDIR%%/pkg_datacompliance-*.zip'
42 | access: 1
43 | -
44 | title: 'Documentation (PDF)'
45 | connection: s3
46 | source: '%%RELEASEDIR%%/datacompliance.pdf'
47 | access: 1
48 |
49 | # Update sources
50 | updates:
51 | -
52 | title: 'Akeeba DataCompliance updates'
53 | connection: s3
54 | directory: 'updates'
55 | stream: 42
56 | base_name: 'pkgdatacompliance'
57 | formats:
58 | - 'xml'
59 |
--------------------------------------------------------------------------------
/component/backend/access.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/component/backend/assets/plugin/AbstractPlugin.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/component/backend/forms/filter_exporttrails.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/component/backend/forms/filter_lifecycle.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/component/backend/forms/filter_usertrails.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/component/backend/forms/filter_wipetrails.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/component/backend/language/en-GB/com_datacompliance.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | COM_DATACOMPLIANCE="Akeeba Data Compliance"
6 | COM_DATACOMPLIANCE_XML_DESCRIPTION="A GDRP compliance tool for Joomla! sites"
7 |
8 | COM_DATAWIPE_ACL_VIEW_TRAIL="Audit trails"
9 | COM_DATAWIPE_ACL_VIEW_TRAIL_DESC="View audit trails"
10 | COM_DATAWIPE_ACL_WIPE="Delete profiles"
11 | COM_DATAWIPE_ACL_WIPE_DESC="Delete profiles of other users"
12 | COM_DATAWIPE_ACL_EXPORT="Export profiles"
13 | COM_DATAWIPE_ACL_EXPORT_DESC="Export the personal information profiles of other users"
14 |
15 | COM_DATACOMPLIANCE_MENUMANAGER_VIEW_OPTIONS_LABEL="Data Processing Options"
16 | COM_DATACOMPLIANCE_MENUMANAGER_VIEW_OPTIONS_DESC="Show the Data Processing Options page to the user. They can manage their processing consent and export or delete their profile."
17 |
--------------------------------------------------------------------------------
/component/backend/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider(new MVCFactory('Akeeba\\Component\\DataCompliance'));
33 | $container->registerServiceProvider(new ComponentDispatcherFactory('Akeeba\\Component\\DataCompliance'));
34 | $container->registerServiceProvider(new RouterFactory('\\Akeeba\\Component\\DataCompliance'));
35 |
36 | $container->set(
37 | ComponentInterface::class,
38 | function (Container $container) {
39 | $component = new DataComplianceComponent($container->get(ComponentDispatcherFactoryInterface::class));
40 |
41 | $component->setRegistry($container->get(Registry::class));
42 | $component->setMVCFactory($container->get(MVCFactoryInterface::class));
43 | $component->setRouterFactory($container->get(RouterFactoryInterface::class));
44 |
45 | return $component;
46 | }
47 | );
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/component/backend/sql/install.mysql.utf8.sql:
--------------------------------------------------------------------------------
1 | /**
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | CREATE TABLE IF NOT EXISTS `#__datacompliance_exporttrails` (
8 | `datacompliance_exporttrail_id` bigint(20) NOT NULL AUTO_INCREMENT,
9 | `user_id` bigint(20) unsigned NOT NULL,
10 | `created_on` datetime NOT NULL,
11 | `created_by` bigint(20) NOT NULL,
12 | `requester_ip` varchar(255) NOT NULL,
13 | PRIMARY KEY (`datacompliance_exporttrail_id`),
14 | KEY `#__datacompliance_exporttrail_user` (`user_id`)
15 | ) DEFAULT COLLATE utf8mb4_unicode_ci;
16 |
17 |
18 | CREATE TABLE IF NOT EXISTS `#__datacompliance_wipetrails` (
19 | `datacompliance_wipetrail_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
20 | `user_id` bigint(20) NOT NULL,
21 | `type` enum ('lifecycle','user','admin') NOT NULL DEFAULT 'user',
22 | `created_on` datetime NOT NULL,
23 | `created_by` bigint(20) NOT NULL,
24 | `requester_ip` varchar(255) NOT NULL,
25 | `items` longtext,
26 | PRIMARY KEY (`datacompliance_wipetrail_id`)
27 | ) DEFAULT COLLATE utf8mb4_unicode_ci;
28 |
29 | CREATE TABLE IF NOT EXISTS `#__datacompliance_consenttrails` (
30 | `created_on` datetime NOT NULL,
31 | `created_by` bigint(20) NOT NULL,
32 | `requester_ip` varchar(255) NOT NULL,
33 | `enabled` int(1) NOT NULL DEFAULT 0,
34 | PRIMARY KEY (`created_by`)
35 | ) DEFAULT COLLATE utf8mb4_unicode_ci;
36 |
37 |
38 | CREATE TABLE IF NOT EXISTS `#__datacompliance_usertrails` (
39 | `datacompliance_usertrail_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
40 | `user_id` bigint(20) NOT NULL,
41 | `created_on` datetime NOT NULL,
42 | `created_by` bigint(20) NOT NULL,
43 | `requester_ip` varchar(255) NOT NULL,
44 | `items` longtext,
45 | PRIMARY KEY (`datacompliance_usertrail_id`)
46 | ) DEFAULT COLLATE utf8mb4_unicode_ci;
47 |
48 | DROP TABLE IF EXISTS `#__datacompliance_cookietrails`;
49 |
50 | DROP TABLE IF EXISTS `#__datacompliance_emailtemplates`;
--------------------------------------------------------------------------------
/component/backend/sql/uninstall.mysql.utf8.sql:
--------------------------------------------------------------------------------
1 | /**
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | DROP TABLE IF EXISTS `#__datacompliance_exporttrails`;
8 | DROP TABLE IF EXISTS `#__datacompliance_wipetrails`;
9 | DROP TABLE IF EXISTS `#__datacompliance_consenttrails`;
10 | DROP TABLE IF EXISTS `#__datacompliance_usertrails`;
11 | DROP TABLE IF EXISTS `#__datacompliance_cookietrails`;
--------------------------------------------------------------------------------
/component/backend/sql/updates/mysql/4.0.0-20210904.sql:
--------------------------------------------------------------------------------
1 | /**
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | # Drop old tables on update
8 | DROP TABLE IF EXISTS `#__datacompliance_emailtemplates`;
9 | DROP TABLE IF EXISTS `#__datacompliance_cookietrails`;
10 |
11 | # Convert all tables to InnoDB
12 | ALTER TABLE `#__datacompliance_exporttrails` ENGINE InnoDB;
13 | ALTER TABLE `#__datacompliance_wipetrails` ENGINE InnoDB;
14 | ALTER TABLE `#__datacompliance_consenttrails` ENGINE InnoDB;
15 | ALTER TABLE `#__datacompliance_usertrails` ENGINE InnoDB;
--------------------------------------------------------------------------------
/component/backend/src/AbstractPlugin.php:
--------------------------------------------------------------------------------
1 | autoloadLanguage = true;
42 |
43 | parent::__construct($subject, $config);
44 | }
45 |
46 | /**
47 | * Formats a number of bytes in human readable format
48 | *
49 | * @param int $size The size in bytes to format, e.g. 8254862
50 | *
51 | * @return string The human-readable representation of the byte size, e.g. "7.87 Mb"
52 | *
53 | * @since 3.0.0
54 | */
55 | protected function formatByteSize(int $size): string
56 | {
57 | $unit = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
58 |
59 | return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
60 | }
61 |
62 | /**
63 | * Returns the current memory usage, formatted human readable
64 | *
65 | * @return string
66 | */
67 | protected function memUsage(): string
68 | {
69 | if (function_exists('memory_get_usage'))
70 | {
71 | $size = memory_get_usage();
72 |
73 | return $this->formatByteSize($size);
74 | }
75 | else
76 | {
77 | return "(unknown)";
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/component/backend/src/CliCommand/MixIt/ConfigureIO.php:
--------------------------------------------------------------------------------
1 | cliInput = $input;
48 | $this->ioStyle = new SymfonyStyle($input, $output);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/component/backend/src/CliCommand/MixIt/MemoryInfo.php:
--------------------------------------------------------------------------------
1 | [1, 'second'],
52 | 'm' => [60, 'minute'],
53 | 'h' => [60 * 60, 'hour'],
54 | 'd' => [60 * 60 * 24, 'day'],
55 | 'y' => [60 * 60 * 24 * 365, 'year'],
56 | ];
57 |
58 | if ($measureBy == '')
59 | {
60 | $usemeasure = 's';
61 |
62 | for ($i = 0; $i < count($calcNum); $i++)
63 | {
64 | if ($clean <= $calcNum[$i][1])
65 | {
66 | $usemeasure = $calcNum[$i][0];
67 | $i = count($calcNum);
68 | }
69 | }
70 | }
71 | else
72 | {
73 | $usemeasure = $measureBy;
74 | }
75 |
76 | $datedifference = floor($clean / $calc[$usemeasure][0]);
77 |
78 | if ($autoText == true && ($currentDateTime == time()))
79 | {
80 | if ($raw < 0)
81 | {
82 | $prospect = ' from now';
83 | }
84 | else
85 | {
86 | $prospect = ' ago';
87 | }
88 | }
89 | else
90 | {
91 | $prospect = '';
92 | }
93 |
94 | if ($referenceDateTime != 0)
95 | {
96 | if ($datedifference == 1)
97 | {
98 | return $datedifference . ' ' . $calc[$usemeasure][1] . ' ' . $prospect;
99 | }
100 | else
101 | {
102 | return $datedifference . ' ' . $calc[$usemeasure][1] . 's ' . $prospect;
103 | }
104 | }
105 | else
106 | {
107 | return 'No input time referenced.';
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/component/backend/src/Controller/ConsenttrailsController.php:
--------------------------------------------------------------------------------
1 | unregisterTask('unpublish');
30 | $this->unregisterTask('archive');
31 | $this->unregisterTask('trash');
32 | $this->unregisterTask('report');
33 | $this->unregisterTask('orderup');
34 | $this->unregisterTask('orderdown');
35 | $this->unregisterTask('delete');
36 | $this->unregisterTask('publish');
37 | $this->unregisterTask('reorder');
38 | $this->unregisterTask('saveorder');
39 | $this->unregisterTask('checkin');
40 | $this->unregisterTask('saveOrderAjax');
41 | $this->unregisterTask('runTransition');
42 | }
43 |
44 | public function getModel($name = 'Consenttrails', $prefix = 'Administrator', $config = ['ignore_request' => true])
45 | {
46 | return parent::getModel($name, $prefix, $config);
47 | }
48 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/ControlpanelController.php:
--------------------------------------------------------------------------------
1 | registerControllerTasks();
43 | }
44 |
45 |
46 | /**
47 | * Get user statistics (active / inactive users), used for displaying graphs
48 | *
49 | * @since 1.0.0
50 | * @noinspection PhpPossiblePolymorphicInvocationInspection
51 | */
52 | public function userstats(): void
53 | {
54 | $stats = $this->getCache()->get(function () {
55 | return $this->getModel()->getUserStats();
56 | }, [], 'userstats');
57 |
58 | echo json_encode($stats);
59 |
60 | $this->app->close();
61 | }
62 |
63 | /**
64 | * Return information about user accounts deleted, used for displaying graphs
65 | *
66 | * @throws Exception
67 | * @since 1.0.0
68 | *
69 | * @noinspection PhpPossiblePolymorphicInvocationInspection
70 | * @noinspection PhpUnused
71 | */
72 | public function wipedstats(): void
73 | {
74 | $to = clone Factory::getDate();
75 | $to->setTime(0, 0);
76 | $from = clone Factory::getDate();
77 | $from->sub(new DateInterval('P1M'));
78 | $from->setTime(0, 0);
79 | $to->setTime(23, 59, 59);
80 |
81 | $stats = $this->getCache()->get(function ($from, $to) {
82 | $statsModel = $this->getModel('Stats', 'Administrator', ['ignore_request' => true]);
83 |
84 | return $statsModel->wipeStats($from, $to);
85 |
86 | }, [$from, $to], 'wipedstats');
87 |
88 | echo json_encode($stats);
89 |
90 | $this->app->close();
91 | }
92 |
93 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/EmailtemplatesController.php:
--------------------------------------------------------------------------------
1 | checkToken('get');
25 |
26 | $returnURL = Route::_('index.php?option=com_datacompliance&view=Emailtemplates', false);
27 | $this->setRedirect($returnURL);
28 |
29 | $affected = TemplateEmails::updateAllTemplates();
30 |
31 | $message = ($affected > 0) ?
32 | Text::plural('COM_DATACOMPLIANCE_EMAILTEMPLATES_LBL_N_UPDATED', $affected) :
33 | Text::_('COM_DATACOMPLIANCE_EMAILTEMPLATES_ERR_NOUPDATE');
34 |
35 | $this->setMessage($message, ($affected > 0) ? 'success' : 'warning');
36 | }
37 |
38 | public function resetEmails($cachable = false, $urlparams = [])
39 | {
40 | $this->checkToken('get');
41 |
42 | $returnURL = Route::_('index.php?option=com_datacompliance&view=Emailtemplates', false);
43 | $this->setRedirect($returnURL);
44 |
45 | $affected = TemplateEmails::resetAllTemplates();
46 |
47 | $message = ($affected > 0) ?
48 | Text::plural('COM_DATACOMPLIANCE_EMAILTEMPLATES_LBL_N_RESET', $affected) :
49 | Text::_('COM_DATACOMPLIANCE_EMAILTEMPLATES_ERR_RESET');
50 |
51 | $this->setMessage($message, ($affected > 0) ? 'success' : 'error');
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/ExporttrailsController.php:
--------------------------------------------------------------------------------
1 | unregisterTask('unpublish');
30 | $this->unregisterTask('archive');
31 | $this->unregisterTask('trash');
32 | $this->unregisterTask('report');
33 | $this->unregisterTask('orderup');
34 | $this->unregisterTask('orderdown');
35 | $this->unregisterTask('delete');
36 | $this->unregisterTask('publish');
37 | $this->unregisterTask('reorder');
38 | $this->unregisterTask('saveorder');
39 | $this->unregisterTask('checkin');
40 | $this->unregisterTask('saveOrderAjax');
41 | $this->unregisterTask('runTransition');
42 | }
43 |
44 | public function getModel($name = 'Exporttrails', $prefix = 'Administrator', $config = ['ignore_request' => true])
45 | {
46 | return parent::getModel($name, $prefix, $config);
47 | }
48 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/LifecycleController.php:
--------------------------------------------------------------------------------
1 | unregisterTask('unpublish');
32 | $this->unregisterTask('archive');
33 | $this->unregisterTask('trash');
34 | $this->unregisterTask('report');
35 | $this->unregisterTask('orderup');
36 | $this->unregisterTask('orderdown');
37 | $this->unregisterTask('delete');
38 | $this->unregisterTask('publish');
39 | $this->unregisterTask('reorder');
40 | $this->unregisterTask('saveorder');
41 | $this->unregisterTask('checkin');
42 | $this->unregisterTask('saveOrderAjax');
43 | $this->unregisterTask('runTransition');
44 | }
45 |
46 | public function display($cachable = false, $urlparams = [])
47 | {
48 | $view = $this->getView();
49 | $wipeModel = $this->getModel('Wipe');
50 | $view->setModel($wipeModel, false);
51 |
52 | return parent::display($cachable, $urlparams);
53 | }
54 |
55 |
56 | public function getModel($name = 'Lifecycle', $prefix = 'Administrator', $config = ['ignore_request' => true])
57 | {
58 | return parent::getModel($name, $prefix, $config);
59 | }
60 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/UsertrailsController.php:
--------------------------------------------------------------------------------
1 | unregisterTask('unpublish');
30 | $this->unregisterTask('archive');
31 | $this->unregisterTask('trash');
32 | $this->unregisterTask('report');
33 | $this->unregisterTask('orderup');
34 | $this->unregisterTask('orderdown');
35 | $this->unregisterTask('delete');
36 | $this->unregisterTask('publish');
37 | $this->unregisterTask('reorder');
38 | $this->unregisterTask('saveorder');
39 | $this->unregisterTask('checkin');
40 | $this->unregisterTask('saveOrderAjax');
41 | $this->unregisterTask('runTransition');
42 | }
43 |
44 | public function getModel($name = 'Usertrails', $prefix = 'Administrator', $config = ['ignore_request' => true])
45 | {
46 | return parent::getModel($name, $prefix, $config);
47 | }
48 | }
--------------------------------------------------------------------------------
/component/backend/src/Controller/WipetrailsController.php:
--------------------------------------------------------------------------------
1 | unregisterTask('unpublish');
30 | $this->unregisterTask('archive');
31 | $this->unregisterTask('trash');
32 | $this->unregisterTask('report');
33 | $this->unregisterTask('orderup');
34 | $this->unregisterTask('orderdown');
35 | $this->unregisterTask('delete');
36 | $this->unregisterTask('publish');
37 | $this->unregisterTask('reorder');
38 | $this->unregisterTask('saveorder');
39 | $this->unregisterTask('checkin');
40 | $this->unregisterTask('saveOrderAjax');
41 | $this->unregisterTask('runTransition');
42 | }
43 |
44 | public function getModel($name = 'Wipetrails', $prefix = 'Administrator', $config = ['ignore_request' => true])
45 | {
46 | return parent::getModel($name, $prefix, $config);
47 | }
48 | }
--------------------------------------------------------------------------------
/component/backend/src/Extension/DataComplianceComponent.php:
--------------------------------------------------------------------------------
1 | get(DatabaseInterface::class);
34 | $this->getRegistry()->register('datacompliance', new DataCompliance($dbo));
35 |
36 | // Make sure the Composer autoloader for our dependencies is loaded
37 | require_once __DIR__ . '/../../vendor/autoload.php';
38 | }
39 | }
--------------------------------------------------------------------------------
/component/backend/src/Field/ArticleField.php:
--------------------------------------------------------------------------------
1 | get(DatabaseInterface::class);
31 | $data = $params->toString('JSON');
32 |
33 | $sql = (method_exists($db, 'createQuery') ? $db->createQuery() : $db->getQuery(true))
34 | ->update($db->qn('#__extensions'))
35 | ->set($db->qn('params') . ' = ' . $db->q($data))
36 | ->where($db->qn('element') . ' = ' . $db->q('com_datacompliance'))
37 | ->where($db->qn('type') . ' = ' . $db->q('component'));
38 |
39 | $db->setQuery($sql);
40 |
41 | try
42 | {
43 | $db->execute();
44 |
45 | // The component parameters are cached. We just changed them. Therefore we MUST reset the system cache which holds them.
46 | CacheCleaner::clearCacheGroups(['_system'], [0, 1]);
47 | }
48 | catch (\Exception $e)
49 | {
50 | // Don't sweat if it fails
51 | }
52 |
53 | // Reset ComponentHelper's cache
54 | $refClass = new \ReflectionClass(ComponentHelper::class);
55 | $refProp = $refClass->getProperty('components');
56 |
57 | $refProp->setAccessible(true);
58 |
59 | if (version_compare(PHP_VERSION, '8.3.0', 'ge'))
60 | {
61 | $components = $refClass->getStaticPropertyValue('components');
62 | }
63 | else
64 | {
65 | $components = $refProp->getValue();
66 | }
67 |
68 | $components['com_datacompliance']->params = $params;
69 |
70 | if (version_compare(PHP_VERSION, '8.3.0', 'ge'))
71 | {
72 | $refClass->setStaticPropertyValue('components', $components);
73 | }
74 | else
75 | {
76 | $refProp->setValue($components);
77 | }
78 |
79 | }
80 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/ControllerCacheTrait.php:
--------------------------------------------------------------------------------
1 | input->get('option', 'com_datacompliance');
44 | $hash = hash('md5', $group . $handler . $storage);
45 |
46 | if (isset(self::$cacheControllers[$hash]))
47 | {
48 | return self::$cacheControllers[$hash];
49 | }
50 |
51 | $options = ['defaultgroup' => $group];
52 | if (isset($storage))
53 | {
54 | $options['storage'] = $storage;
55 | }
56 |
57 | $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)
58 | ->createCacheController($handler, $options);
59 |
60 | self::$cacheControllers[$hash] = $cache;
61 |
62 | return self::$cacheControllers[$hash];
63 | }
64 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/ControllerEventsTrait.php:
--------------------------------------------------------------------------------
1 | task = $task;
41 |
42 | $task = strtolower($task);
43 |
44 | if (isset($this->taskMap[$task]))
45 | {
46 | $doTask = $this->taskMap[$task];
47 | }
48 | elseif (isset($this->taskMap['__default']))
49 | {
50 | $doTask = $this->taskMap['__default'];
51 | }
52 | else
53 | {
54 | throw new RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404);
55 | }
56 |
57 | // Execute onBeforeExecute and onBefore events
58 | $eventName = 'onBefore' . ucfirst($task);
59 |
60 | $this->triggerEvent('onBeforeExecute', [&$task]);
61 | $this->triggerEvent($eventName);
62 |
63 | // The task may have changed, so let's try that once again.
64 | if (isset($this->taskMap[$task]))
65 | {
66 | $doTask = $this->taskMap[$task];
67 | }
68 | elseif (isset($this->taskMap['__default']))
69 | {
70 | $doTask = $this->taskMap['__default'];
71 | }
72 | else
73 | {
74 | throw new RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404);
75 | }
76 |
77 | // Record the actual task being fired and execute it.
78 | $this->doTask = $doTask;
79 | $result = $this->$doTask();
80 |
81 | // Execute onAfter and onAfterExecute events
82 | $eventName = 'onAfter' . ucfirst($task);
83 |
84 | $this->triggerEvent($eventName);
85 | $this->triggerEvent('onAfterExecute', [$task]);
86 |
87 | return $result;
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/ControllerRegisterTasksTrait.php:
--------------------------------------------------------------------------------
1 | registerDefaultTask($defaultTask);
35 |
36 | $refObj = new ReflectionObject($this);
37 |
38 | foreach ($refObj->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod)
39 | {
40 | if (
41 | !$refMethod->isUserDefined() ||
42 | $refMethod->isStatic() || $refMethod->isAbstract() || $refMethod->isClosure() ||
43 | $refMethod->isConstructor() || $refMethod->isDestructor()
44 |
45 | )
46 | {
47 | continue;
48 | }
49 |
50 | $method = $refMethod->getName();
51 |
52 | if (substr($method, 0, 1) == '_')
53 | {
54 | continue;
55 | }
56 |
57 | if (substr($method, 0, 8) == 'onBefore')
58 | {
59 | continue;
60 | }
61 |
62 | if (substr($method, 0, 7) == 'onAfter')
63 | {
64 | continue;
65 | }
66 |
67 | $this->registerTask($method, $method);
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/TableAssertionTrait.php:
--------------------------------------------------------------------------------
1 | assert(!empty($value), $message);
50 | }
51 |
52 | /**
53 | * Assert that $value is set to one of $validValues or throw a RuntimeException with the $message language string
54 | *
55 | * @param mixed $value The value to check
56 | * @param array $validValues An array of valid values for $value
57 | * @param string $message The language key for the message to throw
58 | *
59 | * @throws RuntimeException
60 | */
61 | protected function assertInArray($value, array $validValues, $message)
62 | {
63 | $this->assert(in_array($value, $validValues), $message);
64 | }
65 |
66 | /**
67 | * Assert that $value is set to none of $validValues. Otherwise throw a RuntimeException with the $message language
68 | * string.
69 | *
70 | * @param mixed $value The value to check
71 | * @param array $validValues An array of invalid values for $value
72 | * @param string $message The language key for the message to throw
73 | *
74 | * @throws \RuntimeException
75 | */
76 | protected function assertNotInArray($value, array $validValues, $message)
77 | {
78 | $this->assert(!in_array($value, $validValues, true), $message);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/component/backend/src/Mixin/TableColumnAliasTrait.php:
--------------------------------------------------------------------------------
1 | hasField($name))
17 | {
18 | $realColumn = $this->getColumnAlias($name);
19 |
20 | return $this->{$realColumn};
21 | }
22 |
23 | return $this->{$name};
24 | }
25 |
26 | /**
27 | * Magic setter, is aware of column aliases.
28 | *
29 | * This is required for using Joomla's batch processing to copy / move records of tables which do not have a catid
30 | * column.
31 | *
32 | * @param $name
33 | * @param $value
34 | */
35 | public function __set($name, $value)
36 | {
37 | if ($this->hasField($name))
38 | {
39 | $realColumn = $this->getColumnAlias($name);
40 | $this->{$realColumn} = $value;
41 |
42 | return;
43 | }
44 |
45 | $this->{$name} = $value;
46 | }
47 |
48 |
49 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/TableCreateModifyTrait.php:
--------------------------------------------------------------------------------
1 | updateModified;
23 | }
24 |
25 | public function setUpdateModified(bool $updateModified): void
26 | {
27 | $this->updateModified = $updateModified;
28 | }
29 |
30 | public function getUpdateCreated(): bool
31 | {
32 | return $this->updateCreated;
33 | }
34 |
35 | public function setUpdateCreated(bool $updateCreated): void
36 | {
37 | $this->updateCreated = $updateCreated;
38 | }
39 |
40 | public function onBeforeStore($updateNulls = false)
41 | {
42 | $date = Factory::getDate()->toSql();
43 | $user = Factory::getApplication()->getIdentity();
44 |
45 | // Set created date if not set.
46 | if ($this->updateCreated && $this->hasField('created') && !(int) $this->created)
47 | {
48 | $this->created = $date;
49 | }
50 |
51 | if ($this->updateModified && ($this->getId() > 0))
52 | {
53 | // Existing item
54 | if ($this->hasField('modified_by'))
55 | {
56 | // Set a default value and update it only if we have a valid user object (i.e. we're not under CLI), otherwise the db will complain
57 | $this->modified_by = 0;
58 |
59 | if ($user)
60 | {
61 | $this->modified_by = $user->id;
62 | }
63 | }
64 | if ($this->hasField('modified'))
65 | {
66 | $this->modified = $date;
67 |
68 | }
69 | }
70 | elseif ($this->updateCreated || $this->updateModified)
71 | {
72 | // Field created_by can be set by the user, so we don't touch it if it's set.
73 | if ($this->updateCreated && $this->hasField('created_by') && empty($this->created_by))
74 | {
75 | // Set a default value and update it only if we have a valid user object (i.e. we're not under CLI), otherwise the db will complain
76 | $this->created_by = 0;
77 |
78 | if ($user)
79 | {
80 | $this->created_by = $user->id;
81 | }
82 | }
83 |
84 | // Set modified to created date if not set
85 | if ($this->updateModified && $this->hasField('modified') && $this->hasField('created') && !(int) $this->modified)
86 | {
87 | $this->modified = $this->created;
88 | }
89 |
90 | // Set modified_by to created_by user if not set
91 | if ($this->updateModified && $this->hasField('modified_by') && $this->hasField('created_by') && empty($this->modified_by))
92 | {
93 | $this->modified_by = $this->created_by;
94 | }
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/TriggerEventTrait.php:
--------------------------------------------------------------------------------
1 | onBeforeSomething(123, 456)
24 | * 2. $this->checkACL('@something') if there is no onBeforeSomething and the event starts with onBefore
25 | * 3. Joomla! plugin event onComFoobarControllerItemBeforeSomething($this, 123, 456)
26 | *
27 | * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick
28 | * @param array $arguments The arguments to pass to the event handlers
29 | *
30 | * @return bool
31 | */
32 | protected function triggerEvent(string $event, array $arguments = []): bool
33 | {
34 | // If there is an object method for this event, call it
35 | if (method_exists($this, $event))
36 | {
37 | /**
38 | * IMPORTANT! We use call_user_func_array() so we can pass arguments by reference.
39 | */
40 | if (call_user_func_array([$this, $event], $arguments) === false)
41 | {
42 | return false;
43 | }
44 | }
45 |
46 | // All other event handlers live outside this object, therefore they need to be passed a reference to this
47 | // object as the first argument.
48 | array_unshift($arguments, $this);
49 |
50 | // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later.
51 | $prefix = '';
52 |
53 | if (substr($event, 0, 2) == 'on')
54 | {
55 | $prefix = 'on';
56 | $event = substr($event, 2);
57 | }
58 |
59 | // Get the component name and object type from the namespace of the caller
60 | $callers = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
61 | $namespaceParts = explode('\\', $callers[1]['class']);
62 | $className = array_pop($namespaceParts);
63 | $objectType = array_pop($namespaceParts);
64 | array_pop($namespaceParts);
65 | $bareComponent = strtolower(array_pop($namespaceParts));
66 |
67 | // Get the component/model prefix for the event
68 | $prefix .= 'Com' . ucfirst($bareComponent);
69 | $prefix .= ucfirst($className);
70 |
71 | // The event name will be something like onComFoobarItemsControllerBeforeSomething
72 | $event = $prefix . $event;
73 |
74 | // Call the Joomla! plugins
75 | $results = $this->triggerPluginEvent($event, $arguments);
76 |
77 | return !in_array(false, $results, true);
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/component/backend/src/Mixin/ViewTaskBasedEventsTrait.php:
--------------------------------------------------------------------------------
1 | getModel()->getState('task');
19 |
20 | $eventName = 'onBefore' . ucfirst($task);
21 | $this->triggerEvent($eventName, [&$tpl]);
22 |
23 | parent::display($tpl);
24 |
25 | $eventName = 'onAfter' . ucfirst($task);
26 | $this->triggerEvent($eventName, [&$tpl]);
27 | }
28 | }
--------------------------------------------------------------------------------
/component/backend/src/Model/ControlpanelModel.php:
--------------------------------------------------------------------------------
1 | 0,
33 | 'deleted' => 0,
34 | 'expired' => 0,
35 | ];
36 |
37 | // Total number of users
38 | $db = $this->getDatabase();
39 | $query = (method_exists($db, 'createQuery') ? $db->createQuery() : $db->getQuery(true))
40 | ->select('COUNT(' . $db->quoteName('id') . ')')
41 | ->from($db->quoteName('#__users'));
42 | $totalUsers = $db->setQuery($query)->loadResult();
43 |
44 | // Lifecycle (inactive) users
45 | /** @var WipeModel $wipeModel */
46 | $wipeModel = $this->getMVCFactory()->createModel('Wipe', 'Administrator', ['ignore_request' => true]);
47 | $lifeCycleUsers = $wipeModel->getLifecycleUserIDs(true);
48 | $wipedUsers = $wipeModel->getWipedUserIDs();
49 |
50 | $ret['deleted'] = count($wipedUsers);
51 | $ret['expired'] = count($lifeCycleUsers);
52 | $ret['active'] = $totalUsers - $ret['expired'] - $ret['deleted'];
53 |
54 | return $ret;
55 | }
56 |
57 | /**
58 | * Update the cached live site's URL for the front-end scheduling feature
59 | *
60 | * @return void
61 | *
62 | * @since 1.0.0
63 | */
64 | public function updateMagicParameters(): void
65 | {
66 | $cParams = ComponentHelper::getParams('com_datacompliance');
67 | $cParams->set('siteurl', Uri::root(false));
68 |
69 | ComponentParams::save($cParams);
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/component/backend/src/Provider/RouterFactory.php:
--------------------------------------------------------------------------------
1 | namespace = $namespace;
39 | }
40 |
41 | /**
42 | * Registers the service provider with a DI container.
43 | *
44 | * @param Container $container The DI container.
45 | *
46 | * @return void
47 | *
48 | * @since 4.0.0
49 | */
50 | public function register(Container $container)
51 | {
52 | $container->set(
53 | RouterFactoryInterface::class,
54 | function (Container $container)
55 | {
56 | return new \Akeeba\Component\DataCompliance\Administrator\Router\RouterFactory(
57 | $this->namespace,
58 | $container->get(DatabaseInterface::class),
59 | $container->get(MVCFactoryInterface::class)
60 | );
61 | }
62 | );
63 | }
64 | }
--------------------------------------------------------------------------------
/component/backend/src/Router/RouterFactory.php:
--------------------------------------------------------------------------------
1 | namespace = $namespace;
30 | $this->factory = $factory;
31 | $this->db = $db;
32 | }
33 |
34 | public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface
35 | {
36 | $className = trim($this->namespace, '\\') . '\\' . ucfirst($application->getName()) . '\\Service\\Router';
37 |
38 | if (!class_exists($className))
39 | {
40 | throw new \RuntimeException('No router available for this application.');
41 | }
42 |
43 | return new $className($application, $menu, $this->db, $this->factory);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/component/backend/src/Rule/.gitinclude:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/component/backend/src/Rule/.gitinclude
--------------------------------------------------------------------------------
/component/backend/src/Service/Html/DataCompliance.php:
--------------------------------------------------------------------------------
1 | setDatabase($db);
27 | }
28 |
29 | public function formatDate(?string $date, ?string $format = null, bool $tzAware = true): string
30 | {
31 | if (empty($date))
32 | {
33 | return '';
34 | }
35 |
36 | // Which timezone should I use?
37 | $tz = null;
38 |
39 | if ($tzAware !== false)
40 | {
41 | $userId = is_bool($tzAware) ? null : (int) $tzAware;
42 |
43 | try
44 | {
45 | $tzDefault = Factory::getApplication()->get('offset');
46 | }
47 | catch (\Exception $e)
48 | {
49 | $tzDefault = new \DateTimeZone('GMT');
50 | }
51 |
52 | $user = is_null($userId)
53 | ? Factory::getApplication()->getIdentity()
54 | : Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($userId);
55 | $tz = $user->getParam('timezone', $tzDefault);
56 | }
57 |
58 | $jDate = clone Factory::getDate($date, $tz);
59 |
60 | return $jDate->format($format ?: 'Y-m-d H:i T', true);
61 | }
62 |
63 | public static function booleanList(string $name, bool $value, string $label, ?string $id = null)
64 | {
65 | return (new FileLayout('joomla.form.field.radio.switcher'))->render([
66 | 'id' => $id ?: $value,
67 | 'name' => $name,
68 | 'label' => $label,
69 | 'value' => $value ? 1 : 0,
70 | 'onchange' => '',
71 | 'dataAttribute' => '',
72 | 'readonly' => false,
73 | 'disabled' => false,
74 | 'class' => 'form-control',
75 | 'options' => [
76 | HTMLHelper::_('select.option', '0', Text::_('JNO')),
77 | HTMLHelper::_('select.option', '1', Text::_('JYES')),
78 | ],
79 | ]);
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/component/backend/src/Table/ExporttrailsTable.php:
--------------------------------------------------------------------------------
1 | _supportNullValue = false;
37 | $this->setColumnAlias('created', 'created_on');
38 | $this->setColumnAlias('id', 'datacompliance_exporttrail_id');
39 |
40 | parent::__construct('#__datacompliance_exporttrails', 'datacompliance_exporttrail_id', $db, $dispatcher);
41 | }
42 |
43 | protected function onBeforeCheck()
44 | {
45 | if (empty($this->user_id))
46 | {
47 | throw new \RuntimeException("Export audit trail: cannot have an empty user ID");
48 | }
49 |
50 | $this->requester_ip = $this->requester_ip ?: (IpHelper::getIp() ?: '(CLI)');
51 | }
52 | }
--------------------------------------------------------------------------------
/component/backend/src/Table/GetPropertiesAwareTrait.php:
--------------------------------------------------------------------------------
1 | !empty($x) && !is_numeric($x) && ord(substr($x, 0, 1)) !== 0, ARRAY_FILTER_USE_KEY);
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/component/backend/src/Table/UsertrailsTable.php:
--------------------------------------------------------------------------------
1 | _supportNullValue = false;
41 | $this->setColumnAlias('created', 'created_on');
42 | $this->setColumnAlias('id', 'datacompliance_usertrail_id');
43 |
44 | parent::__construct('#__datacompliance_usertrails', 'datacompliance_usertrail_id', $db, $dispatcher);
45 |
46 | $this->items = [];
47 | }
48 |
49 | public function onAfterReset()
50 | {
51 | $this->items = [];
52 | }
53 |
54 | protected function onBeforeStore(&$updateNulls)
55 | {
56 | $this->onBeforeStoreCreateModifyAware($updateNulls);
57 |
58 | if (is_array($this->items) || is_object($this->items))
59 | {
60 | $this->items = json_encode($this->items);
61 | }
62 | }
63 |
64 | protected function onAfterStore(&$result, $updateNulls)
65 | {
66 | if (!is_array($this->items))
67 | {
68 | $this->items = @json_decode($this->items ?: '{}', true) ?? [];
69 | }
70 | }
71 |
72 | protected function onBeforeBind(&$src, &$ignore = [])
73 | {
74 | $src = (array)$src;
75 |
76 | if (!is_array($src['items'] ?? ''))
77 | {
78 | $this->items = @json_decode($src['params'] ?: '{}', true) ?? [];
79 | }
80 | }
81 |
82 | protected function onBeforeCheck()
83 | {
84 | if (empty($this->user_id))
85 | {
86 | throw new \RuntimeException("Data wipe audit trail: cannot have an empty user ID");
87 | }
88 |
89 | $this->requester_ip = $this->requester_ip ?: (IpHelper::getIp() ?: '(CLI)');
90 |
91 | if (empty($this->items))
92 | {
93 | $this->items = [];
94 | }
95 | }
96 |
97 |
98 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Consenttrails/HtmlView.php:
--------------------------------------------------------------------------------
1 | getModel();
78 | $this->items = $model->getItems();
79 | $this->pagination = $model->getPagination();
80 | $this->state = $model->getState();
81 | $this->filterForm = $model->getFilterForm();
82 | $this->activeFilters = $model->getActiveFilters();
83 | $this->isEmptyState = $this->get('IsEmptyState');
84 |
85 | // Check for errors.
86 | if (count($errors = $this->get('Errors')))
87 | {
88 | throw new GenericDataException(implode("\n", $errors), 500);
89 | }
90 |
91 | if (!\count($this->items) && $this->isEmptyState)
92 | {
93 | $this->setLayout('emptystate');
94 | }
95 |
96 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_TITLE_CONSENTTRAILS'), 'datacompliance');
97 | ToolbarHelper::back('COM_DATACOMPLIANCE_TITLE_DASHBOARD_SHORT', 'index.php?option=com_datacompliance');
98 |
99 | parent::display($tpl);
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Controlpanel/HtmlView.php:
--------------------------------------------------------------------------------
1 | document->getWebAssetManager()
25 | ->useScript('com_datacompliance.controlpanel')
26 | ->useScript('com_datacompliance.chart_moment_adapter');
27 |
28 | $this->document->addScriptOptions(
29 | 'com_datacompliance.controlpanel.userGraphsUrl',
30 | Route::_('index.php?option=com_datacompliance&task=controlpanel.userstats', false, Route::TLS_IGNORE, true)
31 | );
32 | $this->document->addScriptOptions(
33 | 'com_datacompliance.controlpanel.wipedGraphsUrl',
34 | Route::_('index.php?option=com_datacompliance&task=controlpanel.wipedstats', false, Route::TLS_IGNORE, true)
35 | );
36 |
37 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_INACTIVE');
38 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_ACTIVE');
39 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_DELETED');
40 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_USER');
41 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_ADMIN');
42 | Text::script('COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_LIFECYCLE');
43 |
44 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_TITLE_DASHBOARD'), 'datacompliance');
45 | ToolbarHelper::preferences('com_datacompliance');
46 |
47 | parent::display($tpl);
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Emailtemplates/HtmlView.php:
--------------------------------------------------------------------------------
1 | getModel();
78 | $this->items = $model->getItems();
79 | $this->pagination = $model->getPagination();
80 | $this->state = $model->getState();
81 | $this->filterForm = $model->getFilterForm();
82 | $this->activeFilters = $model->getActiveFilters();
83 | $this->isEmptyState = $this->get('IsEmptyState');
84 |
85 | // Check for errors.
86 | if (count($errors = $this->get('Errors')))
87 | {
88 | throw new GenericDataException(implode("\n", $errors), 500);
89 | }
90 |
91 | if (!\count($this->items) && $this->isEmptyState)
92 | {
93 | $this->setLayout('emptystate');
94 | }
95 |
96 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_EXPORTTRAILS'), 'datacompliance');
97 | ToolbarHelper::back('COM_DATACOMPLIANCE_TITLE_DASHBOARD_SHORT', 'index.php?option=com_datacompliance');
98 |
99 | parent::display($tpl);
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Lifecycle/HtmlView.php:
--------------------------------------------------------------------------------
1 | getModel();
78 | $this->items = $model->getItems();
79 | $this->pagination = $model->getPagination();
80 | $this->state = $model->getState();
81 | $this->filterForm = $model->getFilterForm();
82 | $this->activeFilters = $model->getActiveFilters();
83 | $this->isEmptyState = $this->get('IsEmptyState');
84 |
85 | // Check for errors.
86 | if (count($errors = $this->get('Errors')))
87 | {
88 | throw new GenericDataException(implode("\n", $errors), 500);
89 | }
90 |
91 | if (!\count($this->items) && $this->isEmptyState)
92 | {
93 | $this->setLayout('emptystate');
94 | }
95 |
96 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_TITLE_LIFECYCLE'), 'datacompliance');
97 | ToolbarHelper::back('COM_DATACOMPLIANCE_TITLE_DASHBOARD_SHORT', 'index.php?option=com_datacompliance');
98 |
99 | parent::display($tpl);
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Usertrails/HtmlView.php:
--------------------------------------------------------------------------------
1 | getModel();
78 | $this->items = $model->getItems();
79 | $this->pagination = $model->getPagination();
80 | $this->state = $model->getState();
81 | $this->filterForm = $model->getFilterForm();
82 | $this->activeFilters = $model->getActiveFilters();
83 | $this->isEmptyState = $this->get('IsEmptyState');
84 |
85 | // Check for errors.
86 | if (count($errors = $this->get('Errors')))
87 | {
88 | throw new GenericDataException(implode("\n", $errors), 500);
89 | }
90 |
91 | if (!\count($this->items) && $this->isEmptyState)
92 | {
93 | $this->setLayout('emptystate');
94 | }
95 |
96 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_USERTRAILS'), 'datacompliance');
97 | ToolbarHelper::back('COM_DATACOMPLIANCE_TITLE_DASHBOARD_SHORT', 'index.php?option=com_datacompliance');
98 |
99 | parent::display($tpl);
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/component/backend/src/View/Wipetrails/HtmlView.php:
--------------------------------------------------------------------------------
1 | getModel();
78 | $this->items = $model->getItems();
79 | $this->pagination = $model->getPagination();
80 | $this->state = $model->getState();
81 | $this->filterForm = $model->getFilterForm();
82 | $this->activeFilters = $model->getActiveFilters();
83 | $this->isEmptyState = $this->get('IsEmptyState');
84 |
85 | // Check for errors.
86 | if (count($errors = $this->get('Errors')))
87 | {
88 | throw new GenericDataException(implode("\n", $errors), 500);
89 | }
90 |
91 | if (!\count($this->items) && $this->isEmptyState)
92 | {
93 | $this->setLayout('emptystate');
94 | }
95 |
96 | ToolbarHelper::title(Text::_('COM_DATACOMPLIANCE_WIPETRAILS'), 'datacompliance');
97 | ToolbarHelper::back('COM_DATACOMPLIANCE_TITLE_DASHBOARD_SHORT', 'index.php?option=com_datacompliance');
98 |
99 | parent::display($tpl);
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/component/backend/tmpl/consenttrails/emptystate.php:
--------------------------------------------------------------------------------
1 | 'COM_DATACOMPLIANCE_CONSENTTRAILS',
14 | 'formURL' => 'index.php?option=com_datacompliance&view=consenttrails',
15 | //'helpURL' => '',
16 | 'icon' => 'fa fa-check-square',
17 | //'createURL' => '',
18 | ];
19 |
20 | echo LayoutHelper::render('joomla.content.emptystate', $displayData);
21 |
--------------------------------------------------------------------------------
/component/backend/tmpl/controlpanel/default.php:
--------------------------------------------------------------------------------
1 | loadAnyTemplate('Controlpanel/joomla_eol');
15 | }
16 |
17 | ?>
18 |
19 |
20 |
21 |
22 | = $this->loadTemplate('icons') ?>
23 |
24 |
25 | = $this->loadTemplate('stats') ?>
26 |
27 |
28 |
--------------------------------------------------------------------------------
/component/backend/tmpl/controlpanel/default_stats.php:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/component/backend/tmpl/controlpanel/joomla_eol.php:
--------------------------------------------------------------------------------
1 |
12 | 1760475600): ?>
13 |
14 | Joomla! 4 has reached End of Service
15 |
16 | Joomla! 4 became End of Service on October 15th, 2025.
17 |
18 |
19 | Our software for Joomla! 4 is also End of Life. We will no longer provide any updates or support.
20 |
21 |
22 | Kindly note that we started showing these notices since October 15th, 2023 — two years before the planned End of Life of our software for Joomla! 4.
23 |
24 |
25 | 1728939600): ?>
26 |
27 | Joomla! 4 is approaching End of Service
28 |
29 | Joomla! 4 is currently in security–only maintenance. It will become End of Service on October 15th, 2025.
30 |
31 |
32 | Our software for Joomla! 4 is also in security-only maintenance. We only provide security updates and limited support for it until October 15th, 2025.
33 |
34 |
35 | You need to update your site to Joomla! 5 as soon as possible. We will not provide any updates or support after October 15th, 2025. Moreover, we do not guarantee an update path to Joomla! 5 and beyond will exist after October 15th, 2025.
36 |
37 |
38 | 1697317200): ?>
39 |
40 | Joomla! 4 is approaching Security Maintenance
41 |
42 | Joomla! 4 will enter security–only maintenance on October 15th, 2024. It will become End of Service on October 15th, 2025.
43 |
44 |
45 | Will provide full support and updates for our Joomla! 4 software until October 15th, 2024. From then until October 15th, 2025 we will only provide security updates and limited support. We will not provide any updates or support after October 15th, 2025.
46 |
47 |
48 | We urge you to upgrade your site to Joomla! 5 before October 15th, 2024. Please note that we do not guarantee an update path to Joomla! 5 and beyond after October 15th, 2025.
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/component/backend/tmpl/emailtemplates/default.php:
--------------------------------------------------------------------------------
1 | getFormToken();
17 | ?>
18 |
19 |
36 |
37 |
38 |
41 |
42 |
43 |
44 |
54 |
55 | = Text::_('COM_DATACOMPLIANCE_EMAILTEMPLATES_LBL_UPDATE') ?>
56 |
57 |
58 |
59 |
69 |
70 | = Text::_('COM_DATACOMPLIANCE_EMAILTEMPLATES_LBL_RESET') ?>
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/component/backend/tmpl/exporttrails/emptystate.php:
--------------------------------------------------------------------------------
1 | 'COM_DATACOMPLIANCE_EXPORTTRAILS',
14 | 'formURL' => 'index.php?option=com_datacompliance&view=exporttrails',
15 | //'helpURL' => '',
16 | 'icon' => 'fa fa-file-export',
17 | //'createURL' => '',
18 | ];
19 |
20 | echo LayoutHelper::render('joomla.content.emptystate', $displayData);
21 |
--------------------------------------------------------------------------------
/component/backend/tmpl/lifecycle/emptystate.php:
--------------------------------------------------------------------------------
1 | 'COM_DATACOMPLIANCE_LIFECYCLE',
14 | 'formURL' => 'index.php?option=com_datacompliance&view=lifecycle',
15 | //'helpURL' => '',
16 | 'icon' => 'fa fa-user-clock',
17 | //'createURL' => '',
18 | ];
19 |
20 | echo LayoutHelper::render('joomla.content.emptystate', $displayData);
21 |
--------------------------------------------------------------------------------
/component/backend/tmpl/usertrails/emptystate.php:
--------------------------------------------------------------------------------
1 | 'COM_DATACOMPLIANCE_USERTRAILS',
14 | 'formURL' => 'index.php?option=com_datacompliance&view=usertrails',
15 | //'helpURL' => '',
16 | 'icon' => 'fa fa-users',
17 | //'createURL' => '',
18 | ];
19 |
20 | echo LayoutHelper::render('joomla.content.emptystate', $displayData);
21 |
--------------------------------------------------------------------------------
/component/backend/tmpl/wipetrails/emptystate.php:
--------------------------------------------------------------------------------
1 | 'COM_DATACOMPLIANCE_WIPETRAILS',
14 | 'formURL' => 'index.php?option=com_datacompliance&view=wipetrails',
15 | //'helpURL' => '',
16 | 'icon' => 'fa fa-user-minus',
17 | //'createURL' => '',
18 | ];
19 |
20 | echo LayoutHelper::render('joomla.content.emptystate', $displayData);
21 |
--------------------------------------------------------------------------------
/component/datacompliance.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 | COM_DATACOMPLIANCE
8 | 2025-02-10
9 | Nicholas K. Dionysopoulos
10 | nicholas@akeeba.com
11 | https://www.akeeba.com
12 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
13 | This component in released under the GNU/GPL v3 or later license
14 | 3.2.3
15 | COM_DATACOMPLIANCE_XML_DESCRIPTION
16 | Akeeba\Component\DataCompliance
17 |
18 |
19 |
20 | language
21 | src
22 | tmpl
23 |
24 | .htaccess
25 | web.config
26 |
27 |
28 |
29 |
30 | en-GB/com_datacompliance.ini
31 |
32 |
33 |
34 |
35 |
36 | COM_DATACOMPLIANCE
37 |
38 |
39 |
40 | assets
41 | forms
42 | language
43 | layouts
44 | services
45 | sql
46 | src
47 | tmpl
48 | vendor
49 |
50 | access.xml
51 | config.xml
52 |
53 |
54 |
55 |
56 | en-GB/com_datacompliance.ini
57 | en-GB/com_datacompliance.sys.ini
58 |
59 |
60 |
61 |
62 |
63 |
64 | css
65 | js
66 |
67 | joomla.asset.json
68 |
69 |
70 |
71 |
72 | sql/install.mysql.utf8.sql
73 |
74 |
75 |
76 |
77 |
78 | sql/uninstall.mysql.utf8.sql
79 |
80 |
81 |
82 |
83 |
84 | sql/updates/mysql
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/component/frontend/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/component/frontend/src/Controller/OptionsController.php:
--------------------------------------------------------------------------------
1 | setDatabase($db);
33 | $this->setMVCFactory($factory);
34 |
35 | $this->registerView(new RouterViewConfiguration('options'));
36 |
37 | parent::__construct($app, $menu);
38 |
39 | $this->attachRule(new MenuRules($this));
40 | $this->attachRule(new StandardRules($this));
41 | $this->attachRule(new NomenuRules($this));
42 | }
43 |
44 | public function build(&$query)
45 | {
46 | $query['view'] = strtolower($query['view'] ?? 'options');
47 |
48 | $segments = parent::build($query);
49 |
50 | $task = strtolower($query['task'] ?? 'options');
51 |
52 | if (in_array($task, ['export', 'wipe']))
53 | {
54 | $segments[] = $task;
55 | unset($query['task']);
56 | }
57 |
58 | return $segments;
59 | }
60 |
61 | public function parse(&$segments)
62 | {
63 | $query = parent::parse($segments);
64 |
65 | $lastSegment = count($segments) ? array_pop($segments) : null;
66 |
67 | if (empty($lastSegment))
68 | {
69 | return $query;
70 | }
71 |
72 | if (in_array($lastSegment, ['export', 'wipe']))
73 | {
74 | $query['view'] = 'options';
75 | $query['task'] = $lastSegment;
76 |
77 | return $query;
78 | }
79 |
80 | $segments[] = $lastSegment;
81 |
82 | return $query;
83 | }
84 |
85 |
86 | }
--------------------------------------------------------------------------------
/component/frontend/src/View/Options/HtmlView.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | COM_DATACOMPLIANCE_MENUMANAGER_VIEW_OPTIONS_LABEL
15 | COM_DATACOMPLIANCE_MENUMANAGER_VIEW_OPTIONS_DESC
16 |
17 |
18 |
--------------------------------------------------------------------------------
/component/frontend/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/component/media/css/backend.css:
--------------------------------------------------------------------------------
1 | /*!*
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | *//*!*
6 | * @package AkeebaDataCompliance
7 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
8 | * @license GNU General Public License version 3, or later
9 | */@font-face{font-family:"Akeeba Products";src:url("../fonts/Akeeba-Products.woff");font-display:swap}span.icon-datacompliance:before,span.icon-datacompliance-j4:before{font-family:"Akeeba Products" !important;font-style:normal !important;font-weight:normal !important;line-height:1 !important;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;position:relative;content:"O"}/*# sourceMappingURL=backend.css.map */
10 |
--------------------------------------------------------------------------------
/component/media/css/backend.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["backend.scss","sources/_title_icon.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCMA,WACE,8BACA,yCACA,kBAKA,mEACE,yCACA,6BACA,8BACA,yBACA,mCACA,kCAEA,qBACA,kBAEA","file":"backend.css"}
--------------------------------------------------------------------------------
/component/media/css/backend.scss:
--------------------------------------------------------------------------------
1 | /*!*
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | @import 'sources/title_icon.scss';
8 |
9 |
--------------------------------------------------------------------------------
/component/media/css/j5.css:
--------------------------------------------------------------------------------
1 | /*!*
2 | * @package DocImport
3 | * @copyright Copyright (c)2011-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */@media screen{section#content main .alert[class*=alert-] a[class*=btn-]{color:var(--btn-color)}section#content main .alert[class*=alert-] a.btn-primary{color:var(--btn-primary-color)}section#content main .alert[class*=alert-] a.btn-secondary{color:var(--btn-secondary-color)}section#content main .alert[class*=alert-] a[class*=btn-outline-]{color:var(--btn-color)}section#content main .alert[class*=alert-] a.btn:hover{color:var(--btn-hover-color)}}/*# sourceMappingURL=j5.css.map */
6 |
--------------------------------------------------------------------------------
/component/media/css/j5.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["j5.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GAMA,cAII,iFAEA,wFACA,4FAEA,yFAEA","file":"j5.css"}
--------------------------------------------------------------------------------
/component/media/css/j5.scss:
--------------------------------------------------------------------------------
1 | /*!*
2 | * @package DocImport
3 | * @copyright Copyright (c)2011-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | @media screen
8 | {
9 | // Fix button colors in alert DIVs
10 | section#content main .alert[class*="alert-"] a {
11 | &[class*='btn-'] { color: var(--btn-color); }
12 |
13 | &.btn-primary { color: var(--btn-primary-color); }
14 | &.btn-secondary { color: var(--btn-secondary-color); }
15 |
16 | &[class*='btn-outline-'] { color: var(--btn-color); }
17 |
18 | &.btn:hover { color: var(--btn-hover-color); }
19 | }
20 | }
--------------------------------------------------------------------------------
/component/media/css/sources/_title_icon.scss:
--------------------------------------------------------------------------------
1 | /*!*
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 |
7 | @font-face {
8 | font-family: "Akeeba Products";
9 | src: url("../fonts/Akeeba-Products.woff");
10 | font-display: swap;
11 | }
12 |
13 | span.icon-datacompliance,
14 | span.icon-datacompliance-j4, {
15 | &:before{
16 | font-family: 'Akeeba Products' !important;
17 | font-style: normal !important;
18 | font-weight: normal !important;
19 | line-height: 1 !important;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 |
23 | display: inline-block;
24 | position: relative;
25 |
26 | content:'\004F';
27 | }
28 | }
--------------------------------------------------------------------------------
/component/media/fonts/Akeeba-Products.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/component/media/fonts/Akeeba-Products.woff
--------------------------------------------------------------------------------
/component/media/joomla.asset.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
3 | "name": "com_datacompliance",
4 | "version": "3.2.3",
5 | "description": "Akeeba DataCompliance",
6 | "license": "GPL-3.0-or-later",
7 | "assets": [
8 | {
9 | "name": "com_datacompliance.backend",
10 | "description": "Backend CSS",
11 | "type": "style",
12 | "uri": "com_datacompliance/backend.css"
13 | },
14 |
15 | {
16 | "name": "com_datacompliance.j5",
17 | "description": "Backend CSS for Joomla! 5",
18 | "type": "style",
19 | "uri": "com_datacompliance/j5.css"
20 | },
21 |
22 | {
23 | "name": "com_datacompliance.controlpanel",
24 | "description": "Control Panel JavaScript",
25 | "type": "script",
26 | "uri": "com_datacompliance/controlpanel.min.js",
27 | "dependencies": [
28 | "core"
29 | ],
30 | "attributes": {
31 | "defer": true
32 | }
33 | },
34 |
35 | {
36 | "name": "com_datacompliance.options",
37 | "description": "Data Options JavaScript",
38 | "type": "script",
39 | "uri": "com_datacompliance/options.min.js",
40 | "dependencies": [
41 | "core"
42 | ],
43 | "attributes": {
44 | "defer": true
45 | }
46 | },
47 |
48 | {
49 | "name": "com_datacompliance.chart",
50 | "description": "Charts.js — renders charts and graphs",
51 | "type": "script",
52 | "uri": "https://cdn.jsdelivr.net/npm/chart.js@3.2.1/dist/chart.min.js",
53 | "attributes": {
54 | "defer": true
55 | }
56 | },
57 | {
58 | "name": "com_datacompliance.chart_moment_adapter",
59 | "description": "Moment adapter for Charts.js",
60 | "type": "script",
61 | "dependencies": [
62 | "com_datacompliance.chart",
63 | "com_datacompliance.moment"
64 | ],
65 | "uri": "https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@0.1.1",
66 | "attributes": {
67 | "defer": true
68 | }
69 | },
70 | {
71 | "name": "com_datacompliance.moment",
72 | "description": "Moment — handles date conversions in JavaScript",
73 | "type": "script",
74 | "uri": "https://cdn.jsdelivr.net/npm/moment@2.27.0",
75 | "attributes": {
76 | "defer": true
77 | }
78 | },
79 |
80 | {
81 | "name": "com_datacompliance.backend",
82 | "type": "preset",
83 | "dependencies": [
84 | "com_datacompliance.backend#style"
85 | ]
86 | }
87 | ]
88 | }
--------------------------------------------------------------------------------
/component/media/js/controlpanel.min.js:
--------------------------------------------------------------------------------
1 | "use strict";if("undefined"==typeof akeeba)var akeeba={};"undefined"==typeof akeeba.DataCompliance&&(akeeba.DataCompliance={}),akeeba.DataCompliance.ControlPanel={},akeeba.DataCompliance.ControlPanel.loadUserGraphs=function(){var a=Joomla.getOptions("com_datacompliance.controlpanel.userGraphsUrl");Joomla.request({url:a,method:"GET",perform:!0,onSuccess:function(a){var b=JSON.parse(a),c=document.getElementById("adcExpiredUsers").getContext("2d");new Chart(c,{type:"doughnut",data:{labels:[Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_INACTIVE"),Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_ACTIVE"),Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_DELETED")],datasets:[{data:[b.expired,b.active,b.deleted],backgroundColor:["#ff0000","#009900","#666666"],borderWidth:6}]},options:{cutout:"50%",plugins:{legend:{display:!1}}}})}})},akeeba.DataCompliance.ControlPanel.loadWipedGraphs=function(){var a=Joomla.getOptions("com_datacompliance.controlpanel.wipedGraphsUrl");Joomla.request({url:a,method:"GET",perform:!0,onSuccess:function(a){var b=JSON.parse(a),c=document.getElementById("adcWipedUsers").getContext("2d");new Chart(c,{type:"bar",data:{datasets:[{label:Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_USER"),backgroundColor:"#009900",data:b.user},{label:Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_ADMIN"),backgroundColor:"#ff0000",data:b.admin},{label:Joomla.Text._("COM_DATACOMPLIANCE_CONTROLPANEL_LBL_CHART_LIFECYCLE"),backgroundColor:"#666666",data:b.lifecycle}]},options:{scales:{x:{stacked:!0,type:"time",time:{unit:"day"},distribution:"linear"},y:{type:"linear",stacked:!0,ticks:{callback:function(a){return""+a}}}},plugins:{legend:{display:!0,position:"right"}}}})}})},akeeba.DataCompliance.ControlPanel.loadUserGraphs(),akeeba.DataCompliance.ControlPanel.loadWipedGraphs();
2 | //# sourceMappingURL=controlpanel.min.js.map
--------------------------------------------------------------------------------
/component/media/js/options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @package AkeebaDataCompliance
3 | * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
4 | * @license GNU General Public License version 3, or later
5 | */
6 | "use strict";
7 |
8 | document.querySelectorAll('a.akeebaDataComplianceArticleToggle').forEach(function(element) {
9 | element.addEventListener('click', function(event) {
10 | event.preventDefault();
11 |
12 | const elTarget = document.getElementById('datacompliance-article');
13 |
14 | if (elTarget.style.display === "none")
15 | {
16 | elTarget.style.display = "block";
17 |
18 | return false;
19 | }
20 |
21 | elTarget.style.display = "none";
22 |
23 | return false;
24 | })
25 | });
--------------------------------------------------------------------------------
/component/media/js/options.min.js:
--------------------------------------------------------------------------------
1 | "use strict";document.querySelectorAll("a.akeebaDataComplianceArticleToggle").forEach(function(a){a.addEventListener("click",function(a){a.preventDefault();var b=document.getElementById("datacompliance-article");return"none"===b.style.display?(b.style.display="block",!1):(b.style.display="none",!1)})});
2 | //# sourceMappingURL=options.min.js.map
--------------------------------------------------------------------------------
/component/media/js/options.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"options.min.js","names":["document","querySelectorAll","forEach","element","addEventListener","event","preventDefault","elTarget","getElementById","style","display"],"sources":["options.js"],"sourcesContent":["/**\n * @package AkeebaDataCompliance\n * @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd\n * @license GNU General Public License version 3, or later\n */\n\"use strict\";\n\ndocument.querySelectorAll('a.akeebaDataComplianceArticleToggle').forEach(function(element) {\n element.addEventListener('click', function(event) {\n event.preventDefault();\n\n const elTarget = document.getElementById('datacompliance-article');\n\n if (elTarget.style.display === \"none\")\n {\n elTarget.style.display = \"block\";\n\n return false;\n }\n\n elTarget.style.display = \"none\";\n\n return false;\n })\n});"],"mappings":"AAKA,YAAY,CAEZA,QAAQ,CAACC,gBAAgB,CAAC,qCAAqC,CAAC,CAACC,OAAO,CAAC,SAASC,CAAO,CAAE,CACxFA,CAAO,CAACC,gBAAgB,CAAC,OAAO,CAAE,SAASC,CAAK,CAAE,CAC9CA,CAAK,CAACC,cAAc,CAAC,CAAC,CAEtB,GAAM,CAAAC,CAAQ,CAAGP,QAAQ,CAACQ,cAAc,CAAC,wBAAwB,CAAC,CAAC,MAEpC,MAAM,GAAjCD,CAAQ,CAACE,KAAK,CAACC,OAAkB,EAEjCH,CAAQ,CAACE,KAAK,CAACC,OAAO,CAAG,OAAO,MAKpCH,CAAQ,CAACE,KAAK,CAACC,OAAO,CAAG,MAAM,IAGnC,CAAC,CACJ,CAAC,CAAC","ignoreList":[]}
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "akeeba/com_datacompliance",
3 | "description": "Akeeba DataCompliance for Joomla",
4 | "config": {
5 | "vendor-dir": "component/backend/vendor",
6 | "platform": {
7 | "php": "7.4.0"
8 | }
9 | },
10 | "require": {
11 | "php": "^7.4.0||^8.0.0",
12 | "akeeba/s3": "dev-development",
13 | "ext-simplexml": "*",
14 | "ext-dom": "*",
15 | "ext-json": "*"
16 | }
17 | }
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "03733880220dac2f3e15bd3a16d83013",
8 | "packages": [
9 | {
10 | "name": "akeeba/s3",
11 | "version": "dev-development",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/akeeba/s3.git",
15 | "reference": "f4f20122476b27ffa243f8a49aeaf7813cbc8932"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/akeeba/s3/zipball/f4f20122476b27ffa243f8a49aeaf7813cbc8932",
20 | "reference": "f4f20122476b27ffa243f8a49aeaf7813cbc8932",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "ext-curl": "*",
25 | "ext-simplexml": "*",
26 | "php": ">=7.1.0 <8.4"
27 | },
28 | "default-branch": true,
29 | "type": "library",
30 | "autoload": {
31 | "files": [
32 | "src/aliasing.php"
33 | ],
34 | "psr-4": {
35 | "Akeeba\\S3\\": "src"
36 | }
37 | },
38 | "notification-url": "https://packagist.org/downloads/",
39 | "license": [
40 | "GPL-3.0-or-later"
41 | ],
42 | "authors": [
43 | {
44 | "name": "Nicholas K. Dionysopoulos",
45 | "email": "nicholas_NO_SPAM_PLEASE@akeeba.com",
46 | "homepage": "http://www.dionysopoulos.me",
47 | "role": "Lead Developer"
48 | }
49 | ],
50 | "description": "A compact, dependency-less Amazon S3 API client implementing the most commonly used features",
51 | "homepage": "https://github.com/akeeba/s3",
52 | "keywords": [
53 | "s3"
54 | ],
55 | "support": {
56 | "issues": "https://github.com/akeeba/s3/issues",
57 | "source": "https://github.com/akeeba/s3/tree/development"
58 | },
59 | "time": "2025-01-06T14:29:07+00:00"
60 | }
61 | ],
62 | "packages-dev": [],
63 | "aliases": [],
64 | "minimum-stability": "stable",
65 | "stability-flags": {
66 | "akeeba/s3": 20
67 | },
68 | "prefer-stable": false,
69 | "prefer-lowest": false,
70 | "platform": {
71 | "php": "^7.4.0||^8.0.0",
72 | "ext-simplexml": "*",
73 | "ext-dom": "*",
74 | "ext-json": "*"
75 | },
76 | "platform-dev": {},
77 | "platform-overrides": {
78 | "php": "7.4.0"
79 | },
80 | "plugin-api-version": "2.6.0"
81 | }
82 |
--------------------------------------------------------------------------------
/documentation/images/control_panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/documentation/images/control_panel.png
--------------------------------------------------------------------------------
/documentation/images/self_service_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/documentation/images/self_service_page.png
--------------------------------------------------------------------------------
/modules/admin/.gitinclude:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/modules/admin/.gitinclude
--------------------------------------------------------------------------------
/modules/site/.gitinclude:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/modules/site/.gitinclude
--------------------------------------------------------------------------------
/plugins/console/datacompliance/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/console/datacompliance/datacompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_CONSOLE_DATACOMPLIANCE
10 | 3.2.3
11 | 2025-02-10
12 | Nicholas K. Dionysopoulos
13 | nicholas@dionysopoulos.me
14 | https://www.akeeba.com
15 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
16 | GNU General Public License version 3, or later
17 | PLG_CONSOLE_DATACOMPLIANCE_XML_DESCRIPTION
18 | Akeeba\Plugin\Console\DataCompliance
19 |
20 | services
21 | src
22 |
23 | .htaccess
24 | web.config
25 |
26 |
27 | en-GB/plg_console_datacompliance.ini
28 | en-GB/plg_console_datacompliance.sys.ini
29 |
30 |
--------------------------------------------------------------------------------
/plugins/console/datacompliance/language/en-GB/plg_console_datacompliance.ini:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akeeba/com_datacompliance/6ed6e0a91fcd8ca57746e5c0c0432ed1e8a41593/plugins/console/datacompliance/language/en-GB/plg_console_datacompliance.ini
--------------------------------------------------------------------------------
/plugins/console/datacompliance/language/en-GB/plg_console_datacompliance.sys.ini:
--------------------------------------------------------------------------------
1 | PLG_CONSOLE_DATACOMPLIANCE="Console - Data Compliance"
2 | PLG_CONSOLE_DATACOMPLIANCE_XML_DESCRIPTION="Adds Akeeba Data Compliance commands to the Joomla console application (cli/joomla.php)."
--------------------------------------------------------------------------------
/plugins/console/datacompliance/language/index.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
--------------------------------------------------------------------------------
/plugins/console/datacompliance/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider(new MVCFactory('Akeeba\\Component\\DataCompliance'));
40 |
41 | $container->set(
42 | PluginInterface::class,
43 | function (Container $container) {
44 | $config = (array) PluginHelper::getPlugin('console', 'datacompliance');
45 | $subject = $container->get(DispatcherInterface::class);
46 | $mvcFactory = $container->get(MVCFactoryInterface::class);
47 |
48 | $plugin = new DataCompliance($subject, $config, $mvcFactory);
49 |
50 | $plugin->setApplication(Factory::getApplication());
51 | $plugin->setDatabase($container->get(DatabaseInterface::class));
52 |
53 | return $plugin;
54 | }
55 | );
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/plugins/console/datacompliance/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/ars.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_DATACOMPLIANCE_ARS
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_DATACOMPLIANCE_ARS_DESCRIPTION
18 | Akeeba\Plugin\DataCompliance\ARS
19 |
20 |
21 | language
22 | services
23 | src
24 |
25 | .htaccess
26 | web.config
27 |
28 |
29 |
30 | en-GB/plg_datacompliance_ars.ini
31 | en-GB/plg_datacompliance_ars.sys.ini
32 |
33 |
34 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/language/en-GB/plg_datacompliance_ars.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_ARS="Data Compliance – Akeeba Release System"
6 | PLG_DATACOMPLIANCE_ARS_DESCRIPTION="GDPR Data Compliance plugin for Akeeba Release System"
7 | PLG_DATACOMPLIANCE_ARS_DOMAINNAME="Akeeba Release System"
8 |
9 | PLG_DATACOMPLIANCE_ARS_ACTIONS_1="Download log entries linked to your account wil be deleted. Clarification: any download attempts you performed without being logged into our site and / or using a valid Download ID are NOT deleted as they cannot be automatically linked to your user account and we have no technical means to make that connection either."
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/language/en-GB/plg_datacompliance_ars.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_ARS="Data Compliance – Akeeba Release System"
6 | PLG_DATACOMPLIANCE_ARS_DESCRIPTION="GDPR Data Compliance plugin for Akeeba Release System"
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $config = (array) PluginHelper::getPlugin('datacompliance', 'ars');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new ARS(
42 | $dispatcher, $config, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ars/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/ats.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_DATACOMPLIANCE_ATS
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_DATACOMPLIANCE_ATS_DESCRIPTION
18 | Akeeba\Plugin\DataCompliance\ATS
19 |
20 | language
21 | services
22 | src
23 |
24 | .htaccess
25 | web.config
26 |
27 |
28 |
29 | en-GB/plg_datacompliance_ats.ini
30 | en-GB/plg_datacompliance_ats.sys.ini
31 |
32 |
33 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/language/en-GB/plg_datacompliance_ats.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_ATS="Data Compliance – Akeeba Ticket System"
6 | PLG_DATACOMPLIANCE_ATS_DESCRIPTION="GDPR Data Compliance plugin for Akeeba Ticket System"
7 | PLG_DATACOMPLIANCE_ATS_DOMAINNAME="Akeeba Ticket System"
8 |
9 | PLG_DATACOMPLIANCE_ATS_ACTIONS_1="All of your private and public tickets will be deleted, including their posts and attachments."
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/language/en-GB/plg_datacompliance_ats.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_ATS="Data Compliance – Akeeba Ticket System"
6 | PLG_DATACOMPLIANCE_ATS_DESCRIPTION="GDPR Data Compliance plugin for Akeeba Ticket System"
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $config = (array) PluginHelper::getPlugin('datacompliance', 'ats');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new ATS(
42 | $dispatcher, $config, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/ats/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/email/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/email/email.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_DATACOMPLIANCE_EMAIL
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_DATACOMPLIANCE_EMAIL_DESCRIPTION
18 | Akeeba\Plugin\DataCompliance\Email
19 |
20 |
21 | language
22 | services
23 | src
24 |
25 | .htaccess
26 | web.config
27 |
28 |
29 |
30 | en-GB/plg_datacompliance_email.ini
31 | en-GB/plg_datacompliance_email.sys.ini
32 |
33 |
34 |
35 |
36 |
37 |
43 | JNo
44 | JYes
45 |
46 |
47 |
53 | JNo
54 | JYes
55 |
56 |
57 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/plugins/datacompliance/email/language/en-GB/plg_datacompliance_email.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_EMAIL="Data Compliance - Send emails on account deletion"
6 | PLG_DATACOMPLIANCE_EMAIL_DESCRIPTION="Send an email to the user and / or the administrator of the site whenever we are deleting a user account. IMPORTANT! You MUST publish this plugin BEFORE the “Data Compliance - Joomla! Core User Data” plugin. Otherwise this plugin will not be able to find the user's email address since it will have been deleted before this plugin runs!"
7 |
8 | PLG_DATACOMPLIANCE_EMAIL_USERS_LABEL="Email users"
9 | PLG_DATACOMPLIANCE_EMAIL_USERS_DESC="Email the user when they delete their user account, letting them know what steps we took to ensure their personal information is properly deleted."
10 |
11 | PLG_DATACOMPLIANCE_EMAIL_ADMINS_LABEL="Email administrators"
12 | PLG_DATACOMPLIANCE_EMAIL_ADMINS_DESC="Email the administrators of the site (see below) whenever a user deletes their account."
13 |
14 | PLG_DATACOMPLIANCE_JOOMLA_ADMINEMAILS_LABEL="Administrator emails"
15 | PLG_DATACOMPLIANCE_JOOMLA_ADMINEMAILS_DESC="Enter the emails of administrators, one address per line. The email addresses MUST belong to active Super User accounts on this site, otherwise they are ignored. If you leave this empty all Super Users of the site will be emailed when the Email administrators option above is enabled."
--------------------------------------------------------------------------------
/plugins/datacompliance/email/language/en-GB/plg_datacompliance_email.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_EMAIL="Data Compliance - Send emails on account deletion"
6 | PLG_DATACOMPLIANCE_EMAIL_DESCRIPTION="Send an email to the user and / or the administrator of the site whenever we are deleting a user account."
7 |
--------------------------------------------------------------------------------
/plugins/datacompliance/email/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $config = (array) PluginHelper::getPlugin('datacompliance', 'email');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new Email(
42 | $dispatcher, $config, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/email/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/joomla/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/joomla/language/en-GB/plg_datacompliance_joomla.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_JOOMLA="Data Compliance – Joomla! Core User Data"
6 | PLG_DATACOMPLIANCE_JOOMLA_DESCRIPTION="GDPR Data Compliance plugin for Joomla! Core User Data"
--------------------------------------------------------------------------------
/plugins/datacompliance/joomla/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $config = (array) PluginHelper::getPlugin('datacompliance', 'joomla');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new Joomla(
42 | $dispatcher, $config, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/joomla/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/language/en-GB/plg_datacompliance_loginguard.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_LOGINGUARD="Data Compliance – Akeeba LoginGuard"
6 | PLG_DATACOMPLIANCE_LOGINGUARD_DESCRIPTION="GDPR Data Compliance plugin for Akeeba LoginGuard"
7 | PLG_DATACOMPLIANCE_LOGINGUARD_DOMAINNAME="Akeeba LoginGuard"
8 |
9 | PLG_DATACOMPLIANCE_LOGINGUARD_ACTIONS_1="Your Two Step Verification / Two Factor Authentication preferences will be deleted."
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/language/en-GB/plg_datacompliance_loginguard.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_LOGINGUARD="Data Compliance – Akeeba LoginGuard"
6 | PLG_DATACOMPLIANCE_LOGINGUARD_DESCRIPTION="GDPR Data Compliance plugin for Akeeba LoginGuard"
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/loginguard.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_DATACOMPLIANCE_LOGINGUARD
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_DATACOMPLIANCE_LOGINGUARD_DESCRIPTION
18 | Akeeba\Plugin\DataCompliance\LoginGuard
19 |
20 |
21 | language
22 | services
23 | src
24 |
25 | .htaccess
26 | web.config
27 |
28 |
29 |
30 | en-GB/plg_datacompliance_loginguard.ini
31 | en-GB/plg_datacompliance_loginguard.sys.ini
32 |
33 |
34 |
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $params = (array) PluginHelper::getPlugin('datacompliance', 'loginguard');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new LoginGuard(
42 | $dispatcher, $params, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/loginguard/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/datacompliance/s3/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/datacompliance/s3/language/en-GB/plg_datacompliance_s3.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_DATACOMPLIANCE_S3="Data Compliance - Upload user deletion audit trail to S3-compatible storage"
6 | PLG_DATACOMPLIANCE_S3_DESCRIPTION="GDPR Data Compliance plugin to automatically upload the user deletion audit trail records as JSON files to Amazon S3 and other S3-compatible storage providers."
7 |
--------------------------------------------------------------------------------
/plugins/datacompliance/s3/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $params = (array) PluginHelper::getPlugin('datacompliance', 's3');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new S3(
42 | $dispatcher, $params, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/datacompliance/s3/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/system/datacompliance/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/system/datacompliance/datacompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_SYSTEM_DATACOMPLIANCE
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_SYSTEM_DATACOMPLIANCE_DESCRIPTION
18 | Akeeba\Plugin\System\DataCompliance
19 |
20 |
21 | services
22 | src
23 |
24 | .htaccess
25 | web.config
26 |
27 |
28 |
29 | en-GB/plg_system_datacompliance.ini
30 | en-GB/plg_system_datacompliance.sys.ini
31 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/plugins/system/datacompliance/language/en-GB/plg_system_datacompliance.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_SYSTEM_DATACOMPLIANCE="System – Data Compliance"
6 | PLG_SYSTEM_DATACOMPLIANCE_DESCRIPTION="Shows the captive login page for the Akeeba Data Compliance component for GDPR compliance"
7 |
8 | PLG_SYSTEM_DATACOMPLIANCE_EXEMPT_LABEL="Exempt components / views / tasks"
9 | PLG_SYSTEM_DATACOMPLIANCE_EXEMPT_DESC="Combinations of component, view and task which are exempt from the captive login. Comma or newline separated entries. Each entry is in the format component.view.task
. Use *
in any position to match any value."
10 |
11 | PLG_SYSTEM_DATACOMPLIANCE_MSG_MUSTACCEPT="You must provide your consent below to continue using our site."
--------------------------------------------------------------------------------
/plugins/system/datacompliance/language/en-GB/plg_system_datacompliance.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_SYSTEM_DATACOMPLIANCE="System – Data Compliance"
6 | PLG_SYSTEM_DATACOMPLIANCE_DESCRIPTION="Shows the captive login page for the Akeeba Data Compliance component for GDPR compliance"
7 |
--------------------------------------------------------------------------------
/plugins/system/datacompliance/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
35 |
36 | $container->set(
37 | PluginInterface::class,
38 | function (Container $container) {
39 | $params = (array) PluginHelper::getPlugin('system', 'datacompliance');
40 | $dispatcher = $container->get(DispatcherInterface::class);
41 |
42 | $plugin = new DataCompliance(
43 | $dispatcher, $params, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
44 | );
45 |
46 | $plugin->setApplication(Factory::getApplication());
47 | $plugin->setDatabase($container->get(DatabaseInterface::class));
48 |
49 | return $plugin;
50 | }
51 | );
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/plugins/system/datacompliance/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Order deny,allow
3 | Deny from all
4 |
5 |
6 |
7 | Require all denied
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/datacompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | PLG_USER_DATACOMPLIANCE
10 | Nicholas K. Dionysopoulos
11 | nicholas@akeeba.com
12 | https://www.akeeba.com
13 | Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
14 | GNU General Public License version 3, or later
15 | 2025-02-10
16 | 3.2.3
17 | PLG_USER_DATACOMPLIANCE_DESCRIPTION
18 | Akeeba\Plugin\User\DataCompliance
19 |
20 |
21 | datacompliance
22 | language
23 | services
24 | src
25 |
26 | .htaccess
27 | web.config
28 |
29 |
30 |
31 | en-GB/plg_user_datacompliance.ini
32 | en-GB/plg_user_datacompliance.sys.ini
33 |
34 |
35 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/datacompliance/datacompliance.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/datacompliance/list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/language/en-GB/plg_user_datacompliance.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_USER_DATACOMPLIANCE="User – Data Compliance"
6 | PLG_USER_DATACOMPLIANCE_DESCRIPTION="Records user profile changes with the Akeeba Data Compliance component for GDPR compliance. Also displays the special user field which links to the Data Compliance self–service page for Personally Identifiable Information."
7 |
8 | PLG_USER_DATACOMPLIANCE_HEADER="Personal Data Options"
9 | PLG_USER_DATACOMPLIANCE_FIELD_LABEL="Your Personal Data Options"
10 | PLG_USER_DATACOMPLIANCE_FIELD_DESC="Click this link to go into a page where you can can give / withdraw your consent to your personal data being processed, as well as export your personal data or delete your profile with us. When you use this button any other changes you have made to your user profile without previously saving them will be lost."
11 | PLG_USER_DATACOMPLIANCE_FIELD_INFO="Manage your personal data options"
12 | PLG_USER_DATACOMPLIANCE_FIELD_INFO_ADMIN="Manage the user's data options"
13 |
14 | PLG_USER_DATACOMPLIANCE_FIELD_HASCONSENT_LABEL="Consent to personal data processing"
15 |
16 | PLG_USER_DATACOMPLIANCE_ERR_NOUSER="No such user."
17 | PLG_USER_DATACOMPLIANCE_ERR_NOCOMPONENT="The Akeeba Data Compliance component has not been installed or is currently deactivated."
--------------------------------------------------------------------------------
/plugins/user/datacompliance/language/en-GB/plg_user_datacompliance.sys.ini:
--------------------------------------------------------------------------------
1 | ;; @package AkeebaDataCompliance
2 | ;; @copyright Copyright (c)2018-2025 Nicholas K. Dionysopoulos / Akeeba Ltd
3 | ;; @license GNU General Public License version 3, or later
4 |
5 | PLG_USER_DATACOMPLIANCE="User – Data Compliance"
6 | PLG_USER_DATACOMPLIANCE_DESCRIPTION="Records user profile changes with the Akeeba Data Compliance component for GDPR compliance. Also displays the special user field which links to the Data Compliance self–service page for Personally Identifiable Information."
7 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/services/provider.php:
--------------------------------------------------------------------------------
1 | registerServiceProvider($mvcFactory);
34 |
35 | $container->set(
36 | PluginInterface::class,
37 | function (Container $container) {
38 | $params = (array) PluginHelper::getPlugin('user', 'datacompliance');
39 | $dispatcher = $container->get(DispatcherInterface::class);
40 |
41 | $plugin = new DataCompliance(
42 | $dispatcher, $params, new \Joomla\CMS\MVC\Factory\MVCFactory('Akeeba\\Component\\DataCompliance')
43 | );
44 |
45 | $plugin->setApplication(Factory::getApplication());
46 | $plugin->setDatabase($container->get(DatabaseInterface::class));
47 |
48 | return $plugin;
49 | }
50 | );
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/src/Field/DatacomplianceField.php:
--------------------------------------------------------------------------------
1 | form->getData()->get('id', null);
31 |
32 | if (is_null($user_id))
33 | {
34 | return Text::_('PLG_USER_DATACOMPLIANCE_ERR_NOUSER');
35 | }
36 |
37 | /**
38 | * Why not use HMVC to display the Options page inside the user form just like we do with LoginGuard? Our page
39 | * has a FORM element. This is rendered inside Joomla's own FORM element, since all user fields are form fields,
40 | * right? However, Joomla seems to have some magic JavaScript which removes the nested form elements (since
41 | * you can't normally have nested form elements), moving their contents one level up. This of course completely
42 | * breaks the behaviour of our software. So instead I have to add a link to the actual page. Too tired to battle
43 | * with this.
44 | */
45 |
46 | $url = Route::_('index.php?option=com_datacompliance&view=options&user_id=' . $user_id);
47 | $user = Factory::getApplication()->getIdentity() ?? new User();
48 | $isAdmin = $user_id != $user->id;
49 | $key = $isAdmin ? 'PLG_USER_DATACOMPLIANCE_FIELD_INFO_ADMIN' : 'PLG_USER_DATACOMPLIANCE_FIELD_INFO';
50 | $labelText = Text::_($key);
51 | $html = <<< HTML
52 | $labelText
53 |
54 | HTML;
55 |
56 | // Display the content
57 | return $html;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/plugins/user/datacompliance/web.config:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------