diff --git a/.build-rsync-exclude b/.build-rsync-exclude new file mode 100644 index 0000000..7f0ff11 --- /dev/null +++ b/.build-rsync-exclude @@ -0,0 +1,24 @@ +.git +.gitignore +.github/ +.cursor/ +.DS_Store +.phpunit.result* +.build-rsync-exclude +.jshintrc +.idea/ +node_modules/ +vendor/ +bin/ +tests/ +coverage/ +ruleset.xml +phpunit.xml +composer.json +composer.lock +package.json +package-lock.json +Gruntfile.js +README.md +aryo-activity-log/ +aryo-activity-log.*.zip diff --git a/.cursor/rules/run-tests-before-finish.mdc b/.cursor/rules/run-tests-before-finish.mdc new file mode 100644 index 0000000..ca5d0cb --- /dev/null +++ b/.cursor/rules/run-tests-before-finish.mdc @@ -0,0 +1,15 @@ +--- +description: Always run PHPUnit before considering a coding task finished +alwaysApply: true +--- + +# Run tests before finishing + +Before declaring any coding task complete (including refactors, bug fixes, and new features): + +1. Ensure the WordPress test suite is available (`WP_TESTS_DIR` / `bin/install-wp-tests.sh` if missing). +2. Run `composer test` (or `./vendor/bin/phpunit`) from the plugin root. +3. If tests fail, fix the failures (or the harness) before finishing — do not stop at "implementation done". +4. In the final response, briefly report that tests were run and the result (pass/fail + relevant filter if used). + +Skip only if the change cannot affect PHP/runtime behavior (e.g. docs-only or pure `.mdc`/README wording with no code/config impact) — say so explicitly. diff --git a/.github/checkstyle-problem-matcher.json b/.github/checkstyle-problem-matcher.json new file mode 100644 index 0000000..5c6ce66 --- /dev/null +++ b/.github/checkstyle-problem-matcher.json @@ -0,0 +1,23 @@ +{ + "problemMatcher": [ + { + "owner": "phpcs", + "severity": "error", + "pattern": [ + { + "regexp": "^$", + "file": 1 + }, + { + "regexp": "+)$", + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} diff --git a/.github/workflows/php-coding-standards.yml b/.github/workflows/php-coding-standards.yml new file mode 100644 index 0000000..3eae26c --- /dev/null +++ b/.github/workflows/php-coding-standards.yml @@ -0,0 +1,51 @@ +name: PHP Lint + +on: + push: + pull_request: + workflow_dispatch: + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + +permissions: + contents: read + +jobs: + PHP-Code-Standards: + name: Lint PHP files + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip PHPCS]') || !contains(github.event.head_commit.message, '[skip CI]')" + steps: + - name: Check out source code + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + coverage: none + tools: composer, cs2pr, phpcs + env: + fail-fast: 'true' + + - name: Log debug information + run: | + export PATH=$HOME/.composer/vendor/bin:$PATH + php --version + phpcs -i + composer --version + + - name: Install dependencies + uses: ramsey/composer-install@v3 + + - name: Add error matcher + run: echo "::add-matcher::$(pwd)/.github/checkstyle-problem-matcher.json" + + - name: Run style check (errors block, warnings annotate) + run: | + export PATH=$HOME/.composer/vendor/bin:$PATH + composer run lint -- --report=checkstyle + # Also show warnings as PR annotations (non-blocking) + vendor/bin/phpcs --standard=./ruleset.xml --extensions=php --error-severity=0 . --report=checkstyle || true diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml new file mode 100644 index 0000000..818496b --- /dev/null +++ b/.github/workflows/phpunit.yml @@ -0,0 +1,92 @@ +name: PHPUnit Tests + +on: + push: + pull_request: + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + +permissions: + contents: read + +jobs: + phpunit: + name: PHPUnit Tests (PHP ${{ matrix.php-version }}, WP ${{ matrix.wp-version }}) + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php-version: ['7.4', '8.0', '8.1', '8.2', '8.3'] + wp-version: ['6.8', '6.9', '7.0', 'latest'] + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: wordpress_test + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, mysql + env: + fail-fast: true + + - name: Install Composer dependencies + uses: ramsey/composer-install@v2 + with: + dependency-versions: locked + + - name: Install SVN + run: | + sudo apt-get update -y + sudo apt-get install -y subversion + which svn + svn --version + + - name: Set up WordPress test environment + env: + WP_TESTS_DIR: /tmp/wordpress-tests-lib + WP_CORE_DIR: /tmp/wordpress + run: | + bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 ${{ matrix.wp-version }} || { + git clone --depth=1 --branch=trunk https://github.com/WordPress/wordpress-develop.git /tmp/wordpress-develop + mkdir -p /tmp/wordpress-tests-lib + cp -r /tmp/wordpress-develop/tests/phpunit/* /tmp/wordpress-tests-lib/ + + if [ "${{ matrix.wp-version }}" = "latest" ]; then + wget -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz + else + wget -O /tmp/wordpress.tar.gz https://wordpress.org/wordpress-${{ matrix.wp-version }}.tar.gz + fi + tar -xzf /tmp/wordpress.tar.gz -C /tmp/ + } + + # Fix PHPMailer compatibility issue for WordPress 6.7+ + if [ -f "/tmp/wordpress/wp-includes/class-phpmailer.php" ] && [ ! -f "/tmp/wordpress/wp-includes/class-wp-phpmailer.php" ]; then + cd /tmp/wordpress/wp-includes + ln -s "class-phpmailer.php" "class-wp-phpmailer.php" + echo "Created symlink for PHPMailer compatibility" + fi + + - name: Run PHPUnit tests + run: composer test + env: + WP_TESTS_DIR: /tmp/wordpress-tests-lib + WP_CORE_DIR: /tmp/wordpress + DB_NAME: wordpress_test + DB_USER: root + DB_PASSWORD: root + DB_HOST: 127.0.0.1 diff --git a/.gitignore b/.gitignore index e3e4854..a75e597 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ .idea/ node_modules/ +vendor/ build/ log/ npm-debug.log .DS_Store -coverage.xml \ No newline at end of file +coverage.xml +.phpunit.result.cache +aryo-activity-log/ +aryo-activity-log.*.zip \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 1164285..0000000 --- a/.jshintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "bitwise": true, - "browser": true, - "curly": true, - "eqeqeq": true, - "eqnull": true, - "esnext": true, - "immed": true, - "jquery": true, - "latedef": true, - "newcap": true, - "noarg": true, - "node": true, - "strict": false, - "trailing": true -} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eee44bd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Travis CI Configuration File. -sudo: false -language: php - -# Caching to improve build times. -cache: - apt: true - directories: - - node_modules - - vendor - - $HOME/.composer/cache - -# PHP versions to run tests against. -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - -# WordPress versions to run tests against. -env: - - WP_VERSION=latest WP_MULTISITE=0 - - WP_VERSION=latest WP_MULTISITE=1 - - WP_VERSION=4.4 WP_MULTISITE=0 - - WP_VERSION=4.4 WP_MULTISITE=1 - - WP_VERSION=4.5 WP_MULTISITE=0 - - WP_VERSION=4.5 WP_MULTISITE=1 - - WP_VERSION=4.6 WP_MULTISITE=0 - - WP_VERSION=4.6 WP_MULTISITE=1 - - WP_VERSION=4.7 WP_MULTISITE=0 - - WP_VERSION=4.7 WP_MULTISITE=1 - -before_script: - - | - if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then - phpenv config-rm xdebug.ini - else - echo "xdebug.ini does not exist" - fi - - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION - - export PATH="$HOME/.composer/vendor/bin:$PATH" - - | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then - composer global require "phpunit/phpunit=5.7.*" - else - composer global require "phpunit/phpunit=4.8.*" - fi - - phpunit --version - -script: phpunit --coverage-clover=coverage.xml diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index cf972d0..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,257 +0,0 @@ -/** - * ARYO Activity Log Makefile - */ -'use strict'; - -module.exports = function(grunt) { - - require( 'matchdep' ).filterDev( 'grunt-*' ).forEach( grunt.loadNpmTasks ); - - // Project configuration. - grunt.initConfig( { - pkg: grunt.file.readJSON('package.json'), - - checktextdomain: { - standard: { - options:{ - text_domain: 'aryo-activity-log', - correct_domain: true, - keywords: [ - // WordPress keywords - '__:1,2d', - '_e:1,2d', - '_x:1,2c,3d', - 'esc_html__:1,2d', - 'esc_html_e:1,2d', - 'esc_html_x:1,2c,3d', - 'esc_attr__:1,2d', - 'esc_attr_e:1,2d', - 'esc_attr_x:1,2c,3d', - '_ex:1,2c,3d', - '_n:1,2,4d', - '_nx:1,2,4c,5d', - '_n_noop:1,2,3d', - '_nx_noop:1,2,3c,4d' - ] - }, - files: [ { - src: [ - '**/*.php', - '!node_modules/**', - '!classes/freemius/**', - '!build/**', - '!tests/**', - '!vendor/**', - '!*~' - ], - expand: true - } ] - } - }, - - pot: { - options:{ - text_domain: 'aryo-activity-log', - dest: 'language/', - keywords: [ - // WordPress keywords - '__:1', - '_e:1', - '_x:1,2c', - 'esc_html__:1', - 'esc_html_e:1', - 'esc_html_x:1,2c', - 'esc_attr__:1', - 'esc_attr_e:1', - 'esc_attr_x:1,2c', - '_ex:1,2c', - '_n:1,2', - '_nx:1,2,4c', - '_n_noop:1,2', - '_nx_noop:1,2,3c' - ] - }, - files:{ - src: [ - '**/*.php', - '!node_modules/**', - '!build/**', - '!tests/**', - '!vendor/**', - '!*~' - ], - expand: true - } - }, - - jshint: { - options: { - jshintrc: '.jshintrc' - }, - all: [ - 'assets/js/settings.js' - ] - }, - - watch: { - js: { - files: [ - 'assets/js/settings.js' - ], - tasks: [ - 'jshint' - ], - options: {} - } - }, - - bumpup: { - options: { - updateProps: { - pkg: 'package.json' - } - }, - file: 'package.json' - }, - - replace: { - plugin_main: { - src: [ 'aryo-activity-log.php' ], - overwrite: true, - replacements: [ - { - from: /Version: \d{1,1}\.\d{1,2}\.\d{1,2}/g, - to: 'Version: <%= pkg.version %>' - } - ] - }, - - readme: { - src: [ 'readme.txt' ], - overwrite: true, - replacements: [ - { - from: /Stable tag: \d{1,1}\.\d{1,2}\.\d{1,2}/g, - to: 'Stable tag: <%= pkg.version %>' - } - ] - } - }, - - shell: { - git_add_all : { - command: [ - 'git add --all', - 'git commit -m "Bump to <%= pkg.version %>"' - ].join( '&&' ) - } - }, - - release: { - options: { - bump: false, - npm: false, - commit: false, - //tagName: 'v<%= version %>', - commitMessage: 'released v<%= version %>', - tagMessage: 'Tagged as v<%= version %>' - } - }, - - wp_readme_to_markdown: { - github: { - options: { - wordpressPluginSlug: 'aryo-activity-log', - travisUrlRepo: 'https://travis-ci.org/pojome/wordpress-aryo-activity-log', - gruntDependencyStatusUrl: 'https://david-dm.org/pojome/wordpress-aryo-activity-log' - }, - files: { - 'README.md': 'readme.txt' - } - } - }, - - copy: { - main: { - src: [ - '**', - '!node_modules/**', - '!build/**', - '!wp-assets/**', - '!bin/**', - '!.git/**', - '!tests/**', - '!.travis.yml', - '!.jshintrc', - '!README.md', - '!phpunit.xml', - '!vendor/**', - '!Gruntfile.js', - '!package-lock.json', - '!package.json', - '!.gitignore', - '!.gitmodules', - '!*~' - ], - expand: true, - dest: 'build/' - } - }, - - clean: { - //Clean up build folder - main: [ - 'build' - ] - }, - - wp_deploy: { - deploy:{ - options: { - plugin_slug: '<%= pkg.slug %>', - svn_user: 'KingYes', - build_dir: 'build/' - } - } - }, - - phpunit: { - classes: { - dir: '' - }, - options: { - bin: 'phpunit', - bootstrap: 'tests/bootstrap.php', - colors: true - } - } - - } ); - - // Default task(s). - grunt.registerTask( 'default', [ - 'checktextdomain', - 'jshint', - //'pot', - //'phpunit', - 'wp_readme_to_markdown' - ] ); - - grunt.registerTask( 'build', [ - 'default', - 'clean', - 'copy' - ] ); - - grunt.registerTask( 'publish', [ - 'checktextdomain', - //'pot', - 'jshint', - //'phpunit', - 'bumpup', - 'replace', - 'wp_readme_to_markdown', - 'shell:git_add_all', - 'release' - ] ); -}; \ No newline at end of file diff --git a/assets/css/activity-log-table.css b/assets/css/activity-log-table.css new file mode 100644 index 0000000..55ad1ce --- /dev/null +++ b/assets/css/activity-log-table.css @@ -0,0 +1,172 @@ +/* Activity Log – Admin table styles */ + +/* Menu icon (all admin screens) */ +#adminmenu #toplevel_page_activity-log-page div.wp-menu-image:before { + content: "\f321"; +} + +/* Page title icon */ +h1.aal-page-title:before { + content: "\f321"; + font: 400 25px/1 dashicons !important; + speak: none; + color: #030303; + display: inline-block; + padding-inline-end: .2em; + vertical-align: -18%; +} + +/* Export button */ +#record-actions-submit { + margin-top: 10px; +} + +/* Post type badge (existing) */ +.aal-pt { + color: #ffffff; + padding: 1px 4px; + margin: 0 5px; + font-size: 1em; + border-radius: 3px; + background: #808080; + font-family: inherit; +} + +/* Column widths */ +.toplevel_page_activity-log-page .manage-column { + width: auto; +} + +.toplevel_page_activity-log-page .column-description { + width: 20%; +} + +/* Reset filter link */ +#aal-reset-filter { + display: inline-block; + margin-inline-start: 5px; + line-height: 30px; + text-decoration: none; +} + +#aal-reset-filter .dashicons { + font-size: 15px; + line-height: 30px; + text-decoration: none; +} + +/* Promotion row */ +.aal-table-promotion-row td { + padding: 0; +} + +.aal-table-promotion-inner { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px; + background: white; + border: 1px solid #4C43E5; + border-inline-start-width: 3px; +} + +.aal-promotion-cta { + margin-inline-start: 5px; + font-weight: bold; + color: #4C43E5; +} + +.aal-promotion-dismiss { + display: flex; + align-items: center; + transition: all .1s ease-in-out; + border: none; + margin: 0; + padding: 0; + background: none; + cursor: pointer; + color: #7c7c7c; +} + +.aal-promotion-dismiss::before { + content: '\f335'; + display: block; + font: normal 20px/20px dashicons; + height: 20px; + width: 20px; + text-align: center; +} + +.aal-promotion-dismiss:hover { + color: #4C43E5; +} + +/* Source badges in User column */ +.aal-source-badge { + display: inline-block; + font-size: 11px; + line-height: 1.4; + padding: 1px 6px; + margin-top: 4px; + margin-inline-end: 4px; + border-radius: 3px; + background: #f0f0f1; + color: #50575e; + text-decoration: none; + vertical-align: baseline; +} + +.aal-source-badge:hover { + background: #dcdcde; + color: #2c3338; +} + +.aal-source-badge--channel { + background: #e7f4f9; + color: #2271b1; +} + +.aal-source-badge--channel:hover { + background: #d4eaf4; + color: #135e96; +} + +.aal-source-badge--app { + background: #fcf0e6; + color: #9a4e0e; +} + +.aal-source-badge--app:hover { + background: #f5e1cf; + color: #703800; +} + +/* Source column */ +.toplevel_page_activity-log-page .column-source { + width: 12%; +} + +/* Responsive */ +@media (max-width: 767px) { + .toplevel_page_activity-log-page .manage-column { + width: auto; + } + + .toplevel_page_activity-log-page .column-date, + .toplevel_page_activity-log-page .column-author { + display: table-cell; + width: auto; + } + + .toplevel_page_activity-log-page .column-ip, + .toplevel_page_activity-log-page .column-description, + .toplevel_page_activity-log-page .column-label, + .toplevel_page_activity-log-page .column-source { + display: none; + } + + .toplevel_page_activity-log-page .column-author .avatar { + display: none; + } +} diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh index 58828a0..8a7192d 100644 --- a/bin/install-wp-tests.sh +++ b/bin/install-wp-tests.sh @@ -1,18 +1,21 @@ #!/usr/bin/env bash if [ $# -lt 3 ]; then - echo "usage: $0 [db-host] [wp-version]" + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" exit 1 fi DB_NAME=$1 DB_USER=$2 DB_PASS=$3 -DB_HOST=${4-localhost} +DB_HOST=$4 WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} -WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} -WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress} download() { if [ `which curl` ]; then @@ -22,8 +25,21 @@ download() { fi } -if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then - WP_TESTS_TAG="tags/$WP_VERSION" +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%\-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" else # http serves a single offer, whereas https serves multiple. we only want one download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json @@ -35,7 +51,6 @@ else fi WP_TESTS_TAG="tags/$LATEST_VERSION" fi - set -ex install_wp() { @@ -46,22 +61,44 @@ install_wp() { mkdir -p $WP_CORE_DIR - if [ $WP_VERSION == 'latest' ]; then - local ARCHIVE_NAME='latest' + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-trunk + rm -rf $TMPDIR/wordpress-trunk/* + svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress + mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR else - local ARCHIVE_NAME="wordpress-$WP_VERSION" + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR fi - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz - tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR - - download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php + download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php } install_test_suite() { # portable in-place argument for both GNU sed and Mac OSX sed if [[ $(uname -s) == 'Darwin' ]]; then - local ioption='-i .bak' + local ioption='-i.bak' else local ioption='-i' fi @@ -70,14 +107,17 @@ install_test_suite() { if [ ! -d $WP_TESTS_DIR ]; then # set up testing suite mkdir -p $WP_TESTS_DIR - svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + rm -rf $WP_TESTS_DIR/{includes,data} + svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data fi - cd $WP_TESTS_DIR - if [ ! -f wp-tests-config.php ]; then download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s:__DIR__ . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php @@ -86,7 +126,29 @@ install_test_suite() { } +recreate_db() { + shopt -s nocasematch + if [[ $1 =~ ^(y|yes)$ ]] + then + mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA + create_db + echo "Recreated the database ($DB_NAME)." + else + echo "Leaving the existing database ($DB_NAME) in place." + fi + shopt -u nocasematch +} + +create_db() { + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + # parse DB_HOST for port or socket references local PARTS=(${DB_HOST//\:/ }) local DB_HOSTNAME=${PARTS[0]}; @@ -104,9 +166,16 @@ install_db() { fi # create database - mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA + if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] + then + echo "Reinstalling will delete the existing test database ($DB_NAME)" + read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + recreate_db $DELETE_EXISTING_DB + else + create_db + fi } install_wp install_test_suite -install_db \ No newline at end of file +install_db diff --git a/classes/class-aal-activity-log-list-table.php b/classes/class-aal-activity-log-list-table.php index 7175b11..86d94c8 100644 --- a/classes/class-aal-activity-log-list-table.php +++ b/classes/class-aal-activity-log-list-table.php @@ -170,27 +170,18 @@ public function get_columns() { $columns = array( 'date' => __( 'Date', 'aryo-activity-log' ), 'author' => __( 'User', 'aryo-activity-log' ), - 'ip' => __( 'IP', 'aryo-activity-log' ), + 'source' => __( 'Source', 'aryo-activity-log' ), 'type' => __( 'Topic', 'aryo-activity-log' ), 'label' => __( 'Context', 'aryo-activity-log' ), 'description' => __( 'Meta', 'aryo-activity-log' ), 'action' => __( 'Action', 'aryo-activity-log' ), ); - if ( ! $this->is_store_ip_address() ) { - unset( $columns['ip'] ); - } - return $columns; } - private function is_store_ip_address() { - return 'no-collect-ip' !== AAL_Main::instance()->settings->get_option( 'log_visitor_ip_source' ); - } - public function get_sortable_columns() { return array( - 'ip' => array( 'hist_ip', 'desc' ), 'date' => array( 'hist_time', true ), ); } @@ -339,10 +330,6 @@ public function column_default( $item, $column_name ) { $return .= '
' . date_i18n( get_option( 'time_format' ), $item->hist_time ); break; - case 'ip': - $return = '' . esc_html( $item->hist_ip ) . ''; - break; - default: if ( isset( $item->$column_name ) ) { $return = $item->$column_name; @@ -375,6 +362,49 @@ public function column_author( $item ) { ); } + public function column_source( $item ) { + $parts = array(); + + $raw = isset( $item->request_source ) ? $item->request_source : ''; + if ( '' !== $raw ) { + $parsed = AAL_API::parse_request_source( $raw ); + $channel_labels = AAL_API::get_channel_labels(); + + if ( ! empty( $parsed['channel'] ) && isset( $channel_labels[ $parsed['channel'] ] ) ) { + $parts[] = sprintf( + '%s', + esc_url( $this->get_filtered_link( 'sourceshow', $parsed['channel'] ) ), + esc_html( $channel_labels[ $parsed['channel'] ] ) + ); + } + + if ( ! empty( $parsed['app_name'] ) ) { + $parts[] = sprintf( + '%s', + esc_url( $this->get_filtered_link( 'sourceshow', 'app_password' ) ), + esc_attr__( 'Application Password', 'aryo-activity-log' ), + esc_html( $parsed['app_name'] ) + ); + } elseif ( '' === $parsed['app_name'] && false !== strpos( $raw, 'app:' ) ) { + $parts[] = sprintf( + '%s', + esc_url( $this->get_filtered_link( 'sourceshow', 'app_password' ) ), + esc_html__( 'App Password', 'aryo-activity-log' ) + ); + } + } + + if ( ! empty( $item->hist_ip ) && 'no-collect-ip' !== AAL_Main::instance()->settings->get_option( 'log_visitor_ip_source' ) ) { + $parts[] = sprintf( + '%s', + esc_url( $this->get_filtered_link( 'filter_ip', $item->hist_ip ) ), + esc_html( $item->hist_ip ) + ); + } + + return implode( '
', $parts ); + } + public function column_type( $item ) { $return = __( $item->object_type, 'aryo-activity-log' ); @@ -677,12 +707,35 @@ public function extra_tablenav( $which ) { echo ''; } + if ( AAL_Maintenance::is_schema_ready( '1.1' ) ) { + if ( ! isset( $_REQUEST['sourceshow'] ) ) { + $_REQUEST['sourceshow'] = ''; + } + + $source_options = array( + '' => __( 'All Sources', 'aryo-activity-log' ), + 'abilities' => __( 'WP Abilities', 'aryo-activity-log' ), + 'rest' => __( 'REST API', 'aryo-activity-log' ), + 'xmlrpc' => __( 'XML-RPC', 'aryo-activity-log' ), + 'cli' => __( 'WP-CLI', 'aryo-activity-log' ), + 'cron' => __( 'WP-Cron', 'aryo-activity-log' ), + 'app_password' => __( 'App Password', 'aryo-activity-log' ), + ); + + echo ''; + } + $filters = array( 'dateshow', 'capshow', 'usershow', 'typeshow', 'showaction', + 'sourceshow', ); foreach ( $filters as $filter ) { @@ -729,6 +782,19 @@ public function prepare_items() { $where .= $wpdb->prepare( ' AND `user_caps` = %s', strtolower( sanitize_text_field( $_REQUEST['capshow'] ) ) ); } + if ( AAL_Maintenance::is_schema_ready( '1.1' ) && ! empty( $_REQUEST['sourceshow'] ) ) { + $source_filter = sanitize_key( $_REQUEST['sourceshow'] ); + if ( 'app_password' === $source_filter ) { + $where .= " AND (`request_source` LIKE '%|app:%' OR `request_source` LIKE 'app:%')"; + } else { + $where .= $wpdb->prepare( + " AND (`request_source` = %s OR `request_source` LIKE %s)", + $source_filter, + $wpdb->esc_like( $source_filter ) . '|%' + ); + } + } + if ( isset( $_REQUEST['dateshow'] ) ) { $current_time = current_time( 'timestamp' ); @@ -761,7 +827,7 @@ public function prepare_items() { if ( isset( $_REQUEST['s'] ) ) { $search_esc_like = '%' . $wpdb->esc_like( $_REQUEST['s'] ) . '%'; - $where .= $wpdb->prepare( ' AND (`object_name` LIKE %s OR `object_subtype` LIKE %s)', $search_esc_like, $search_esc_like ); + $where .= $wpdb->prepare( ' AND (`object_name` LIKE %s OR `object_subtype` LIKE %s OR `hist_ip` LIKE %s)', $search_esc_like, $search_esc_like, $search_esc_like ); } $offset = ( $this->get_pagenum() - 1 ) * $items_per_page; diff --git a/classes/class-aal-admin-ui.php b/classes/class-aal-admin-ui.php index 578d75e..42549bc 100644 --- a/classes/class-aal-admin-ui.php +++ b/classes/class-aal-admin-ui.php @@ -31,108 +31,6 @@ public function activity_log_page_func() { - -