diff --git a/.appveyor.yml b/.appveyor.yml
index 60cee9472f7bf..7e5f1c09bcf00 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -2,17 +2,37 @@ build: false
platform:
- x64
clone_folder: C:\projects\joomla-cms
+
+branches:
+ except:
+ - /l10n_*/
+
## Build matrix for lowest and highest possible targets
environment:
+ DLLVersion: "5.6.1"
+ PHPBuild: "x64"
+ VC: "vc15"
matrix:
- php_ver_target: 5.6
+ PHPBuild: "x86"
+ VC: "vc11"
+ WINCACHE: "1.3.7.12"
- php_ver_target: 7.0
+ DLLVersion: "5.3.0"
+ VC: "vc14"
+ WINCACHE: "2.0.0.8"
- php_ver_target: 7.1
+ DLLVersion: "5.3.0"
+ VC: "vc14"
+ WINCACHE: "2.0.0.8"
+ - php_ver_target: 7.2
+ DLLVersion: "5.3.0"
+ - php_ver_target: 7.3
init:
- SET PATH=C:\Program Files\OpenSSL;C:\tools\php;%PATH%
- SET COMPOSER_NO_INTERACTION=1
- - SET PHP=1 # This var relates to caching the php install
+ - SET PHP=1 # This var relates to caching the php install
- SET ANSICON=121x90 (121x90)
services:
- mssql2014
@@ -23,15 +43,14 @@ services:
## Install PHP and composer, and run the appropriate composer command
install:
- IF EXIST C:\tools\php (SET PHP=0)
+ # TODO: This is a workaround for https://github.com/chocolatey/choco/issues/1843. Once this is fixed we
+ # should go back to latest version in appveyor saving ourselves test time
+ - ps: choco upgrade chocolatey -y --version 0.10.13 --allow-downgrade
- ps: >-
If ($env:php_ver_target -eq "5.6") {
appveyor-retry cinst --params '""/InstallDir:C:\tools\php""' --ignore-checksums -y --forcex86 php --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','')
- $VC = "vc11"
- $PHPBuild = "x86"
} Else {
appveyor-retry cinst --params '""/InstallDir:C:\tools\php""' --ignore-checksums -y php --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','')
- $VC = "vc14"
- $PHPBuild = "x64"
}
- appveyor-retry cinst -y sqlite
- cd C:\tools\php
@@ -48,20 +67,21 @@ install:
copy SQLSRV\php_pdo_sqlsrv_56_nts.dll ext\php_pdo_sqlsrv_nts.dll
Remove-Item C:\tools\php\* -include .zip
} Else {
- $DLLVersion = "4.3.0"
cd c:\tools\php\ext
- $source = "http://windows.php.net/downloads/pecl/releases/sqlsrv/$($DLLVersion)/php_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip"
- $destination = "c:\tools\php\ext\php_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip"
+ $source = "https://windows.php.net/downloads/pecl/releases/sqlsrv/$($env:DLLVersion)/php_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
+ $destination = "c:\tools\php\ext\php_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
Invoke-WebRequest $source -OutFile $destination
- #appveyor-retry appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/sqlsrv/$($DLLVersion)/php_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip
- 7z x -y php_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip > $null
- $source = "http://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/$($DLLVersion)/php_pdo_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip"
- $destination = "c:\tools\php\ext\php_pdo_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip"
+ #appveyor-retry appveyor DownloadFile https://windows.php.net/downloads/pecl/releases/sqlsrv/$($env:DLLVersion)/php_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip
+ 7z x -y php_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip > $null
+ $source = "https://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/$($env:DLLVersion)/php_pdo_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
+ $destination = "c:\tools\php\ext\php_pdo_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
Invoke-WebRequest $source -OutFile $destination
- #appveyor-retry appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/$($DLLVersion)/php_pdo_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip
- 7z x -y php_pdo_sqlsrv-$($DLLVersion)-$($env:php_ver_target)-nts-vc14-x64.zip > $null
+ #appveyor-retry appveyor DownloadFile https://windows.php.net/downloads/pecl/releases/pdo_sqlsrv/$($env:DLLVersion)/php_pdo_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip
+ 7z x -y php_pdo_sqlsrv-$($env:DLLVersion)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip > $null
Remove-Item c:\tools\php\ext* -include .zip
- cd c:\tools\php}}
+ cd c:\tools\php
+ }
+ }
- IF %PHP%==1 copy php.ini-production php.ini /Y
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini
- IF %PHP%==1 echo extension_dir=ext >> php.ini
@@ -89,18 +109,19 @@ install:
- IF %PHP%==1 echo extension=php_curl.dll >> php.ini
# Get the Wincache DLLs
- ps: >-
- If ($env:PHP -eq "1") {
- If ($env:php_ver_target -eq "5.6") {$wincache = "1.3.7.12"} Else {$wincache = "2.0.0.8"}
+ If ($env:PHP -eq "1" -and $env:WINCACHE) {
cd c:\tools\php\ext
- $source = "http://windows.php.net/downloads/pecl/releases/wincache/$($wincache)/php_wincache-$($wincache)-$($env:php_ver_target)-nts-$($VC)-$($PHPBuild).zip"
- $destination = "c:\tools\php\ext\php_wincache-$($wincache)-$($env:php_ver_target)-nts-$($VC)-$($PHPBuild).zip"
+ $source = "https://windows.php.net/downloads/pecl/releases/wincache/$($env:WINCACHE)/php_wincache-$($env:WINCACHE)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
+ $destination = "c:\tools\php\ext\php_wincache-$($env:WINCACHE)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip"
Invoke-WebRequest $source -OutFile $destination
- #appveyor-retry appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/wincache/$($wincache)/php_wincache-$($wincache)-$($env:php_ver_target)-nts-$($VC)-$($PHPBuild).zip
- 7z x -y php_wincache-$($wincache)-$($env:php_ver_target)-nts-$($VC)-$($PHPBuild).zip > $null
+ #appveyor-retry appveyor DownloadFile https://windows.php.net/downloads/pecl/releases/wincache/$($env:WINCACHE)/php_wincache-$($env:WINCACHE)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip
+ 7z x -y php_wincache-$($env:WINCACHE)-$($env:php_ver_target)-nts-$($env:VC)-$($env:PHPBuild).zip > $null
Remove-Item C:\tools\php\ext* -include .zip
- cd c:\tools\php}
- - IF %PHP%==1 echo extension=php_wincache.dll >> php.ini
- - IF %PHP%==1 echo wincache.enablecli = 1 >> php.ini
+ cd c:\tools\php
+ Add-Content php.ini "`nextension=php_wincache.dll"
+ Add-Content php.ini "`wincache.enablecli = 1"
+ Add-Content php.ini "`n"
+ }
- IF %PHP%==1 echo zend_extension=php_opcache.dll >> php.ini
- IF %PHP%==1 echo opcache.enable_cli=1 >> php.ini
- IF %PHP%==1 echo extension=php_ldap.dll >> php.ini
@@ -130,4 +151,4 @@ before_test:
test_script:
- cd C:\projects\joomla-cms
- - libraries/vendor/bin/phpunit -c appveyor-phpunit.xml
+ - ps: If ($env:php_ver_target -eq "5.6") {libraries/vendor/bin/phpunit -c appveyor-phpunit.xml --exclude-group not-on-windows } Else {libraries/vendor/bin/phpunit -c appveyor-phpunit.xml}
diff --git a/.drone.yml b/.drone.yml
index aee8988aa80fb..53f57addf5136 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1,21 +1,33 @@
-pipeline:
- clone:
- image: plugins/git
- depth: 1
+---
+kind: pipeline
+name: default
- phpcs:
- image: joomlaprojects/docker-phpcs
+clone:
+ depth: 42
+
+steps:
+ - name: phpcs
+ image: joomlaprojects/docker-images:phpcs1.5
commands:
- echo $(date)
- - /root/.composer/vendor/bin/phpcs --report=full --encoding=utf-8 --extensions=php -p --standard=build/phpcs/Joomla .
+ - /usr/local/vendor/bin/phpcs --report=full --encoding=utf-8 --extensions=php -p --standard=build/phpcs/Joomla .
- echo $(date)
- javascript:
- image: joomlaprojects/docker-systemtests:latest
+ - name: restore-cache
+ image: drillster/drone-volume-cache
+ settings:
+ restore: true
+ mount:
+ - ./tests/javascript/node_modules
+ cache_key: [ DRONE_REPO_NAMESPACE, DRONE_REPO_NAME, DRONE_BRANCH, DRONE_STAGE_NUMBER ]
+ volumes:
+ - name: cache
+ path: /cache
+
+ - name: javascript
+ image: joomlaprojects/docker-images:systemtests
commands:
- echo $(date)
- - apt-get install nodejs npm
- - ln -s /usr/bin/nodejs /usr/bin/node
- export DISPLAY=:0
- Xvfb -screen 0 1024x768x24 -ac +extension GLX +render -noreset > /dev/null 2>&1 &
- sleep 3
@@ -26,3 +38,41 @@ pipeline:
- tests/javascript/node_modules/karma/bin/karma start karma.conf.js --single-run
- echo $(date)
+ - name: rebuild-cache
+ image: drillster/drone-volume-cache
+ settings:
+ rebuild: true
+ mount:
+ - ./tests/javascript/node_modules
+ cache_key: [ DRONE_REPO_NAMESPACE, DRONE_REPO_NAME, DRONE_BRANCH, DRONE_STAGE_NUMBER ]
+ volumes:
+ - name: cache
+ path: /cache
+
+ - name: analysis3x
+ image: rips/rips-cli:3.2.2
+ when:
+ branch: staging
+ commands:
+ - export RIPS_BASE_URI='https://api.rips.joomla.org'
+ - if [ $DRONE_REPO_NAMESPACE != 'joomla' ]; then echo "The analysis check only run on the main repos"; exit 0; fi
+ - rips-cli rips:list --table=scans -n -p filter='{"__and":[{"__lessThan":{"percent":100}}]}'
+ - rips-cli rips:scan:start -G -a 1 -t 1 -p $(pwd) -t 1 -R -k -T $DRONE_REPO_NAMESPACE-$DRONE_BRANCH || { echo "Please contact the security team at security@joomla.org"; exit 1; }
+ environment:
+ RIPS_EMAIL:
+ from_secret: RIPS_EMAIL
+ RIPS_PASSWORD:
+ from_secret: RIPS_PASSWORD
+
+volumes:
+- name: cache
+ host:
+ path: /tmp/cache
+
+branches:
+ exclude: [ l10n_* ]
+---
+kind: signature
+hmac: 7d1fbc23a21067e251bb5d22a875c5db23c88520d0384f1913a73aa0c992d017
+
+...
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000000..287896868eaa4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 3af433d8336f1..46edbbe030714 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -7,18 +7,11 @@ plugins/fields/* @laoneo
plugins/systems/fields/* @laoneo
# Smart Search
-administrator/components/com_finder/* @mbabker
-components/com_finder/* @mbabker
-modules/mod_finder/* @mbabker
-plugins/content/finder/* @mbabker
-plugins/finder/* @mbabker
-
-# Language strings
-administrator/language/en-GB/* @brianteeman
-installation/language/en-GB/* @brianteeman
-language/en-GB/* @brianteeman
-README.md @brianteeman
-README.txt @brianteeman
+#administrator/components/com_finder/*
+#components/com_finder/*
+#modules/mod_finder/*
+#plugins/content/finder/*
+#plugins/finder/*
# CodeMirror
media/editors/codemirror/* @okonomiyaki3000
@@ -28,33 +21,33 @@ plugins/editors/codemirror/* @okonomiyaki3000
plugins/system/stats/* @mbabker @wilsonge
# Release Tools
-build.xml @mbabker
-build/build.php @mbabker @rdeutz @wilsonge
-build/bump.php @mbabker @rdeutz @wilsonge
-build/deleted_file_check.php @mbabker @rdeutz @wilsonge
+build.xml @wilsonge
+build/build.php @rdeutz @wilsonge
+build/bump.php @rdeutz @wilsonge
+build/deleted_file_check.php @rdeutz @wilsonge
# Core/Extension Install/Update Tools
-administrator/components/com_joomlaupdate/* @mbabker @rdeutz @wilsonge @zero-24
-libraries/src/Installer/* @mbabker @rdeutz @wilsonge @zero-24
-libraries/src/Updater/* @mbabker @rdeutz @wilsonge @zero-24
+administrator/components/com_joomlaupdate/* @rdeutz @wilsonge @zero-24
+libraries/src/Installer/* @rdeutz @wilsonge @zero-24
+libraries/src/Updater/* @rdeutz @wilsonge @zero-24
# Automated Testing
-build/jenkins/* @mbabker @rdeutz
-build/travis/* @mbabker @rdeutz
+build/jenkins/* @rdeutz
+build/travis/* @rdeutz
tests/codeception/* @rdeutz
-tests/javascript/* @dgt41 @rdeutz
-tests/unit/* @mbabker @rdeutz
-.appveyor.yml @mbabker @rdeutz
+tests/javascript/* @wilsonge @rdeutz
+tests/unit/* @rdeutz
+.appveyor.yml @rdeutz
.drone.yml @rdeutz
-.hound.yml @mbabker
-.travis.yml @mbabker @rdeutz
-appveyor-phpunit.xml @mbabker @rdeutz
+.hound.yml @wilsonge
+.travis.yml @rdeutz
+appveyor-phpunit.xml @rdeutz
codeception.yml @rdeutz
-karma.conf.js @dgt41 @rdeutz
-phpunit.xml.dist @mbabker @rdeutz
+karma.conf.js @wilsonge @rdeutz
+phpunit.xml.dist @rdeutz
RoboFile.dist.ini @rdeutz
RoboFile.php @rdeutz
-travis-phpunit.xml @mbabker @rdeutz
+travis-phpunit.xml @rdeutz
# Core JS
-media/*/js/* @dgt41
+media/*/js/* @wilsonge
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 8de2598070b5a..3e1635228df47 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -19,7 +19,6 @@ There are other branches available which serve specific purposes.
| Branch | Purpose |
| ------ | ------- |
-| staging | Current codebase. Branch for the next minor Joomla version. New backward compatible features go into this branch. |
-| 3.9-dev | Branch for the next minor Joomla version. The 3.9.0 release will include the Privacy Tools Suite features. Commits to staging will be applied to this branch as well. |
+| staging | Current codebase. Branch for the next minor Joomla version. New backward compatible features go into this branch. The 3.9.0 release will include the Privacy Tools Suite features. |
| 3.10-dev | Branch for the next minor Joomla version. The 3.10.0 release will only include compatibility features for 4.0. Commits to staging will be applied to this branch as well. |
| 4.0-dev | Branch for the next major Joomla version. New features go into this branch. Commits to staging will be applied to this branch as well. |
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000000..0be9be57ae051
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: https://community.joomla.org/sponsorship-campaigns.html
diff --git a/SECURITY.md b/.github/SECURITY.md
similarity index 87%
rename from SECURITY.md
rename to .github/SECURITY.md
index 449c2a5978fdd..52af74f0b6c0a 100644
--- a/SECURITY.md
+++ b/.github/SECURITY.md
@@ -9,9 +9,7 @@ This document outlines security procedures and policies for the `Joomla! Project
## Reporting a Bug
-The `Joomla` team and community take all security bugs in `Joomla` seriously.
-
-The Joomla! Project takes security vulnerabilities very seriously. As such, the Joomla! Security Strike Team (JSST) oversees the project's security issues and follows some specific procedures when dealing with these issues.
+The `Joomla` team and community take all security bugs in `Joomla` seriously. The Joomla! Security Strike Team (JSST) oversees the project's security issues and follows some specific procedures when dealing with these issues.
If you find a possible vulnerability, please report it to the JSST using the [online form](https://developer.joomla.org/security/contact-the-team.html) or via email at security@joomla.org
@@ -25,7 +23,7 @@ Thank you for improving the security of `Joomla`.
## Response Handling
-The JSST aims to ensure all issues are handled in a timely manner and for clear communication between the team and issue reporters. As such, we have established the following guidelines for responding to issue reports:
+The JSST aims to ensure all issues are handled in a timely manner and for clear communication between the team and issue reporters. We have established the following guidelines for responding to issue reports:
* Within 24 hours every report gets acknowledged
* Within 7 days every report gets a further response stating either
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index f716bd5f7e8e5..ba0ca3bc7b488 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -1,8 +1,8 @@
Where can you get support and help?
====================
-* [The Joomla! Documentation](https://docs.joomla.org/Special:MyLanguage/Main_Page);
-* [Frequently Asked Questions](https://docs.joomla.org/Special:MyLanguage/Category:FAQ) (FAQ);
-* Find the [information you need](https://docs.joomla.org/Special:MyLanguage/Start_here);
-* Find [help and other users](https://www.joomla.org/about-joomla/create-and-share.html);
-* Post questions at [our forums](https://forum.joomla.org);
+* [The Joomla! Documentation](https://docs.joomla.org/Special:MyLanguage/Main_Page).
+* [Frequently Asked Questions](https://docs.joomla.org/Special:MyLanguage/Category:FAQ) (FAQ).
+* Find the [information you need](https://docs.joomla.org/Special:MyLanguage/Start_here).
+* Find [help and other users](https://www.joomla.org/about-joomla/create-and-share.html).
+* Post questions at [our forums](https://forum.joomla.org).
* [Joomla Resources Directory](https://resources.joomla.org) (JRD).
diff --git a/.gitignore b/.gitignore
index fea6b1c925bcc..2cc459232af68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
# IDE & System Related Files #
.buildpath
-.editorconfig
.project
.settings
.DS_Store
@@ -39,6 +38,7 @@ phpdoc-*
/media/com_patchtester
# Install from Web plugin #
+/media/plg_installer_webinstaller
/plugins/installer/webinstaller
# Languages #
@@ -70,6 +70,23 @@ Desktop.ini
# Extra files installed by Composer not needed in the CMS environment
# This should only ignore files like unit testing or READMEs, production
# code must remain to ensure all libraries properly function
+/libraries/vendor/brumann/polyfill-unserialize/.gitattributes
+/libraries/vendor/brumann/polyfill-unserialize/.gitignore
+/libraries/vendor/brumann/polyfill-unserialize/.travis.yml
+/libraries/vendor/brumann/polyfill-unserialize/composer.json
+/libraries/vendor/brumann/polyfill-unserialize/composer.lock
+/libraries/vendor/brumann/polyfill-unserialize/LICENSE
+/libraries/vendor/brumann/polyfill-unserialize/phpunit.xml.dist
+/libraries/vendor/brumann/polyfill-unserialize/README.md
+/libraries/vendor/brumann/polyfill-unserialize/tests
+/libraries/vendor/google/recaptcha/examples
+/libraries/vendor/google/recaptcha/tests
+/libraries/vendor/google/recaptcha/.gitignore
+/libraries/vendor/google/recaptcha/.travis.yml
+/libraries/vendor/google/recaptcha/composer.json
+/libraries/vendor/google/recaptcha/CONTRIBUTING.md
+/libraries/vendor/google/recaptcha/phpunit.xml.dist
+/libraries/vendor/google/recaptcha/README.md
/libraries/vendor/ircmaxell/password-compat/test
/libraries/vendor/ircmaxell/password-compat/.travis.yml
/libraries/vendor/ircmaxell/password-compat/composer.json
@@ -98,6 +115,7 @@ Desktop.ini
/libraries/vendor/joomla/session/Joomla/Session/composer.json
/libraries/vendor/joomla/session/Joomla/Session/phpunit.xml.dist
/libraries/vendor/joomla/session/Joomla/Session/README.md
+/libraries/vendor/joomla/session/Joomla/Session/ruleset.xml
/libraries/vendor/leafo/lessphp/docs
/libraries/vendor/leafo/lessphp/tests
/libraries/vendor/leafo/lessphp/.gitignore
@@ -139,6 +157,7 @@ Desktop.ini
/libraries/vendor/phpmailer/phpmailer/.travis.yml
/libraries/vendor/phpmailer/phpmailer/changelog.md
/libraries/vendor/phpmailer/phpmailer/composer.json
+/libraries/vendor/phpmailer/phpmailer/composer.lock
/libraries/vendor/phpmailer/phpmailer/ISSUE_TEMPLATE.md
/libraries/vendor/phpmailer/phpmailer/phpdoc.dist.xml
/libraries/vendor/phpmailer/phpmailer/README.md
@@ -159,6 +178,8 @@ Desktop.ini
/libraries/vendor/symfony/polyfill-php55/README.md
/libraries/vendor/symfony/polyfill-php56/composer.json
/libraries/vendor/symfony/polyfill-php56/README.md
+/libraries/vendor/symfony/polyfill-php71/composer.json
+/libraries/vendor/symfony/polyfill-php71/README.md
/libraries/vendor/symfony/polyfill-php73/composer.json
/libraries/vendor/symfony/polyfill-php73/README.md
/libraries/vendor/symfony/polyfill-util/composer.json
@@ -166,6 +187,9 @@ Desktop.ini
/libraries/vendor/symfony/polyfill-util/LegacyTestListener.php
/libraries/vendor/symfony/polyfill-util/TestListener.php
/libraries/vendor/symfony/polyfill-util/TestListenerTrait.php
+/libraries/vendor/symfony/polyfill-util/TestListenerForV5.php
+/libraries/vendor/symfony/polyfill-util/TestListenerForV6.php
+/libraries/vendor/symfony/polyfill-util/TestListenerForV7.php
/libraries/vendor/symfony/yaml/Tests
/libraries/vendor/symfony/yaml/.gitignore
/libraries/vendor/symfony/yaml/CHANGELOG.md
@@ -182,6 +206,15 @@ Desktop.ini
/libraries/vendor/simplepie/simplepie/build
/libraries/vendor/simplepie/simplepie/idn/ReadMe.txt
/libraries/vendor/simplepie/simplepie/composer.json
+/libraries/vendor/typo3/phar-stream-wrapper/tests
+/libraries/vendor/typo3/phar-stream-wrapper/.appveyor.yml
+/libraries/vendor/typo3/phar-stream-wrapper/.gitattributes
+/libraries/vendor/typo3/phar-stream-wrapper/.gitignore
+/libraries/vendor/typo3/phar-stream-wrapper/.travis.yml
+/libraries/vendor/typo3/phar-stream-wrapper/composer.json
+/libraries/vendor/typo3/phar-stream-wrapper/LICENSE
+/libraries/vendor/typo3/phar-stream-wrapper/phpunit.xml
+/libraries/vendor/typo3/phar-stream-wrapper/README.md
# System Test related files
tests/codeception/acceptance.suite.yml
@@ -197,4 +230,3 @@ composer.phar
# Build related
RoboFile.ini
-
diff --git a/.travis.yml b/.travis.yml
index 070893c5e55d0..7e16b322c1b61 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,41 +6,51 @@ language: php
env:
global:
- RUN_UNIT_TESTS="yes"
- - INSTALL_MEMCACHE="yes"
+ - INSTALL_APCU="yes"
- INSTALL_MEMCACHED="yes"
- INSTALL_REDIS="yes"
matrix:
fast_finish: true
include:
- - php: 7.0
- env: INSTALL_APCU="yes" INSTALL_MEMCACHE="no"
- - php: 7.1
- env: INSTALL_APCU="yes" INSTALL_MEMCACHE="no"
# Requires older Precise image
- php: 5.3
- env: INSTALL_APC="yes"
+ env:
+ - INSTALL_APC="yes"
+ - INSTALL_APCU="no"
+ - INSTALL_MEMCACHE="yes"
sudo: true
dist: precise
# The new Trusty image has issues with running APC, do not enable it here
- php: 5.4
- env: INSTALL_APC="no"
+ env:
+ - INSTALL_APCU="no"
+ - INSTALL_MEMCACHE="yes"
+ dist: trusty
- php: 5.5
- env: INSTALL_APCU="yes"
+ env:
+ - INSTALL_MEMCACHE="yes"
+ dist: trusty
- php: 5.6
- env: INSTALL_APCU="yes"
+ env:
+ - INSTALL_MEMCACHE="yes"
+ dist: trusty
+ - php: 7.0
+ - php: 7.1
- php: 7.2
- env: INSTALL_APCU="yes" INSTALL_MEMCACHE="no"
+ - php: 7.3
+ - php: 7.4snapshot
- php: nightly
- env: INSTALL_APCU="yes" INSTALL_MEMCACHE="no"
allow_failures:
- - php: 7.2
+ - php: 7.4snapshot
- php: nightly
services:
- memcache
- memcached
- redis-server
+ - mysql
+ - postgresql
before_script:
# Make sure all dev dependencies are installed
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000000..990fc7493fc7f
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,62 @@
+# Joomla Contributor Covenant Code of Conduct
+
+This document outlines the Code of Conduct for all persons volunteering their service to the Joomla Project and/or Open Source Matters. It covers your behaviour as a member of the Joomla community, in any forum, mailing list, Wiki, Web site, IRC channel, install-fest, public meeting or private correspondence.
+
+If you cannot agree to any of these principles, then volunteering in the Joomla Project is not for you. Contributing to our community assumes acceptance of these principles:
+## Be Considerate
+
+You are working with others as a team so be considerate of how your actions or contribution affects your colleagues and the community as a whole.
+## Be Respectful
+
+Treat one another and members of the community with respect. Everyone can make a valuable contribution to Joomla. We may not always agree, but disagreement is no excuse for poor behavior or poor manners.
+
+We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It's important to remember that a community where people feel uncomfortable or threatened is not a productive one. We expect the members of Joomla community to be respectful when dealing with other volunteers as well as with people from outside projects and initiatives and with users.
+
+Avoid becoming involved in flame wars, trolling, personal attacks, and repetitive arguments. Take the matters "outside" (off-list, etc) if it helps resolve the situation, and do not use communal methods of communication to be a vehicle for your private "wall of shame."
+## Be Collaborative
+
+Joomla is free software and about collaboration and working together. Collaboration reduces redundancy of work done in the free software world, and improves the quality of the software produced regardless of whether you are writing code or performing some other task.
+
+When you disagree, consult others. Disagreements, both political and technical, happen all the time, and Joomla is no exception. Disagreement, debate and constructive criticism is often how progress is made and are a necessary part of doing complex work in a team.
+
+The important goal is not to avoid disagreements or differing views but to resolve them constructively. Above all, avoid making conflicts about the work into personal conflicts. Debate should never include reference to someone's nationality, gender, religion or other personal characteristics.
+
+You should turn to the community and to the community process to seek advice and to resolve disagreements. Team leaders and Department Coordinators are able to help you figure out which direction will be most acceptable.
+
+When you are unsure, ask for help. Nobody knows everything and nobody is expected to be perfect. Asking questions avoids many problems down the road and so questions are encouraged. Those who are asked should be responsive and helpful. However, when asking a question, care must be taken to do so in an appropriate forum. Off-topic questions, such as requests for help on a development mailing list, detract from productive discussion.
+## Step Down Considerately
+
+People on every project come and go, and Joomla is no different. When you leave or disengage from the community, in whole or in part, we ask that you do so in a way that minimizes disruption to the Project. This means you should tell people you are leaving and take the proper steps to ensure that others can pick up where you leave off.
+## Be Available
+
+Check your e-mails regularly and answer them promptly—even if it's "I'll get back to you."
+## Be Honest
+
+Sometimes the hardest thing to say is "no" or admit you've forgotten do something. Be honest with each other and yourself with regards to what you say and what you can realistically commit to.
+## Follow the Rules
+
+Volunteers are expected to uphold Joomla's licensing and trademark requirements including, but not limited to, compliance on their own or affiliate Web sites and extensions. Make sure you have sought the appropriate approvals for domain name, name and logo usage prior to volunteering and that any extensions you distribute comply with the Joomla license.
+
+All work contributed to the Project, whether code, documentation or other material, must observe the appropriate licenses as set down by the Core Team and Open Source Matters.
+
+Some contributors represent the Joomla Project in specific areas, but you should not speak on behalf of the Project or present yourself as an official representative of the Project unless you are specifically authorized to do so, and you should never state your opinions as the official policies of the Project.
+## Exercise Discretion and Confidentiality at Appropriate Times
+
+Depending on your role, you will be privy to various levels of information. As a volunteer you are expected to keep site access details (such as logins, FTP details, etc.) secure at all times.
+
+Information contained within private forums (for example, about serious security matters, legal cases, or personal details), private mailing lists, chats or other mediums is also to be kept confidential even after you have discontinued your service. Breaches in the area of privacy and confidentiality are taken very seriously by the Project.
+## Conflict of Interest
+
+When using Project resources or making decisions within the board, your department, team, or subteam or the concerning Project's policy positions, you must do so based only on the best interests of the Project and its user community. If you have a situation or affiliation that might constitute or lead to a conflict of interest or might be perceived by a reasonable person in the community to be a conflict of interest, disclose this to your Team Leaders or the team as a whole. If appropriate, after discussing with your team, you should remove yourself from specific decisions or discussions in which you may have a conflict of interest.
+## Conflict Resolution Team
+
+Should you wish to make a CoC violation report or are facing a conflict that has failed to be resolved in the manner set out in this CoC, please [click here](https://docs.google.com/forms/d/e/1FAIpQLSea_VL9NUvUM3iX-gptNuJe5oz5-jp9y5Y4v9ZyKImblfYTcQ/viewform) to access the report form.
+## The Fine Print
+
+Members of the board of Open Source Matters are governed by additional guidelines and requirements and, where a conflict exists, these take precedence over this Code of Conduct.
+## The Last Bit
+
+This Code of Conduct has changed over time and will continue to develop, but was originally derived, with permission, from the Ubuntu CoC.
+
+Last Updated 8th of June 2017
+Online version of this document is available at https://www.joomla.org/about-joomla/the-project/code-of-conduct.html
diff --git a/README.md b/README.md
index b0af7b464221d..f5f4ef48d15e1 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,17 @@
-Joomla! CMS™ [![Analytics](https://ga-beacon.appspot.com/UA-544070-3/joomla-cms/readme)](https://github.com/igrigorik/ga-beacon)
+Joomla! CMS™ [![Analytics](https://ga-beacon.appspot.com/UA-544070-3/joomla-cms/readme)](https://github.com/igrigorik/ga-beacon) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
====================
Build Status
---------------------
-| Travis-CI | Drone-CI | AppVeyor | Jenkins |
-| ------------- | ------------- | ------------- | ------------- |
-| [![Build Status](https://travis-ci.org/joomla/joomla-cms.svg?branch=staging)](https://travis-ci.org/joomla/joomla-cms) | [![Build Status](http://213.160.72.75/api/badges/joomla/joomla-cms/status.svg)](http://213.160.72.75/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/bpcxulw6nnxlv8kb/branch/staging?svg=true)](https://ci.appveyor.com/project/joomla/joomla-cms) | [![Build Status](http://build.joomla.org/job/cms/badge/icon)](http://build.joomla.org/job/cms/) |
+| Travis-CI | Drone-CI | AppVeyor |
+| ------------- | ------------- | ------------- |
+| [![Build Status](https://travis-ci.org/joomla/joomla-cms.svg?branch=staging)](https://travis-ci.org/joomla/joomla-cms) | [![Build Status](https://ci.joomla.org/api/badges/joomla/joomla-cms/status.svg)](https://ci.joomla.org/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/ru6sxal8jmfckvjc/branch/staging?svg=true)](https://ci.appveyor.com/project/release-joomla/joomla-cms) |
What is this?
---------------------
* This is a Joomla! 3.x installation/upgrade package.
* Joomla's [Official website](https://www.joomla.org).
-* Joomla! 3.8 [version history](https://docs.joomla.org/Special:MyLanguage/Joomla_3.8_version_history).
+* Joomla! 3.9 [version history](https://docs.joomla.org/Special:MyLanguage/Joomla_3.9_version_history).
* Detailed changes are in the [changelog](https://github.com/joomla/joomla-cms/commits/staging).
What is Joomla?
@@ -24,7 +24,7 @@ Is Joomla! for you?
---------------------
* Joomla! is [the right solution for most content web projects](https://docs.joomla.org/Special:MyLanguage/Portal:Learn_More).
* View Joomla's [core features here](https://www.joomla.org/core-features.html).
-* Try it out for yourself in our [online demo](https://demo.joomla.org).
+* Try it out for yourself on our [free hosting service](https://launch.joomla.org).
How to find a Joomla! translation?
---------------------
@@ -62,11 +62,11 @@ Updates are free!
Where can you get support and help?
---------------------
-* [The Joomla! Documentation](https://docs.joomla.org/Special:MyLanguage/Main_Page);
-* [Frequently Asked Questions](https://docs.joomla.org/Special:MyLanguage/Category:FAQ) (FAQ);
-* Find the [information you need](https://docs.joomla.org/Special:MyLanguage/Start_here);
-* Find [help and other users](https://www.joomla.org/about-joomla/create-and-share.html);
-* Post questions at [our forums](https://forum.joomla.org);
+* [The Joomla! Documentation](https://docs.joomla.org/Special:MyLanguage/Main_Page).
+* [Frequently Asked Questions](https://docs.joomla.org/Special:MyLanguage/Category:FAQ) (FAQ).
+* Find the [information you need](https://docs.joomla.org/Special:MyLanguage/Start_here).
+* Find [help and other users](https://www.joomla.org/about-joomla/create-and-share.html).
+* Post questions at [our forums](https://forum.joomla.org).
* [Joomla Resources Directory](https://resources.joomla.org) (JRD).
Do you already have a Joomla! site that isn't built with Joomla! 3.x?
@@ -86,7 +86,6 @@ Do you want to improve Joomla?
Copyright
---------------------
-* Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
-* [Special Thanks](https://docs.joomla.org/Special:MyLanguage/Joomla!_Credits_and_Thanks)
+* Copyright (C) 2005 - 2019 Open Source Matters. All rights reserved.
* Distributed under the GNU General Public License version 2 or later
* See [License details](https://docs.joomla.org/Special:MyLanguage/Joomla_Licenses)
diff --git a/README.txt b/README.txt
index 6d93aedcc1e61..bfe1f7663c870 100644
--- a/README.txt
+++ b/README.txt
@@ -1,7 +1,7 @@
1- What is this?
* This is a Joomla! installation/upgrade package to version 3.x
* Joomla! Official site: https://www.joomla.org
- * Joomla! 3.8 version history - https://docs.joomla.org/Special:MyLanguage/Joomla_3.8_version_history
+ * Joomla! 3.9 version history - https://docs.joomla.org/Special:MyLanguage/Joomla_3.9_version_history
* Detailed changes in the Changelog: https://github.com/joomla/joomla-cms/commits/staging
2- What is Joomla?
@@ -13,7 +13,7 @@
3- Is Joomla! for you?
* Joomla! is the right solution for most content web projects: https://docs.joomla.org/Special:MyLanguage/Portal:Learn_More
* See Joomla's core features - https://www.joomla.org/core-features.html
- * Try out our online demo: https://demo.joomla.org
+ * Try out our free hosting service: https://launch.joomla.org
4- How to find a Joomla! translation?
* Repository of accredited language packs: https://community.joomla.org/translations.html
@@ -66,7 +66,6 @@
* Documentation for Web designers: https://docs.joomla.org/Special:MyLanguage/Web_designers
Copyright:
- * Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
- * Special Thanks: https://docs.joomla.org/Special:MyLanguage/Joomla!_Credits_and_Thanks
+ * Copyright (C) 2005 - 2019 Open Source Matters. All rights reserved.
* Distributed under the GNU General Public License version 2 or later
- * See Licenses details at https://docs.joomla.org/Special:MyLanguage/Joomla_Licenses
+ * See License details at https://docs.joomla.org/Special:MyLanguage/Joomla_Licenses
diff --git a/RoboFile.php b/RoboFile.php
index e271464e187ec..f11cef3538462 100644
--- a/RoboFile.php
+++ b/RoboFile.php
@@ -3,7 +3,7 @@
* @package Joomla.Site
* @subpackage RoboFile
*
- * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
+ * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
diff --git a/administrator/components/com_actionlogs/actionlogs.php b/administrator/components/com_actionlogs/actionlogs.php
new file mode 100644
index 0000000000000..e5bd0b701ae6d
--- /dev/null
+++ b/administrator/components/com_actionlogs/actionlogs.php
@@ -0,0 +1,24 @@
+authorise('core.admin'))
+{
+ throw new NotAllowed(Text::_('JERROR_ALERTNOAUTHOR'), 403);
+}
+
+$controller = BaseController::getInstance('Actionlogs');
+$controller->execute(Factory::getApplication()->input->get('task'));
+$controller->redirect();
diff --git a/administrator/components/com_actionlogs/actionlogs.xml b/administrator/components/com_actionlogs/actionlogs.xml
new file mode 100644
index 0000000000000..19be5ab5d0722
--- /dev/null
+++ b/administrator/components/com_actionlogs/actionlogs.xml
@@ -0,0 +1,29 @@
+
+
+ com_actionlogs
+ Joomla! Project
+ May 2018
+ Copyright (C) 2005 - 2019 Open Source Matters. All rights reserved.
+ GNU General Public License version 2 or later; see LICENSE.txt
+ admin@joomla.org
+ www.joomla.org
+ 3.9.0
+ COM_ACTIONLOGS_XML_DESCRIPTION
+
+
+
+ actionlogs.php
+ config.xml
+ access.xml
+ controller.php
+ controllers
+ helpers
+ models
+ views
+
+
+ language/en-GB.com_actionlogs.ini
+ language/en-GB.com_actionlogs.sys.ini
+
+
+
diff --git a/administrator/components/com_actionlogs/config.xml b/administrator/components/com_actionlogs/config.xml
new file mode 100644
index 0000000000000..ce40015c59884
--- /dev/null
+++ b/administrator/components/com_actionlogs/config.xml
@@ -0,0 +1,35 @@
+
+
+
+
diff --git a/administrator/components/com_actionlogs/controller.php b/administrator/components/com_actionlogs/controller.php
new file mode 100644
index 0000000000000..bf51ea71444c1
--- /dev/null
+++ b/administrator/components/com_actionlogs/controller.php
@@ -0,0 +1,19 @@
+registerTask('exportSelectedLogs', 'exportLogs');
+ }
+
+ /**
+ * Method to get a model object, loading it if required.
+ *
+ * @param string $name The model name. Optional.
+ * @param string $prefix The class prefix. Optional.
+ * @param array $config Configuration array for model. Optional.
+ *
+ * @return object The model.
+ *
+ * @since 3.9.0
+ */
+ public function getModel($name = 'Actionlogs', $prefix = 'ActionlogsModel', $config = array('ignore_request' => true))
+ {
+ // Return the model
+ return parent::getModel($name, $prefix, $config);
+ }
+
+ /**
+ * Method to export logs
+ *
+ * @return void
+ *
+ * @since 3.9.0
+ */
+ public function exportLogs()
+ {
+ // Check for request forgeries.
+ $this->checkToken();
+
+ $task = $this->getTask();
+
+ $pks = array();
+
+ if ($task == 'exportSelectedLogs')
+ {
+ // Get selected logs
+ $pks = ArrayHelper::toInteger(explode(',', $this->input->post->getString('cids')));
+ }
+
+ /** @var ActionlogsModelActionlogs $model */
+ $model = $this->getModel();
+
+ // Get the logs data
+ $data = $model->getLogDataAsIterator($pks);
+
+ if (count($data))
+ {
+
+ try
+ {
+ $rows = ActionlogsHelper::getCsvData($data);
+ }
+ catch (InvalidArgumentException $exception)
+ {
+ $this->setMessage(Text::_('COM_ACTIONLOGS_ERROR_COULD_NOT_EXPORT_DATA'), 'error');
+ $this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false));
+
+ return;
+ }
+
+ // Destroy the iterator now
+ unset($data);
+
+ $date = new Date('now', new DateTimeZone('UTC'));
+ $filename = 'logs_' . $date->format('Y-m-d_His_T');
+
+ $csvDelimiter = ComponentHelper::getComponent('com_actionlogs')->getParams()->get('csv_delimiter', ',');
+
+ $app = Factory::getApplication();
+ $app->setHeader('Content-Type', 'application/csv', true)
+ ->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '.csv"', true)
+ ->setHeader('Cache-Control', 'must-revalidate', true)
+ ->sendHeaders();
+
+ $output = fopen("php://output", "w");
+
+ foreach ($rows as $row)
+ {
+ fputcsv($output, $row, $csvDelimiter);
+ }
+
+ fclose($output);
+ $app->triggerEvent('onAfterLogExport', array());
+ $app->close();
+ }
+ else
+ {
+ $this->setMessage(Text::_('COM_ACTIONLOGS_NO_LOGS_TO_EXPORT'));
+ $this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false));
+ }
+ }
+
+ /**
+ * Clean out the logs
+ *
+ * @return void
+ *
+ * @since 3.9.0
+ */
+ public function purge()
+ {
+ // Check for request forgeries.
+ $this->checkToken();
+
+ $model = $this->getModel();
+
+ if ($model->purge())
+ {
+ $message = Text::_('COM_ACTIONLOGS_PURGE_SUCCESS');
+ }
+ else
+ {
+ $message = Text::_('COM_ACTIONLOGS_PURGE_FAIL');
+ }
+
+ $this->setRedirect(Route::_('index.php?option=com_actionlogs&view=actionlogs', false), $message);
+ }
+}
diff --git a/administrator/components/com_actionlogs/helpers/actionlogs.php b/administrator/components/com_actionlogs/helpers/actionlogs.php
new file mode 100644
index 0000000000000..7a03bea2ec9f8
--- /dev/null
+++ b/administrator/components/com_actionlogs/helpers/actionlogs.php
@@ -0,0 +1,376 @@
+='))
+ {
+ // Only include the PHP 5.5 helper in this conditional to prevent the potential of parse errors for PHP 5.4 or earlier
+ JLoader::register('ActionlogsHelperPhp55', __DIR__ . '/actionlogsphp55.php');
+
+ return ActionlogsHelperPhp55::getCsvAsGenerator($data);
+ }
+
+ $disabledText = Text::_('COM_ACTIONLOGS_DISABLED');
+
+ $rows = array();
+
+ // Header row
+ $rows[] = array('Id', 'Message', 'Date', 'Extension', 'User', 'Ip');
+
+ foreach ($data as $log)
+ {
+ $date = new Date($log->log_date, new DateTimeZone('UTC'));
+ $extension = strtok($log->extension, '.');
+
+ static::loadTranslationFiles($extension);
+
+ $rows[] = array(
+ 'id' => $log->id,
+ 'message' => self::escapeCsvFormula(strip_tags(static::getHumanReadableLogMessage($log, false))),
+ 'date' => $date->format('Y-m-d H:i:s T'),
+ 'extension' => self::escapeCsvFormula(Text::_($extension)),
+ 'name' => self::escapeCsvFormula($log->name),
+ 'ip_address' => self::escapeCsvFormula($log->ip_address === 'COM_ACTIONLOGS_DISABLED' ? $disabledText : $log->ip_address)
+ );
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Load the translation files for an extension
+ *
+ * @param string $extension Extension name
+ *
+ * @return void
+ *
+ * @since 3.9.0
+ */
+ public static function loadTranslationFiles($extension)
+ {
+ static $cache = array();
+ $extension = strtolower($extension);
+
+ if (isset($cache[$extension]))
+ {
+ return;
+ }
+
+ $lang = Factory::getLanguage();
+ $source = '';
+
+ switch (substr($extension, 0, 3))
+ {
+ case 'com':
+ default:
+ $source = JPATH_ADMINISTRATOR . '/components/' . $extension;
+ break;
+
+ case 'lib':
+ $source = JPATH_LIBRARIES . '/' . substr($extension, 4);
+ break;
+
+ case 'mod':
+ $source = JPATH_SITE . '/modules/' . $extension;
+ break;
+
+ case 'plg':
+ $parts = explode('_', $extension, 3);
+
+ if (count($parts) > 2)
+ {
+ $source = JPATH_PLUGINS . '/' . $parts[1] . '/' . $parts[2];
+ }
+ break;
+
+ case 'pkg':
+ $source = JPATH_SITE;
+ break;
+
+ case 'tpl':
+ $source = JPATH_BASE . '/templates/' . substr($extension, 4);
+ break;
+
+ }
+
+ $lang->load($extension, JPATH_ADMINISTRATOR, null, false, true)
+ || $lang->load($extension, $source, null, false, true);
+
+ if (!$lang->hasKey(strtoupper($extension)))
+ {
+ $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, true)
+ || $lang->load($extension . '.sys', $source, null, false, true);
+ }
+
+ $cache[$extension] = true;
+ }
+
+ /**
+ * Get parameters to be
+ *
+ * @param string $context The context of the content
+ *
+ * @return mixed An object contains content type parameters, or null if not found
+ *
+ * @since 3.9.0
+ */
+ public static function getLogContentTypeParams($context)
+ {
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true)
+ ->select('a.*')
+ ->from($db->quoteName('#__action_log_config', 'a'))
+ ->where($db->quoteName('a.type_alias') . ' = ' . $db->quote($context));
+
+ $db->setQuery($query);
+
+ return $db->loadObject();
+ }
+
+ /**
+ * Get human readable log message for a User Action Log
+ *
+ * @param stdClass $log A User Action log message record
+ * @param boolean $generateLinks Flag to disable link generation when creating a message
+ *
+ * @return string
+ *
+ * @since 3.9.0
+ */
+ public static function getHumanReadableLogMessage($log, $generateLinks = true)
+ {
+ static $links = array();
+
+ $message = Text::_($log->message_language_key);
+ $messageData = json_decode($log->message, true);
+
+ // Special handling for translation extension name
+ if (isset($messageData['extension_name']))
+ {
+ static::loadTranslationFiles($messageData['extension_name']);
+ $messageData['extension_name'] = Text::_($messageData['extension_name']);
+ }
+
+ // Translating application
+ if (isset($messageData['app']))
+ {
+ $messageData['app'] = Text::_($messageData['app']);
+ }
+
+ // Translating type
+ if (isset($messageData['type']))
+ {
+ $messageData['type'] = Text::_($messageData['type']);
+ }
+
+ $linkMode = Factory::getApplication()->get('force_ssl', 0) >= 1 ? Route::TLS_FORCE : Route::TLS_IGNORE;
+
+ foreach ($messageData as $key => $value)
+ {
+ // Convert relative url to absolute url so that it is clickable in action logs notification email
+ if ($generateLinks && StringHelper::strpos($value, 'index.php?') === 0)
+ {
+ if (!isset($links[$value]))
+ {
+ $links[$value] = Route::link('administrator', $value, false, $linkMode);
+ }
+
+ $value = $links[$value];
+ }
+
+ $message = str_replace('{' . $key . '}', $value, $message);
+ }
+
+ return $message;
+ }
+
+ /**
+ * Get link to an item of given content type
+ *
+ * @param string $component
+ * @param string $contentType
+ * @param integer $id
+ * @param string $urlVar
+ *
+ * @return string Link to the content item
+ *
+ * @since 3.9.0
+ */
+ public static function getContentTypeLink($component, $contentType, $id, $urlVar = 'id')
+ {
+ // Try to find the component helper.
+ $eName = str_replace('com_', '', $component);
+ $file = Path::clean(JPATH_ADMINISTRATOR . '/components/' . $component . '/helpers/' . $eName . '.php');
+
+ if (file_exists($file))
+ {
+ $prefix = ucfirst(str_replace('com_', '', $component));
+ $cName = $prefix . 'Helper';
+
+ JLoader::register($cName, $file);
+
+ if (class_exists($cName) && is_callable(array($cName, 'getContentTypeLink')))
+ {
+ return $cName::getContentTypeLink($contentType, $id);
+ }
+ }
+
+ if (empty($urlVar))
+ {
+ $urlVar = 'id';
+ }
+
+ // Return default link to avoid having to implement getContentTypeLink in most of our components
+ return 'index.php?option=' . $component . '&task=' . $contentType . '.edit&' . $urlVar . '=' . $id;
+ }
+
+ /**
+ * Load both enabled and disabled actionlog plugins language file.
+ *
+ * It is used to make sure actions log is displayed properly instead of only language items displayed when a plugin is disabled.
+ *
+ * @return void
+ *
+ * @since 3.9.0
+ */
+ public static function loadActionLogPluginsLanguage()
+ {
+ $lang = Factory::getLanguage();
+ $db = Factory::getDbo();
+
+ // Get all (both enabled and disabled) actionlog plugins
+ $query = $db->getQuery(true)
+ ->select(
+ $db->quoteName(
+ array(
+ 'folder',
+ 'element',
+ 'params',
+ 'extension_id'
+ ),
+ array(
+ 'type',
+ 'name',
+ 'params',
+ 'id'
+ )
+ )
+ )
+ ->from('#__extensions')
+ ->where('type = ' . $db->quote('plugin'))
+ ->where('folder = ' . $db->quote('actionlog'))
+ ->where('state IN (0,1)')
+ ->order('ordering');
+ $db->setQuery($query);
+
+ try
+ {
+ $rows = $db->loadObjectList();
+ }
+ catch (RuntimeException $e)
+ {
+ $rows = array();
+ }
+
+ if (empty($rows))
+ {
+ return;
+ }
+
+ foreach ($rows as $row)
+ {
+ $name = $row->name;
+ $type = $row->type;
+ $extension = 'Plg_' . $type . '_' . $name;
+ $extension = strtolower($extension);
+
+ // If language already loaded, don't load it again.
+ if ($lang->getPaths($extension))
+ {
+ continue;
+ }
+
+ $lang->load($extension, JPATH_ADMINISTRATOR, null, false, true)
+ || $lang->load($extension, JPATH_PLUGINS . '/' . $type . '/' . $name, null, false, true);
+ }
+
+ // Load com_privacy too.
+ $lang->load('com_privacy', JPATH_ADMINISTRATOR, null, false, true);
+ }
+
+ /**
+ * Escapes potential characters that start a formula in a CSV value to prevent injection attacks
+ *
+ * @param mixed $value csv field value
+ *
+ * @return mixed
+ *
+ * @since 3.9.7
+ */
+ protected static function escapeCsvFormula($value)
+ {
+ if ($value == '')
+ {
+ return $value;
+ }
+
+ if (in_array($value[0], self::$characters, true))
+ {
+ $value = ' ' . $value;
+ }
+
+ return $value;
+ }
+}
diff --git a/administrator/components/com_actionlogs/helpers/actionlogsphp55.php b/administrator/components/com_actionlogs/helpers/actionlogsphp55.php
new file mode 100644
index 0000000000000..9cca5ee5ff97b
--- /dev/null
+++ b/administrator/components/com_actionlogs/helpers/actionlogsphp55.php
@@ -0,0 +1,102 @@
+extension, '.');
+
+ ActionlogsHelper::loadTranslationFiles($extension);
+
+ yield array(
+ 'id' => $log->id,
+ 'message' => self::escapeCsvFormula(strip_tags(ActionlogsHelper::getHumanReadableLogMessage($log, false))),
+ 'date' => (new Date($log->log_date, new DateTimeZone('UTC')))->format('Y-m-d H:i:s T'),
+ 'extension' => self::escapeCsvFormula(Text::_($extension)),
+ 'name' => self::escapeCsvFormula($log->name),
+ 'ip_address' => self::escapeCsvFormula($log->ip_address === 'COM_ACTIONLOGS_DISABLED' ? $disabledText : $log->ip_address)
+ );
+ }
+ }
+
+ /**
+ * Escapes potential characters that start a formula in a CSV value to prevent injection attacks
+ *
+ * @param mixed $value csv field value
+ *
+ * @return mixed
+ *
+ * @since 3.9.7
+ */
+ protected static function escapeCsvFormula($value)
+ {
+ if ($value == '')
+ {
+ return $value;
+ }
+
+ if (in_array($value[0], self::$characters, true))
+ {
+ $value = ' ' . $value;
+ }
+
+ return $value;
+ }
+}
diff --git a/administrator/components/com_actionlogs/layouts/logstable.php b/administrator/components/com_actionlogs/layouts/logstable.php
new file mode 100644
index 0000000000000..20cbc2133ae26
--- /dev/null
+++ b/administrator/components/com_actionlogs/layouts/logstable.php
@@ -0,0 +1,50 @@
+load("com_actionlogs", JPATH_ADMINISTRATOR, null, false, true);
+
+$messages = $displayData['messages'];
+$showIpColumn = $displayData['showIpColumn'];
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
message; ?>
+
log_date, 'Y-m-d H:i:s T', 'UTC'); ?>
+
extension; ?>
+
+
+
ip_address); ?>
+
+
+
+
+
diff --git a/administrator/components/com_actionlogs/libraries/actionlogplugin.php b/administrator/components/com_actionlogs/libraries/actionlogplugin.php
new file mode 100644
index 0000000000000..3c0e0d1b6694b
--- /dev/null
+++ b/administrator/components/com_actionlogs/libraries/actionlogplugin.php
@@ -0,0 +1,99 @@
+ $message)
+ {
+ if (!array_key_exists('userid', $message))
+ {
+ $message['userid'] = $user->id;
+ }
+
+ if (!array_key_exists('username', $message))
+ {
+ $message['username'] = $user->username;
+ }
+
+ if (!array_key_exists('accountlink', $message))
+ {
+ $message['accountlink'] = 'index.php?option=com_users&task=user.edit&id=' . $user->id;
+ }
+
+ if (array_key_exists('type', $message))
+ {
+ $message['type'] = strtoupper($message['type']);
+ }
+
+ if (array_key_exists('app', $message))
+ {
+ $message['app'] = strtoupper($message['app']);
+ }
+
+ $messages[$index] = $message;
+ }
+
+ /** @var ActionlogsModelActionlog $model **/
+ $model = BaseDatabaseModel::getInstance('Actionlog', 'ActionlogsModel');
+ $model->addLog($messages, strtoupper($messageLanguageKey), $context, $userId);
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/actionlog.php b/administrator/components/com_actionlogs/models/actionlog.php
new file mode 100644
index 0000000000000..92fecacb3e3a0
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/actionlog.php
@@ -0,0 +1,175 @@
+getDbo();
+ $date = Factory::getDate();
+ $params = ComponentHelper::getComponent('com_actionlogs')->getParams();
+
+ if ($params->get('ip_logging', 0))
+ {
+ $ip = IpHelper::getIp();
+
+ if (!filter_var($ip, FILTER_VALIDATE_IP))
+ {
+ $ip = 'COM_ACTIONLOGS_IP_INVALID';
+ }
+ }
+ else
+ {
+ $ip = 'COM_ACTIONLOGS_DISABLED';
+ }
+
+ $loggedMessages = array();
+
+ foreach ($messages as $message)
+ {
+ $logMessage = new stdClass;
+ $logMessage->message_language_key = $messageLanguageKey;
+ $logMessage->message = json_encode($message);
+ $logMessage->log_date = (string) $date;
+ $logMessage->extension = $context;
+ $logMessage->user_id = $user->id;
+ $logMessage->ip_address = $ip;
+ $logMessage->item_id = isset($message['id']) ? (int) $message['id'] : 0;
+
+ try
+ {
+ $db->insertObject('#__action_logs', $logMessage);
+ $loggedMessages[] = $logMessage;
+ }
+ catch (RuntimeException $e)
+ {
+ // Ignore it
+ }
+ }
+
+ // Send notification email to users who choose to be notified about the action logs
+ $this->sendNotificationEmails($loggedMessages, $user->name, $context);
+ }
+
+ /**
+ * Send notification emails about the action log
+ *
+ * @param array $messages The logged messages
+ * @param string $username The username
+ * @param string $context The Context
+ *
+ * @return void
+ *
+ * @since 3.9.0
+ */
+ protected function sendNotificationEmails($messages, $username, $context)
+ {
+ $db = $this->getDbo();
+ $query = $db->getQuery(true);
+ $params = ComponentHelper::getParams('com_actionlogs');
+ $showIpColumn = (bool) $params->get('ip_logging', 0);
+
+ $query
+ ->select($db->quoteName(array('u.email', 'l.extensions')))
+ ->from($db->quoteName('#__users', 'u'))
+ ->join(
+ 'INNER',
+ $db->quoteName('#__action_logs_users', 'l') . ' ON ( ' . $db->quoteName('l.notify') . ' = 1 AND '
+ . $db->quoteName('l.user_id') . ' = ' . $db->quoteName('u.id') . ')'
+ );
+
+ $db->setQuery($query);
+
+ try
+ {
+ $users = $db->loadObjectList();
+ }
+ catch (RuntimeException $e)
+ {
+ JError::raiseWarning(500, $e->getMessage());
+
+ return;
+ }
+
+ $recipients = array();
+
+ foreach ($users as $user)
+ {
+ $extensions = json_decode($user->extensions, true);
+
+ if ($extensions && in_array(strtok($context, '.'), $extensions))
+ {
+ $recipients[] = $user->email;
+ }
+ }
+
+ if (empty($recipients))
+ {
+ return;
+ }
+
+ $layout = new FileLayout('components.com_actionlogs.layouts.logstable', JPATH_ADMINISTRATOR);
+ $extension = strtok($context, '.');
+ ActionlogsHelper::loadTranslationFiles($extension);
+
+ foreach ($messages as $message)
+ {
+ $message->extension = Text::_($extension);
+ $message->message = ActionlogsHelper::getHumanReadableLogMessage($message);
+ }
+
+ $displayData = array(
+ 'messages' => $messages,
+ 'username' => $username,
+ 'showIpColumn' => $showIpColumn,
+ );
+
+ $body = $layout->render($displayData);
+ $mailer = Factory::getMailer();
+ $mailer->addRecipient($recipients);
+ $mailer->setSubject(Text::_('COM_ACTIONLOGS_EMAIL_SUBJECT'));
+ $mailer->isHTML(true);
+ $mailer->Encoding = 'base64';
+ $mailer->setBody($body);
+
+ if (!$mailer->Send())
+ {
+ JError::raiseWarning(500, Text::_('JERROR_SENDING_EMAIL'));
+ }
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/actionlogs.php b/administrator/components/com_actionlogs/models/actionlogs.php
new file mode 100644
index 0000000000000..345352ceba72b
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/actionlogs.php
@@ -0,0 +1,396 @@
+getUserStateFromRequest($this->context . 'filter.search', 'filter_search', '', 'string');
+ $this->setState('filter.search', $search);
+
+ $user = $app->getUserStateFromRequest($this->context . 'filter.user', 'filter_user', '', 'string');
+ $this->setState('filter.user', $user);
+
+ $extension = $app->getUserStateFromRequest($this->context . 'filter.extension', 'filter_extension', '', 'string');
+ $this->setState('filter.extension', $extension);
+
+ $ip_address = $app->getUserStateFromRequest($this->context . 'filter.ip_address', 'filter_ip_address', '', 'string');
+ $this->setState('filter.ip_address', $ip_address);
+
+ $dateRange = $app->getUserStateFromRequest($this->context . 'filter.dateRange', 'filter_dateRange', '', 'string');
+ $this->setState('filter.dateRange', $dateRange);
+
+ parent::populateState($ordering, $direction);
+ }
+
+ /**
+ * Build an SQL query to load the list data.
+ *
+ * @return JDatabaseQuery
+ *
+ * @since 3.9.0
+ */
+ protected function getListQuery()
+ {
+ $db = $this->getDbo();
+ $query = $db->getQuery(true)
+ ->select('a.*, u.name')
+ ->from('#__action_logs AS a')
+ ->leftJoin('#__users AS u ON a.user_id = u.id');
+
+ // Get ordering
+ $fullorderCol = $this->state->get('list.fullordering', 'a.id DESC');
+
+ // Apply ordering
+ if (!empty($fullorderCol))
+ {
+ $query->order($db->escape($fullorderCol));
+ }
+
+ // Get filter by user
+ $user = $this->getState('filter.user');
+
+ // Apply filter by user
+ if (!empty($user))
+ {
+ $query->where($db->quoteName('a.user_id') . ' = ' . (int) $user);
+ }
+
+ // Get filter by extension
+ $extension = $this->getState('filter.extension');
+
+ // Apply filter by extension
+ if (!empty($extension))
+ {
+ $query->where($db->quoteName('a.extension') . ' LIKE ' . $db->quote($extension . '%'));
+ }
+
+ // Get filter by date range
+ $dateRange = $this->getState('filter.dateRange');
+
+ // Apply filter by date range
+ if (!empty($dateRange))
+ {
+ $date = $this->buildDateRange($dateRange);
+
+ // If the chosen range is not more than a year ago
+ if ($date['dNow'] != false)
+ {
+ $query->where(
+ $db->qn('a.log_date') . ' >= ' . $db->quote($date['dStart']->format('Y-m-d H:i:s')) .
+ ' AND ' . $db->qn('a.log_date') . ' <= ' . $db->quote($date['dNow']->format('Y-m-d H:i:s'))
+ );
+ }
+ }
+
+ // Filter the items over the search string if set.
+ $search = $this->getState('filter.search');
+
+ if (!empty($search))
+ {
+ if (stripos($search, 'id:') === 0)
+ {
+ $query->where($db->quoteName('a.id') . ' = ' . (int) substr($search, 3));
+ }
+ elseif (stripos($search, 'item_id:') === 0)
+ {
+ $query->where($db->quoteName('a.item_id') . ' = ' . (int) substr($search, 8));
+ }
+ else
+ {
+ $search = $db->quote('%' . $db->escape($search, true) . '%');
+ $query->where('(' . $db->quoteName('u.username') . ' LIKE ' . $search . ')');
+ }
+ }
+
+ return $query;
+ }
+
+ /**
+ * Construct the date range to filter on.
+ *
+ * @param string $range The textual range to construct the filter for.
+ *
+ * @return array The date range to filter on.
+ *
+ * @since 3.9.0
+ */
+ private function buildDateRange($range)
+ {
+ // Get UTC for now.
+ $dNow = new Date;
+ $dStart = clone $dNow;
+
+ switch ($range)
+ {
+ case 'past_week':
+ $dStart->modify('-7 day');
+ break;
+
+ case 'past_1month':
+ $dStart->modify('-1 month');
+ break;
+
+ case 'past_3month':
+ $dStart->modify('-3 month');
+ break;
+
+ case 'past_6month':
+ $dStart->modify('-6 month');
+ break;
+
+ case 'past_year':
+ $dStart->modify('-1 year');
+ break;
+
+ case 'today':
+ // Ranges that need to align with local 'days' need special treatment.
+ $offset = Factory::getApplication()->get('offset');
+
+ // Reset the start time to be the beginning of today, local time.
+ $dStart = new Date('now', $offset);
+ $dStart->setTime(0, 0, 0);
+
+ // Now change the timezone back to UTC.
+ $tz = new DateTimeZone('GMT');
+ $dStart->setTimezone($tz);
+ break;
+ }
+
+ return array('dNow' => $dNow, 'dStart' => $dStart);
+ }
+
+ /**
+ * Get all log entries for an item
+ *
+ * @param string $extension The extension the item belongs to
+ * @param integer $itemId The item ID
+ *
+ * @return array
+ *
+ * @since 3.9.0
+ */
+ public function getLogsForItem($extension, $itemId)
+ {
+ $db = $this->getDbo();
+ $query = $db->getQuery(true)
+ ->select('a.*, u.name')
+ ->from('#__action_logs AS a')
+ ->innerJoin('#__users AS u ON a.user_id = u.id')
+ ->where($db->quoteName('a.extension') . ' = ' . $db->quote($extension))
+ ->where($db->quoteName('a.item_id') . ' = ' . (int) $itemId);
+
+ // Get ordering
+ $fullorderCol = $this->getState('list.fullordering', 'a.id DESC');
+
+ // Apply ordering
+ if (!empty($fullorderCol))
+ {
+ $query->order($db->escape($fullorderCol));
+ }
+
+ $db->setQuery($query);
+
+ return $db->loadObjectList();
+ }
+
+ /**
+ * Get logs data into JTable object
+ *
+ * @param integer[]|null $pks An optional array of log record IDs to load
+ *
+ * @return array All logs in the table
+ *
+ * @since 3.9.0
+ */
+ public function getLogsData($pks = null)
+ {
+ $db = $this->getDbo();
+ $query = $this->getLogDataQuery($pks);
+
+ $db->setQuery($query);
+
+ return $db->loadObjectList();
+ }
+
+ /**
+ * Get logs data as a database iterator
+ *
+ * @param integer[]|null $pks An optional array of log record IDs to load
+ *
+ * @return JDatabaseIterator
+ *
+ * @since 3.9.0
+ */
+ public function getLogDataAsIterator($pks = null)
+ {
+ $db = $this->getDbo();
+ $query = $this->getLogDataQuery($pks);
+
+ $db->setQuery($query);
+
+ return $db->getIterator();
+ }
+
+ /**
+ * Get the query for loading logs data
+ *
+ * @param integer[]|null $pks An optional array of log record IDs to load
+ *
+ * @return JDatabaseQuery
+ *
+ * @since 3.9.0
+ */
+ private function getLogDataQuery($pks = null)
+ {
+ $db = $this->getDbo();
+ $query = $db->getQuery(true)
+ ->select('a.*, u.name')
+ ->from('#__action_logs AS a')
+ ->innerJoin('#__users AS u ON a.user_id = u.id');
+
+ if (is_array($pks) && count($pks) > 0)
+ {
+ $query->where($db->quoteName('a.id') . ' IN (' . implode(',', ArrayHelper::toInteger($pks)) . ')');
+ }
+
+ return $query;
+ }
+
+ /**
+ * Delete logs
+ *
+ * @param array $pks Primary keys of logs
+ *
+ * @return boolean
+ *
+ * @since 3.9.0
+ */
+ public function delete(&$pks)
+ {
+ $db = $this->getDbo();
+ $query = $db->getQuery(true)
+ ->delete($db->quoteName('#__action_logs'))
+ ->where($db->quoteName('id') . ' IN (' . implode(',', ArrayHelper::toInteger($pks)) . ')');
+ $db->setQuery($query);
+
+ try
+ {
+ $db->execute();
+ }
+ catch (RuntimeException $e)
+ {
+ $this->setError($e->getMessage());
+
+ return false;
+ }
+
+ Factory::getApplication()->triggerEvent('onAfterLogPurge', array());
+
+ return true;
+ }
+
+ /**
+ * Removes all of logs from the table.
+ *
+ * @return boolean result of operation
+ *
+ * @since 3.9.0
+ */
+ public function purge()
+ {
+ try
+ {
+ $this->getDbo()->truncateTable('#__action_logs');
+ }
+ catch (Exception $e)
+ {
+ return false;
+ }
+
+ Factory::getApplication()->triggerEvent('onAfterLogPurge', array());
+
+ return true;
+ }
+
+ /**
+ * Get the filter form
+ *
+ * @param array $data data
+ * @param boolean $loadData load current data
+ *
+ * @return \JForm|boolean The \JForm object or false on error
+ *
+ * @since 3.9.0
+ */
+ public function getFilterForm($data = array(), $loadData = true)
+ {
+ $form = parent::getFilterForm($data, $loadData);
+ $params = ComponentHelper::getParams('com_actionlogs');
+ $ipLogging = (bool) $params->get('ip_logging', 0);
+
+ // Add ip sort options to sort dropdown
+ if ($form && $ipLogging)
+ {
+ /* @var JFormFieldList $field */
+ $field = $form->getField('fullordering', 'list');
+ $field->addOption(Text::_('COM_ACTIONLOGS_IP_ADDRESS_ASC'), array('value' => 'a.ip_address ASC'));
+ $field->addOption(Text::_('COM_ACTIONLOGS_IP_ADDRESS_DESC'), array('value' => 'a.ip_address DESC'));
+ }
+
+ return $form;
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/fields/extension.php b/administrator/components/com_actionlogs/models/fields/extension.php
new file mode 100644
index 0000000000000..a26057ba665af
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/fields/extension.php
@@ -0,0 +1,73 @@
+getQuery(true)
+ ->select('DISTINCT ' . $db->quoteName('extension'))
+ ->from($db->quoteName('#__action_logs'))
+ ->order($db->quoteName('extension'));
+
+ $db->setQuery($query);
+ $context = $db->loadColumn();
+
+ $options = array();
+
+ if (count($context) > 0)
+ {
+ foreach ($context as $item)
+ {
+ $extensions[] = strtok($item, '.');
+ }
+
+ $extensions = array_unique($extensions);
+
+ foreach ($extensions as $extension)
+ {
+ ActionlogsHelper::loadTranslationFiles($extension);
+ $options[] = HTMLHelper::_('select.option', $extension, Text::_($extension));
+ }
+ }
+
+ return array_merge(parent::getOptions(), $options);
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/fields/logcreator.php b/administrator/components/com_actionlogs/models/fields/logcreator.php
new file mode 100644
index 0000000000000..3668c1d62b32e
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/fields/logcreator.php
@@ -0,0 +1,82 @@
+element);
+
+ if (!isset(static::$options[$hash]))
+ {
+ static::$options[$hash] = parent::getOptions();
+
+ $options = array();
+
+ $db = Factory::getDbo();
+
+ // Construct the query
+ $query = $db->getQuery(true)
+ ->select($db->quoteName('u.id', 'value'))
+ ->select($db->quoteName('u.username', 'text'))
+ ->from($db->quoteName('#__users', 'u'))
+ ->join('INNER', $db->quoteName('#__action_logs', 'c') . ' ON ' . $db->quoteName('c.user_id') . ' = ' . $db->quoteName('u.id'))
+ ->group($db->quoteName('u.id'))
+ ->group($db->quoteName('u.username'))
+ ->order($db->quoteName('u.username'));
+
+ // Setup the query
+ $db->setQuery($query);
+
+ // Return the result
+ if ($options = $db->loadObjectList())
+ {
+ static::$options[$hash] = array_merge(static::$options[$hash], $options);
+ }
+ }
+
+ return static::$options[$hash];
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/fields/logsdaterange.php b/administrator/components/com_actionlogs/models/fields/logsdaterange.php
new file mode 100644
index 0000000000000..346858c256f11
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/fields/logsdaterange.php
@@ -0,0 +1,62 @@
+ 'COM_ACTIONLOGS_OPTION_RANGE_TODAY',
+ 'past_week' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_WEEK',
+ 'past_1month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_1MONTH',
+ 'past_3month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_3MONTH',
+ 'past_6month' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_6MONTH',
+ 'past_year' => 'COM_ACTIONLOGS_OPTION_RANGE_PAST_YEAR',
+ );
+
+ /**
+ * Method to instantiate the form field object.
+ *
+ * @param JForm $form The form to attach to the form field object.
+ *
+ * @since 3.9.0
+ */
+ public function __construct($form = null)
+ {
+ parent::__construct($form);
+
+ // Load the required language
+ $lang = Factory::getLanguage();
+ $lang->load('com_actionlogs', JPATH_ADMINISTRATOR);
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/fields/logtype.php b/administrator/components/com_actionlogs/models/fields/logtype.php
new file mode 100644
index 0000000000000..1634648afb9ef
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/fields/logtype.php
@@ -0,0 +1,66 @@
+getQuery(true)
+ ->select($db->quoteName('extension'))
+ ->from($db->quoteName('#__action_logs_extensions'));
+
+ $extensions = $db->setQuery($query)->loadColumn();
+
+ $options = array();
+ $tmp = array('checked' => true);
+
+ foreach ($extensions as $extension)
+ {
+ ActionlogsHelper::loadTranslationFiles($extension);
+ $option = HTMLHelper::_('select.option', $extension, Text::_($extension));
+ $options[ApplicationHelper::stringURLSafe(Text::_($extension)) . '_' . $extension] = (object) array_merge($tmp, (array) $option);
+ }
+
+ ksort($options);
+
+ return array_merge(parent::getOptions(), array_values($options));
+ }
+}
diff --git a/administrator/components/com_actionlogs/models/fields/plugininfo.php b/administrator/components/com_actionlogs/models/fields/plugininfo.php
new file mode 100644
index 0000000000000..a9ebb6550ca05
--- /dev/null
+++ b/administrator/components/com_actionlogs/models/fields/plugininfo.php
@@ -0,0 +1,65 @@
+getQuery(true)
+ ->select($db->quoteName('extension_id'))
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('folder') . ' = ' . $db->quote('actionlog'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote('joomla'));
+ $db->setQuery($query);
+
+ try
+ {
+ $result = (int) $db->loadResult();
+ }
+ catch (RuntimeException $e)
+ {
+ JError::raiseWarning(500, $e->getMessage());
+ }
+
+ $link = JHtml::_(
+ 'link',
+ JRoute::_('index.php?option=com_plugins&task=plugin.edit&extension_id=' . $result),
+ JText::_('PLG_SYSTEM_ACTIONLOGS_JOOMLA_ACTIONLOG_DISABLED'),
+ array('class' => 'alert-link')
+ );
+
+ return '