diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dd5bf99a534f8b3d7cec5173b8332a8af93233ae
--- /dev/null
+++ b/.github/workflows/moodle-ci.yml
@@ -0,0 +1,171 @@
+name: Moodle Plugin CI
+
+on: [push, pull_request]
+
+jobs:
+  static:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        php: ['7.4']
+        moodle-branch: ['MOODLE_311_STABLE']
+        database: ['pgsql']
+
+    steps:
+      - name: Start PostgreSQL
+        run: docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:9.6
+
+      - name: Check out repository code
+        uses: actions/checkout@v2
+        with:
+          path: plugin
+
+      - name: Setup PHP ${{ matrix.php }}
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          coverage: none
+
+      - name: Get composer cache directory
+        id: composer-cache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Composer cache
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composer-cache.outputs.dir }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: |
+            ${{ runner.os }}-composer-
+
+      - name: npm cache
+        uses: actions/cache@v2
+        with:
+          path: ~/.npm
+          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
+          restore-keys: |
+            ${{ runner.os }}-node-
+
+      - name: Initialise moodle-plugin-ci
+        run: |
+          composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
+          echo $(cd ci/bin; pwd) >> $GITHUB_PATH
+          echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
+          sudo locale-gen en_AU.UTF-8
+          echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
+
+      - name: Install moodle-plugin-ci
+        run: |
+          moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 --no-init
+        env:
+          DB: ${{ matrix.database }}
+          MOODLE_BRANCH: ${{ matrix.moodle-branch }}
+
+      - name: PHP Lint
+        if: ${{ always() }}
+        run: moodle-plugin-ci phplint
+
+      - name: PHP Copy/Paste Detector
+        if: ${{ always() }}
+        run: moodle-plugin-ci phpcpd
+
+      - name: PHP Mess Detector
+        if: ${{ always() }}
+        run: moodle-plugin-ci phpmd
+
+      - name: Moodle Code Checker
+        if: ${{ always() }}
+        run: moodle-plugin-ci codechecker
+
+      - name: Moodle PHPDoc Checker
+        if: ${{ always() }}
+        run: moodle-plugin-ci phpdoc
+
+      - name: Validating
+        if: ${{ always() }}
+        run: moodle-plugin-ci validate
+
+      - name: Check upgrade savepoints
+        if: ${{ always() }}
+        run: moodle-plugin-ci savepoints
+
+      - name: Mustache Lint
+        if: ${{ always() }}
+        run: moodle-plugin-ci mustache
+
+      - name: Grunt
+        if: ${{ always() }}
+        run: moodle-plugin-ci grunt
+
+  test:
+    runs-on: ubuntu-latest
+    needs: static
+
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.3', '7.4']
+        moodle-branch: ['MOODLE_39_STABLE', 'MOODLE_310_STABLE', 'MOODLE_311_STABLE']
+        database: ['mariadb', 'pgsql']
+
+    steps:
+      - name: Start MariaDB
+        if: matrix.database == 'mariadb'
+        run: docker run -p 3306:3306 -e MYSQL_USER=root -e MYSQL_ALLOW_EMPTY_PASSWORD=true -d mariadb:10
+
+      - name: Start PostgreSQL
+        if: matrix.database == 'pgsql'
+        run: docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:9.6
+
+      - name: Check out repository code
+        uses: actions/checkout@v2
+        with:
+          path: plugin
+
+      - name: Setup PHP ${{ matrix.php }}
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          coverage: none
+
+      - name: Get composer cache directory
+        id: composer-cache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+      - name: Composer cache
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composer-cache.outputs.dir }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: |
+            ${{ runner.os }}-composer-
+      - name: npm cache
+        uses: actions/cache@v2
+        with:
+          path: ~/.npm
+          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
+          restore-keys: |
+            ${{ runner.os }}-node-
+
+      - name: Initialise moodle-plugin-ci
+        run: |
+          composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
+          echo $(cd ci/bin; pwd) >> $GITHUB_PATH
+          echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
+          sudo locale-gen en_AU.UTF-8
+          echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
+
+      - name: Install moodle-plugin-ci
+        run: |
+          moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
+        env:
+          DB: ${{ matrix.database }}
+          MOODLE_BRANCH: ${{ matrix.moodle-branch }}
+
+      - name: PHPUnit tests
+        if: ${{ always() }}
+        run: moodle-plugin-ci phpunit
+
+      - name: Behat features
+        if: ${{ always() }}
+        run: moodle-plugin-ci behat --profile chrome
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1e94b2598ea3da1ed7f773415bbde0b30b89875a..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-language: php
-os: linux
-dist: xenial
-
-addons:
-  postgresql: "9.6"
-
-cache:
-  directories:
-    - $HOME/.composer/cache
-    - $HOME/.npm
-
-services:
-  - mysql
-  - docker
-
-php:
-  - 7.2
-  - 7.3
-  - 7.4
-
-env:
-  jobs:
-    - DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE
-    - DB=pgsql MOODLE_BRANCH=MOODLE_38_STABLE
-    - DB=pgsql MOODLE_BRANCH=MOODLE_39_STABLE
-    - DB=pgsql MOODLE_BRANCH=MOODLE_310_STABLE
-    - DB=pgsql MOODLE_BRANCH=master
-    - DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE
-    - DB=mysqli MOODLE_BRANCH=MOODLE_38_STABLE
-    - DB=mysqli MOODLE_BRANCH=MOODLE_39_STABLE
-    - DB=mysqli MOODLE_BRANCH=MOODLE_310_STABLE
-    - DB=mysqli MOODLE_BRANCH=master
-
-before_install:
-  - phpenv config-rm xdebug.ini
-  - cd ../..
-  - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
-  - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
-
-jobs:
-  fast_finish: true
-  include:
-    # Prechecks against latest Moodle stable only.
-    - stage: static
-      php: 7.4
-      env: DB=mysqli MOODLE_BRANCH=MOODLE_310_STABLE
-      install:
-        - moodle-plugin-ci install --no-init
-      script:
-        - moodle-plugin-ci phpdoc
-        - moodle-plugin-ci phplint
-        - moodle-plugin-ci phpcpd
-        - moodle-plugin-ci phpmd
-        - moodle-plugin-ci codechecker
-        - moodle-plugin-ci validate
-        - moodle-plugin-ci savepoints
-        - moodle-plugin-ci mustache
-        - moodle-plugin-ci grunt
-    # Smaller build matrix for development builds
-    - stage: develop
-      php: 7.4
-      env: DB=mysqli MOODLE_BRANCH=MOODLE_310_STABLE
-  exclude:
-    - php: 7.3
-      env: DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE
-    - php: 7.3
-      env: DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE
-    - php: 7.4
-      env: DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE
-    - php: 7.4
-      env: DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE
-
-# Unit tests and behat tests against full matrix.
-install:
-  - moodle-plugin-ci install
-script:
-  - moodle-plugin-ci phpunit --coverage-clover
-  - moodle-plugin-ci behat
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
-
-stages:
-  - static
-  - name: develop
-    if: branch != master AND (type != pull_request OR head_branch != master) AND (tag IS blank)
-  - name: test
-    if: branch = master OR (type = pull_request AND head_branch = master) OR (tag IS present)
diff --git a/classes/local/table/interaction_remaining_table.php b/classes/local/table/interaction_remaining_table.php
index 5e6a06a123de3742687b7e109a902835428f80e4..95ee55d211ebf76dd8c78e99cfdf5694762db0a8 100644
--- a/classes/local/table/interaction_remaining_table.php
+++ b/classes/local/table/interaction_remaining_table.php
@@ -49,7 +49,7 @@ class interaction_remaining_table extends interaction_table {
      */
     public function __construct($uniqueid, $courseids) {
         parent::__construct($uniqueid);
-        global $PAGE;
+        global $PAGE, $CFG;
 
         $this->availabletools = workflow_manager::get_manual_trigger_tools_for_active_workflows();
 
@@ -58,8 +58,13 @@ class interaction_remaining_table extends interaction_table {
         // Otherwise, it would mess up the sorting.
         $fields = "c.id as courseid, p.id AS processid, c.fullname AS coursefullname, c.shortname AS courseshortname, " .
                   "c.startdate, cc.name AS category, COALESCE(l.time, 0) AS lastmodified, l.userid, " .
-                  "l.action, s.subpluginname, " .
-                   get_all_user_name_fields(true, 'u');
+                  "l.action, s.subpluginname, ";
+        if ($CFG->branch >= 311) {
+            $fields .= \core_user\fields::for_name()->get_sql('u', false, '', '', false)->selects;
+        } else {
+            $fields .= get_all_user_name_fields(true, 'u');
+        }
+
         $from = '{course} c ' .
             'LEFT JOIN (' .
                 /* This Subquery creates a table with the one record per course from {tool_lifecycle_action_log}
diff --git a/classes/local/table/interaction_table.php b/classes/local/table/interaction_table.php
index a2d350957d0381fc390460ed96a9ccaa231be57f..73055dbf1f08ed2e2d723f0a7be53ff15099564b 100644
--- a/classes/local/table/interaction_table.php
+++ b/classes/local/table/interaction_table.php
@@ -54,7 +54,7 @@ abstract class interaction_table extends \table_sql {
     /**
      * Initialises the columns of the table. Necessary since attention_table has extra column date.
      */
-    public abstract function init();
+    abstract public function init();
 
     /**
      * Render coursefullname column.
@@ -86,7 +86,7 @@ abstract class interaction_table extends \table_sql {
      * @param object $row Row data.
      * @return string Rendered tools html
      */
-    public abstract function col_tools($row);
+    abstract public function col_tools($row);
 
     /**
      * Render status column.
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index 82b29138263a1ae3f7d10c2b5f49b4a99767f21f..0000000000000000000000000000000000000000
--- a/codecov.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-fixes:
-	- "moodle/admin/tool/lifecycle/::"
-ignore:
-    # artificial/Moodle API files
-    - "classes/plugininfo/*"
-    - "tests/**/*"
-    - "lang/**/*"
-    - "db/**/*"
-    - "db/.*"
-    - "version.php"
-    - "step/**/lang"
-    - "step/**/db"
-    - "step/**/settings.php"
-    - "step/**/version.php"
-    - "trigger/**/db"
-    - "trigger/**/lang"
-    - "trigger/**/settings.php"
-    - "trigger/**/version.php"
-    # UI classes
-    - "classes/table/*"
-    - "activeprocesses.php"
-    - "coursebackups.php"
-    - "renderer.php"
-    - "restore.php"
-    - "settings.php"
-    - "adminsettings.php"
-    - "view.php"
\ No newline at end of file
diff --git a/step/interactionlib.php b/step/interactionlib.php
index 9e27698fdf5619055e34a0413b7100a838d9e140..780314f4309578d7851802eb660c6421cff0a046 100644
--- a/step/interactionlib.php
+++ b/step/interactionlib.php
@@ -48,7 +48,7 @@ abstract class interactionlibbase {
      * Returns the capability a user has to have to make decisions for a specific course.
      * @return string capability string.
      */
-    public abstract function get_relevant_capability();
+    abstract public function get_relevant_capability();
 
     /**
      * Returns if only the courses of a step instance should be shown or all courses for the submodule.
@@ -66,7 +66,7 @@ abstract class interactionlibbase {
      * @param process $process process the action tools are requested for
      * @return array of action tools
      */
-    public abstract function get_action_tools($process);
+    abstract public function get_action_tools($process);
 
 
     /**
@@ -74,7 +74,7 @@ abstract class interactionlibbase {
      * @param process $process process the status message is requested for
      * @return string status message
      */
-    public abstract function get_status_message($process);
+    abstract public function get_status_message($process);
 
     /**
      * Returns the display name for the given action.
@@ -83,7 +83,7 @@ abstract class interactionlibbase {
      * @param string $user html-link with username as text that refers to the user profile.
      * @return string action display name
      */
-    public abstract function get_action_string($action, $user);
+    abstract public function get_action_string($action, $user);
 
     /**
      * Called when a user triggered an action for a process instance.
@@ -96,7 +96,7 @@ abstract class interactionlibbase {
      *      - noaction: the action is not defined for the step.
      *      - rollback: the step has finished and respective controller class should rollback the process.
      */
-    public abstract function handle_interaction($process, $step, $action = 'default');
+    abstract public function handle_interaction($process, $step, $action = 'default');
 
     /**
      * Returns the due date.
diff --git a/step/lib.php b/step/lib.php
index f544e9ea7ea761cfb61bf452fa7b300fd229b283..a21044faf22a992e596389f51937692ea2aa6ebe 100644
--- a/step/lib.php
+++ b/step/lib.php
@@ -51,7 +51,7 @@ abstract class libbase {
      * @param mixed $course to be processed.
      * @return step_response
      */
-    public abstract function process_course($processid, $instanceid, $course);
+    abstract public function process_course($processid, $instanceid, $course);
 
     /**
      * Processes the course in status waiting and returns a repsonse.
@@ -97,7 +97,7 @@ abstract class libbase {
      * The return value should be equivalent with the name of the subplugin folder.
      * @return string technical name of the subplugin
      */
-    public abstract function get_subpluginname();
+    abstract public function get_subpluginname();
 
     /**
      * Defines which settings each instance of the subplugin offers for the user to define.
diff --git a/trigger/lib.php b/trigger/lib.php
index f36d216a75df068013cbe5263778335e59644784..5ec5ad787bd783f49a9067881211443630b1072f 100644
--- a/trigger/lib.php
+++ b/trigger/lib.php
@@ -43,7 +43,7 @@ abstract class base {
      * The return value should be equivalent with the name of the subplugin folder.
      * @return string technical name of the subplugin
      */
-    public abstract function get_subpluginname();
+    abstract public function get_subpluginname();
 
     /**
      * Defines which settings each instance of the subplugin offers for the user to define.
@@ -94,7 +94,7 @@ abstract class base {
      * Specifies if the trigger is a manual or an automatic trigger.
      * @return boolean
      */
-    public abstract function is_manual_trigger();
+    abstract public function is_manual_trigger();
 
     /**
      * Returns the status message for the trigger.
@@ -123,7 +123,7 @@ abstract class base_automatic extends base {
      * @param int $triggerid Id of the trigger instance.
      * @return trigger_response
      */
-    public abstract function check_course($course, $triggerid);
+    abstract public function check_course($course, $triggerid);
 
     /**
      * Defines if the trigger subplugin is started manually or automatically.