diff --git a/amd/build/tablebulkactions.min.js b/amd/build/tablebulkactions.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..b899d9847d0498228f1a697236e18e9e313176e5
--- /dev/null
+++ b/amd/build/tablebulkactions.min.js
@@ -0,0 +1,2 @@
+define ("tool_lifecycle/tablebulkactions",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=function(){var a=document.querySelectorAll("input[name=\"procerror-select\"]"),b=document.querySelectorAll("*[data-lifecycle-action]");b.forEach(function(b){b.onclick=function(c){c.preventDefault();var d=[{k:"action",v:b.getAttribute("data-lifecycle-action")},{k:"sesskey",v:M.cfg.sesskey}];if("1"===b.getAttribute("data-lifecycle-forall")){d.push({k:"all",v:"1"});e(window.location,d)}else{a.forEach(function(a){if(a.checked){d.push({k:"id[]",v:a.value})}});e(window.location,d)}}})};function b(a){if("undefined"==typeof Symbol||null==a[Symbol.iterator]){if(Array.isArray(a)||(a=c(a))){var b=0,d=function(){};return{s:d,n:function n(){if(b>=a.length)return{done:!0};return{done:!1,value:a[b++]}},e:function e(a){throw a},f:d}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var e,f=!0,g=!1,h;return{s:function s(){e=a[Symbol.iterator]()},n:function n(){var a=e.next();f=a.done;return a},e:function e(a){g=!0;h=a},f:function f(){try{if(!f&&null!=e.return)e.return()}finally{if(g)throw h}}}}function c(a,b){if(!a)return;if("string"==typeof a)return d(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return d(a,b)}function d(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);c<b;c++){d[c]=a[c]}return d}function e(a,c){var d=document.createElement("form");document.body.appendChild(d);d.method="post";d.action=a;var e=b(c),f;try{for(e.s();!(f=e.n()).done;){var g=f.value,h=document.createElement("input");h.type="hidden";h.name=g.k;h.value=g.v;d.appendChild(h)}}catch(a){e.e(a)}finally{e.f()}d.submit()}});
+//# sourceMappingURL=tablebulkactions.min.js.map
diff --git a/amd/build/tablebulkactions.min.js.map b/amd/build/tablebulkactions.min.js.map
new file mode 100644
index 0000000000000000000000000000000000000000..ade9ff54eeee348eec8c06028506811da483ec53
--- /dev/null
+++ b/amd/build/tablebulkactions.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/tablebulkactions.js"],"names":["checkboxes","document","querySelectorAll","action","forEach","a","onclick","e","preventDefault","data","k","v","getAttribute","M","cfg","sesskey","push","redirectPost","window","location","c","checked","value","url","form","createElement","body","appendChild","method","pair","input","type","name","submit"],"mappings":"uIA6CO,UAAgB,IACbA,CAAAA,CAAU,CAAGC,QAAQ,CAACC,gBAAT,CAA0B,kCAA1B,CADA,CAGbC,CAAM,CAAGF,QAAQ,CAACC,gBAAT,CAA0B,0BAA1B,CAHI,CAInBC,CAAM,CAACC,OAAP,CAAe,SAACC,CAAD,CAAO,CAClBA,CAAC,CAACC,OAAF,CAAY,SAACC,CAAD,CAAO,CACfA,CAAC,CAACC,cAAF,GACA,GAAIC,CAAAA,CAAI,CAAG,CACP,CAACC,CAAC,CAAE,QAAJ,CAAcC,CAAC,CAAEN,CAAC,CAACO,YAAF,CAAe,uBAAf,CAAjB,CADO,CAEP,CAACF,CAAC,CAAE,SAAJ,CAAeC,CAAC,CAAEE,CAAC,CAACC,GAAF,CAAMC,OAAxB,CAFO,CAAX,CAIA,GAAgD,GAA5C,GAAAV,CAAC,CAACO,YAAF,CAAe,uBAAf,CAAJ,CAAqD,CACjDH,CAAI,CAACO,IAAL,CAAU,CAACN,CAAC,CAAE,KAAJ,CAAWC,CAAC,CAAE,GAAd,CAAV,EACAM,CAAY,CAACC,MAAM,CAACC,QAAR,CAAkBV,CAAlB,CACf,CAHD,IAGQ,CACJT,CAAU,CAACI,OAAX,CAAmB,SAACgB,CAAD,CAAO,CACtB,GAAIA,CAAC,CAACC,OAAN,CAAe,CACXZ,CAAI,CAACO,IAAL,CAAU,CAACN,CAAC,CAAE,MAAJ,CAAYC,CAAC,CAAES,CAAC,CAACE,KAAjB,CAAV,CACH,CACJ,CAJD,EAKAL,CAAY,CAACC,MAAM,CAACC,QAAR,CAAkBV,CAAlB,CACf,CACJ,CACJ,CAnBD,CAoBH,C,u/BA1CD,QAASQ,CAAAA,CAAT,CAAsBM,CAAtB,CAA2Bd,CAA3B,CAAiC,CAC7B,GAAMe,CAAAA,CAAI,CAAGvB,QAAQ,CAACwB,aAAT,CAAuB,MAAvB,CAAb,CACAxB,QAAQ,CAACyB,IAAT,CAAcC,WAAd,CAA0BH,CAA1B,EACAA,CAAI,CAACI,MAAL,CAAc,MAAd,CACAJ,CAAI,CAACrB,MAAL,CAAcoB,CAAd,CAJ6B,QAKVd,CALU,QAK7B,2BAAyB,IAAdoB,CAAAA,CAAc,SACfC,CAAK,CAAG7B,QAAQ,CAACwB,aAAT,CAAuB,OAAvB,CADO,CAErBK,CAAK,CAACC,IAAN,CAAa,QAAb,CACAD,CAAK,CAACE,IAAN,CAAaH,CAAI,CAACnB,CAAlB,CACAoB,CAAK,CAACR,KAAN,CAAcO,CAAI,CAAClB,CAAnB,CACAa,CAAI,CAACG,WAAL,CAAiBG,CAAjB,CACH,CAX4B,+BAY7BN,CAAI,CAACS,MAAL,EACH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript controller for checkboxed table.\n * @module     tool_lifecycle/tablebulkactions\n * @copyright  2021 Justus Dieckmann WWU\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Helper function to redirect via POST\n * @param {String} url redirect to\n * @param {Array} data redirect with data\n */\nfunction redirectPost(url, data) {\n    const form = document.createElement('form');\n    document.body.appendChild(form);\n    form.method = 'post';\n    form.action = url;\n    for (const pair of data) {\n        const input = document.createElement('input');\n        input.type = 'hidden';\n        input.name = pair.k;\n        input.value = pair.v;\n        form.appendChild(input);\n    }\n    form.submit();\n}\n\n/**\n * Init function\n */\nexport function init() {\n    const checkboxes = document.querySelectorAll('input[name=\"procerror-select\"]');\n\n    const action = document.querySelectorAll('*[data-lifecycle-action]');\n    action.forEach((a) => {\n        a.onclick = (e) => {\n            e.preventDefault();\n            let data = [\n                {k: 'action', v: a.getAttribute('data-lifecycle-action')},\n                {k: 'sesskey', v: M.cfg.sesskey}\n            ];\n            if (a.getAttribute('data-lifecycle-forall') === '1') {\n                data.push({k: 'all', v: '1'});\n                redirectPost(window.location, data);\n            } else  {\n                checkboxes.forEach((c) => {\n                    if (c.checked) {\n                        data.push({k: 'id[]', v: c.value});\n                    }\n                });\n                redirectPost(window.location, data);\n            }\n        };\n    });\n}"],"file":"tablebulkactions.min.js"}
\ No newline at end of file
diff --git a/amd/src/tablebulkactions.js b/amd/src/tablebulkactions.js
new file mode 100644
index 0000000000000000000000000000000000000000..5e0afbad517e8e478e1551d8002996ab26d9f330
--- /dev/null
+++ b/amd/src/tablebulkactions.js
@@ -0,0 +1,70 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Javascript controller for checkboxed table.
+ * @module     tool_lifecycle/tablebulkactions
+ * @copyright  2021 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Helper function to redirect via POST
+ * @param {String} url redirect to
+ * @param {Array} data redirect with data
+ */
+function redirectPost(url, data) {
+    const form = document.createElement('form');
+    document.body.appendChild(form);
+    form.method = 'post';
+    form.action = url;
+    for (const pair of data) {
+        const input = document.createElement('input');
+        input.type = 'hidden';
+        input.name = pair.k;
+        input.value = pair.v;
+        form.appendChild(input);
+    }
+    form.submit();
+}
+
+/**
+ * Init function
+ */
+export function init() {
+    const checkboxes = document.querySelectorAll('input[name="procerror-select"]');
+
+    const action = document.querySelectorAll('*[data-lifecycle-action]');
+    action.forEach((a) => {
+        a.onclick = (e) => {
+            e.preventDefault();
+            let data = [
+                {k: 'action', v: a.getAttribute('data-lifecycle-action')},
+                {k: 'sesskey', v: M.cfg.sesskey}
+            ];
+            if (a.getAttribute('data-lifecycle-forall') === '1') {
+                data.push({k: 'all', v: '1'});
+                redirectPost(window.location, data);
+            } else  {
+                checkboxes.forEach((c) => {
+                    if (c.checked) {
+                        data.push({k: 'id[]', v: c.value});
+                    }
+                });
+                redirectPost(window.location, data);
+            }
+        };
+    });
+}
\ No newline at end of file
diff --git a/classes/action.php b/classes/action.php
index fe93c00d6663794350e042ba34e2842ff6345988..cc55e6281c7d14029f9d798e3dbe996f3dec4548 100644
--- a/classes/action.php
+++ b/classes/action.php
@@ -22,8 +22,6 @@
  */
 namespace tool_lifecycle;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Delivers all available action names throughout the plugin.
  * @package    tool_lifecycle
diff --git a/classes/event/process_proceeded.php b/classes/event/process_proceeded.php
index 0df4ac716448c4a923d5a91b9ca5f96dd2a5108f..c3c9ac93c20ca29b3335909d09edc11f7adf626b 100644
--- a/classes/event/process_proceeded.php
+++ b/classes/event/process_proceeded.php
@@ -26,8 +26,6 @@ namespace tool_lifecycle\event;
 use moodle_url;
 use tool_lifecycle\local\entity\process;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * The process_proceeded event class.
  *
diff --git a/classes/event/process_rollback.php b/classes/event/process_rollback.php
index b83d73b3a8adbac83657e93ae01db4af38c5ae39..5fadca0902f99d649a43e47fda7423ea4b346447 100644
--- a/classes/event/process_rollback.php
+++ b/classes/event/process_rollback.php
@@ -27,8 +27,6 @@ namespace tool_lifecycle\event;
 use moodle_url;
 use tool_lifecycle\local\entity\process;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * The process_rollback event class.
  *
diff --git a/classes/event/process_triggered.php b/classes/event/process_triggered.php
index dcde2225f088971ea69ab6c15fdcf3d76edc8ccd..0648b50195cd450bf2095ba0c33b98086a8f9966 100644
--- a/classes/event/process_triggered.php
+++ b/classes/event/process_triggered.php
@@ -27,8 +27,6 @@ namespace tool_lifecycle\event;
 use moodle_url;
 use tool_lifecycle\local\entity\process;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * The process_triggered event class.
  *
diff --git a/classes/local/backup/backup_lifecycle_workflow.php b/classes/local/backup/backup_lifecycle_workflow.php
index 4505fa032a56e4ae035456cbfe5fe0704806fa15..4cdfa07a34f430aa67188174354f51855a0553e7 100644
--- a/classes/local/backup/backup_lifecycle_workflow.php
+++ b/classes/local/backup/backup_lifecycle_workflow.php
@@ -31,8 +31,6 @@ use tool_lifecycle\local\manager\trigger_manager;
 use tool_lifecycle\local\manager\settings_manager;
 use tool_lifecycle\settings_type;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Class to backup a workflow.
  * @package    tool_lifecycle
diff --git a/classes/local/backup/restore_lifecycle_workflow.php b/classes/local/backup/restore_lifecycle_workflow.php
index 16a4141e317411d76c2f34e50bcc3f6140824d97..8662fa2c184c9d647d85c5737e7d3d10c8918c27 100644
--- a/classes/local/backup/restore_lifecycle_workflow.php
+++ b/classes/local/backup/restore_lifecycle_workflow.php
@@ -31,8 +31,6 @@ use tool_lifecycle\local\manager\trigger_manager;
 use tool_lifecycle\local\manager\settings_manager;
 use tool_lifecycle\settings_type;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Class to restore a workflow.
  * @package    tool_lifecycle
diff --git a/classes/local/data/manual_trigger_tool.php b/classes/local/data/manual_trigger_tool.php
index 48ffa42c6c75d09d3784199942862e8ce470ac6f..d669ba827bffde201301cd1c1e46a93f964a816a 100644
--- a/classes/local/data/manual_trigger_tool.php
+++ b/classes/local/data/manual_trigger_tool.php
@@ -25,8 +25,6 @@ namespace tool_lifecycle\local\data;
 
 use renderable;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Class representing a manual trigger tool
  *
diff --git a/classes/local/entity/process.php b/classes/local/entity/process.php
index c05fea0e6604abcf031fd70c965a10ae9577e3f6..3f94460e3adbd4f51e5fd828c3469cd400bf232e 100644
--- a/classes/local/entity/process.php
+++ b/classes/local/entity/process.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\entity;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Life Cycle Process class
  *
diff --git a/classes/local/entity/step_subplugin.php b/classes/local/entity/step_subplugin.php
index e50ecde014f0425de8fcfc72ba6bc11fd07867e1..711a37e17c80ddbea688f165c8bb7499ef763460 100644
--- a/classes/local/entity/step_subplugin.php
+++ b/classes/local/entity/step_subplugin.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\entity;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Subplugin class
  *
diff --git a/classes/local/entity/subplugin.php b/classes/local/entity/subplugin.php
index 15e3809850fe88b939646a7cabd1f13f76ba0968..2190ba233658c4129582b4e7b3063b830075e01b 100644
--- a/classes/local/entity/subplugin.php
+++ b/classes/local/entity/subplugin.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\entity;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Subplugin class
  *
@@ -32,7 +30,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class subplugin{
+abstract class subplugin {
 
     /** @var int $id Id of subplugin */
     public $id;
diff --git a/classes/local/entity/trigger_subplugin.php b/classes/local/entity/trigger_subplugin.php
index d4c98fab424340ff84d83ed0856413678e6e2d08..ebc8d14d77835713160863b765d69e2da9877bbb 100644
--- a/classes/local/entity/trigger_subplugin.php
+++ b/classes/local/entity/trigger_subplugin.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\entity;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Trigger subplugin class
  *
@@ -32,7 +30,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class trigger_subplugin extends subplugin{
+class trigger_subplugin extends subplugin {
 
     /**
      * Creates a subplugin from a db record.
diff --git a/classes/local/entity/workflow.php b/classes/local/entity/workflow.php
index 9be4f03c67a378f78f9f286df138a40bdb58eb82..11bd5afd9f7faabbb63d0d3ff5c57f11732e63f6 100644
--- a/classes/local/entity/workflow.php
+++ b/classes/local/entity/workflow.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\entity;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Life Cycle Workflow class
  *
diff --git a/classes/local/manager/delayed_courses_manager.php b/classes/local/manager/delayed_courses_manager.php
index cb989ec8dfbc6affb870338bc443681294d3cdab..a9c4024574a42d82f4a96fab1decd7e10df173ad 100644
--- a/classes/local/manager/delayed_courses_manager.php
+++ b/classes/local/manager/delayed_courses_manager.php
@@ -24,7 +24,6 @@
  */
 namespace tool_lifecycle\local\manager;
 
-defined('MOODLE_INTERNAL') || die();
 /**
  * Manager for Delayed Courses.
  *
diff --git a/classes/local/manager/interaction_manager.php b/classes/local/manager/interaction_manager.php
index 5dbe2d45f531bcee597e90d610fcde46614a2ebc..87ecfff877f534a2ff7c4f2ab802b2bcfaddbdf0 100644
--- a/classes/local/manager/interaction_manager.php
+++ b/classes/local/manager/interaction_manager.php
@@ -27,8 +27,6 @@ use tool_lifecycle\local\entity\process;
 use tool_lifecycle\processor;
 use tool_lifecycle\local\response\step_interactive_response;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager to handle interactions by users
  *
diff --git a/classes/local/manager/lib_manager.php b/classes/local/manager/lib_manager.php
index 7e8277735069d1018dfb55de02fc76476d183b57..afd381eead7d6a79d679f4b6e7f168d57916e859 100644
--- a/classes/local/manager/lib_manager.php
+++ b/classes/local/manager/lib_manager.php
@@ -28,8 +28,6 @@ use tool_lifecycle\trigger\base;
 use tool_lifecycle\trigger\base_automatic;
 use tool_lifecycle\trigger\base_manual;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager to retrive the lib of each subplugin.
  *
diff --git a/classes/local/manager/process_data_manager.php b/classes/local/manager/process_data_manager.php
index fb3f168198f52b778d1dd8bfa4fc2a3e2935da05..722e2a6a2731320751316700c1ab3ffe7575ab72 100644
--- a/classes/local/manager/process_data_manager.php
+++ b/classes/local/manager/process_data_manager.php
@@ -30,8 +30,6 @@ namespace tool_lifecycle\local\manager;
 use tool_lifecycle\local\entity\process;
 use tool_lifecycle\local\entity\trigger_subplugin;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager for data of Life Cycle Processes.
  *
diff --git a/classes/local/manager/process_manager.php b/classes/local/manager/process_manager.php
index f8b28c9fc9b7a16eced8bc6eb1e5f9efe940b451..0eba63e97738ce0492d641c5d832d8518182a24e 100644
--- a/classes/local/manager/process_manager.php
+++ b/classes/local/manager/process_manager.php
@@ -24,12 +24,11 @@
 namespace tool_lifecycle\local\manager;
 
 use core\event\course_deleted;
+use Exception;
 use tool_lifecycle\local\entity\process;
 use tool_lifecycle\event\process_proceeded;
 use tool_lifecycle\event\process_rollback;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager for Life Cycle Processes
  *
@@ -245,4 +244,69 @@ class process_manager {
         $steplib->abort_course($process);
         self::remove_process($process);
     }
+
+    /**
+     * Moves a process into the procerror table.
+     *
+     * @param process $process The process
+     * @param Exception $e The exception
+     * @return void
+     */
+    public static function insert_process_error(process $process, Exception $e) {
+        global $DB;
+
+        $procerror = (object) clone $process;
+        $procerror->errormessage = get_class($e) . ': ' . $e->getMessage();
+        $procerror->errortrace = $e->getTraceAsString();
+        $procerror->errortimecreated = time();
+        $m = '';
+        foreach ($e->getTrace() as $v) {
+            $m .= $v['file'] . ':' . $v['line'] . '::';
+        }
+        $procerror->errorhash = md5($m);
+        $procerror->waiting = intval($procerror->waiting);
+
+        $DB->insert_record_raw('tool_lifecycle_proc_error', $procerror, false, false, true);
+        $DB->delete_records('tool_lifecycle_process', ['id' => $process->id]);
+    }
+
+    /**
+     * Proceed process from procerror back into the process board.
+     * @param int $processid the processid
+     * @return void
+     */
+    public static function proceed_process_after_error(int $processid) {
+        global $DB;
+        $process = $DB->get_record('tool_lifecycle_proc_error', ['id' => $processid]);
+        // Unset process error entries.
+        unset($process->errormessage);
+        unset($process->errortrace);
+        unset($process->errorhash);
+        unset($process->errortimecreated);
+
+        $DB->insert_record_raw('tool_lifecycle_process', $process, false, false, true);
+        $DB->delete_records('tool_lifecycle_proc_error', ['id' => $process->id]);
+    }
+
+    /**
+     * Rolls back a process from procerror table
+     * @param int $processid the processid
+     * @return void
+     */
+    public static function rollback_process_after_error(int $processid) {
+        global $DB;
+
+        $process = $DB->get_record('tool_lifecycle_proc_error', ['id' => $processid]);
+        // Unset process error entries.
+        unset($process->errormessage);
+        unset($process->errortrace);
+        unset($process->errorhash);
+        unset($process->errortimecreated);
+
+        $DB->insert_record_raw('tool_lifecycle_process', $process, false, false, true);
+        $DB->delete_records('tool_lifecycle_proc_error', ['id' => $process->id]);
+
+        delayed_courses_manager::set_course_delayed_for_workflow($process->courseid, true, $process->workflowid);
+        self::rollback_process($process);
+    }
 }
diff --git a/classes/local/manager/step_manager.php b/classes/local/manager/step_manager.php
index 4a03f4ee57fb07c8b567040fa78e3a2f01e9dd56..cd58d1a5b3d01348d91a53f92c38619a8c60ce83 100644
--- a/classes/local/manager/step_manager.php
+++ b/classes/local/manager/step_manager.php
@@ -27,8 +27,6 @@ use tool_lifecycle\action;
 use tool_lifecycle\local\entity\step_subplugin;
 use tool_lifecycle\settings_type;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager for Subplugins
  *
diff --git a/classes/local/manager/subplugin_manager.php b/classes/local/manager/subplugin_manager.php
index 8728582f15ab31a2f53c4db5c21751a70bc03abe..951cc6690aaa9f33d0fcebb33acad57ecd06fbce 100644
--- a/classes/local/manager/subplugin_manager.php
+++ b/classes/local/manager/subplugin_manager.php
@@ -23,22 +23,6 @@
  */
 namespace tool_lifecycle\local\manager;
 
-defined('MOODLE_INTERNAL') || die();
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
 /**
  * Manager for Subplugins
  *
diff --git a/classes/local/manager/trigger_manager.php b/classes/local/manager/trigger_manager.php
index e59a8a4af65979274e9d32c37a464923f35cb031..2c7d84d34d4167ea1bf604b7d161e7b40fea9bd0 100644
--- a/classes/local/manager/trigger_manager.php
+++ b/classes/local/manager/trigger_manager.php
@@ -28,8 +28,6 @@ use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
 use tool_lifecycle\settings_type;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager for Trigger subplugins
  *
diff --git a/classes/local/manager/workflow_manager.php b/classes/local/manager/workflow_manager.php
index 690d139712dfc884187f9065f6520b2401d7f914..34e721ea96b9aa6cafc111accd198bb7b3cdc002 100644
--- a/classes/local/manager/workflow_manager.php
+++ b/classes/local/manager/workflow_manager.php
@@ -30,8 +30,6 @@ use tool_lifecycle\local\backup\backup_lifecycle_workflow;
 use tool_lifecycle\local\data\manual_trigger_tool;
 use tool_lifecycle\settings_type;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Manager for Life Cycle Workflows
  *
diff --git a/classes/local/response/step_interactive_response.php b/classes/local/response/step_interactive_response.php
index f9c89d414fde3a681a426abfaef78acc11ee5bab..ab23fdd0af282fde5e9f2af96380fe69c6f39353 100644
--- a/classes/local/response/step_interactive_response.php
+++ b/classes/local/response/step_interactive_response.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\response;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Possible Responses of a Subplugin for interaction handling
  *
diff --git a/classes/local/response/step_response.php b/classes/local/response/step_response.php
index f6301d74169dfec5023e0274bb838425a812a784..15266179daf0e6b92acaa87d58c5473314e7bc86 100644
--- a/classes/local/response/step_response.php
+++ b/classes/local/response/step_response.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\response;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Possible Responses of a Subplugin
  *
diff --git a/classes/local/response/trigger_response.php b/classes/local/response/trigger_response.php
index 9e4f09752314094299406e94b64e1e0c7870c6e1..8276b0969c986f48a2c07c763537c213a378e8f6 100644
--- a/classes/local/response/trigger_response.php
+++ b/classes/local/response/trigger_response.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\local\response;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Possible Responses of a Trigger Subplugin
  *
diff --git a/classes/local/table/process_errors_table.php b/classes/local/table/process_errors_table.php
new file mode 100644
index 0000000000000000000000000000000000000000..545a883dc689e77a9c43fa2aabb98ca6351a83ef
--- /dev/null
+++ b/classes/local/table/process_errors_table.php
@@ -0,0 +1,221 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Table listing all process errors
+ *
+ * @package tool_lifecycle
+ * @copyright  2021 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_lifecycle\local\table;
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Table listing all process errors
+ *
+ * @package tool_lifecycle
+ * @copyright  2021 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class process_errors_table extends \table_sql {
+
+    /**
+     * @var array "cached" lang strings
+     */
+    private $strings;
+
+    /**
+     * Constructor for delayed_courses_table.
+     *
+     * @throws \coding_exception
+     */
+    public function __construct() {
+        global $OUTPUT;
+
+        parent::__construct('tool_lifecycle-process_errors');
+
+        $this->strings = [
+                'proceed' => get_string('proceed', 'tool_lifecycle'),
+                'rollback' => get_string('rollback', 'tool_lifecycle')
+        ];
+
+        $fields = 'c.fullname as course, w.title as workflow, s.instancename as step, pe.*';
+
+        $from = '{tool_lifecycle_proc_error} pe ' .
+            'JOIN {tool_lifecycle_workflow} w ON pe.workflowid = w.id ' .
+            'JOIN {tool_lifecycle_step} s ON pe.workflowid = s.workflowid AND pe.stepindex = s.sortindex ' .
+            'LEFT JOIN {course} c ON pe.courseid = c.id ';
+
+        $this->set_sql($fields, $from, 'TRUE');
+        $this->column_nosort = ['select', 'tools'];
+        $this->define_columns(['select', 'workflow', 'step', 'courseid', 'course', 'error', 'tools']);
+        $this->define_headers([
+                $OUTPUT->render(new \core\output\checkbox_toggleall('procerrors-table', true, [
+                        'id' => 'select-all-procerrors',
+                        'name' => 'select-all-procerrors',
+                        'label' => get_string('selectall'),
+                        'labelclasses' => 'sr-only',
+                        'classes' => 'm-1',
+                        'checked' => false,
+                ])),
+                get_string('workflow', 'tool_lifecycle'),
+                get_string('step', 'tool_lifecycle'),
+                get_string('courseid', 'tool_lifecycle'),
+                get_string('course'),
+                get_string('error'),
+                get_string('tools', 'tool_lifecycle')
+        ]);
+    }
+
+    /**
+     * Render error column.
+     *
+     * @param object $row Row data.
+     * @return string error cell
+     * @throws \coding_exception
+     * @throws \moodle_exception
+     */
+    public function col_error($row) {
+        return "<details><summary>" .
+                nl2br(htmlentities($row->errormessage)) .
+                "</summary><code>" .
+                nl2br(htmlentities($row->errortrace)) .
+                "</code></details>";
+    }
+
+    /**
+     * Render tools column.
+     *
+     * @param object $row Row data.
+     * @return string pluginname of the subplugin
+     * @throws \coding_exception
+     * @throws \moodle_exception
+     */
+    public function col_tools($row) {
+        global $OUTPUT;
+
+        $actionmenu = new \action_menu();
+        $actionmenu->add_primary_action(
+                new \action_menu_link_primary(
+                        new \moodle_url('', ['action' => 'proceed', 'id[]' => $row->id, 'sesskey' => sesskey()]),
+                        new \pix_icon('e/tick', $this->strings['proceed']),
+                        $this->strings['proceed']
+                )
+        );
+        $actionmenu->add_primary_action(
+                new \action_menu_link_primary(
+                        new \moodle_url('', ['action' => 'rollback', 'id[]' => $row->id, 'sesskey' => sesskey()]),
+                        new \pix_icon('e/undo', $this->strings['rollback']),
+                        $this->strings['rollback']
+                )
+        );
+        return $OUTPUT->render($actionmenu);
+    }
+
+    /**
+     * Generate the select column.
+     *
+     * @param \stdClass $data
+     * @return string
+     */
+    public function col_select($data) {
+        global $OUTPUT;
+
+        $checkbox = new \core\output\checkbox_toggleall('procerrors-table', false, [
+                'classes' => 'usercheckbox m-1',
+                'id' => 'procerror' . $data->id,
+                'name' => 'procerror-select',
+                'value' => $data->id,
+                'checked' => false,
+                'label' => get_string('selectitem', 'moodle', $data->id),
+                'labelclasses' => 'accesshide',
+        ]);
+
+        return $OUTPUT->render($checkbox);
+    }
+
+    /**
+     * Override the table show_hide_link to not show for select column.
+     *
+     * @param string $column the column name, index into various names.
+     * @param int $index numerical index of the column.
+     * @return string HTML fragment.
+     */
+    protected function show_hide_link($column, $index) {
+        if ($index > 0) {
+            return parent::show_hide_link($column, $index);
+        }
+        return '';
+    }
+
+    /**
+     * Show custom nothing to display message.
+     * @return void
+     */
+    public function print_nothing_to_display() {
+        global $OUTPUT;
+
+        // Render the dynamic table header.
+        echo $this->get_dynamic_table_html_start();
+
+        // Render button to allow user to reset table preferences.
+        echo $this->render_reset_button();
+
+        $this->print_initials_bar();
+
+        echo $OUTPUT->heading(get_string('noprocesserrors', 'tool_lifecycle'));
+
+        // Render the dynamic table footer.
+        echo $this->get_dynamic_table_html_end();
+    }
+
+    /**
+     * Hook that can be overridden in child classes to wrap a table in a form
+     * for example. Called only when there is data to display and not
+     * downloading.
+     */
+    public function wrap_html_finish() {
+        global $OUTPUT;
+        parent::wrap_html_finish();
+        echo "<br>";
+
+        $actionmenu = new \action_menu();
+        $actionmenu->add_secondary_action(
+                new \action_menu_link_secondary(
+                        new \moodle_url(''),
+                        new \pix_icon('e/tick', $this->strings['proceed']),
+                        $this->strings['proceed'],
+                        ['data-lifecycle-action' => 'proceed']
+                )
+        );
+
+        $actionmenu->add_secondary_action(
+                new \action_menu_link_secondary(
+                        new \moodle_url(''),
+                        new \pix_icon('e/undo', $this->strings['rollback']),
+                        $this->strings['rollback'],
+                        ['data-lifecycle-action' => 'rollback']
+                )
+        );
+
+        $actionmenu->set_menu_trigger(get_string('forselected', 'tool_lifecycle'));
+        echo $OUTPUT->render_action_menu($actionmenu);
+    }
+}
diff --git a/classes/plugininfo/lifecyclestep.php b/classes/plugininfo/lifecyclestep.php
index d8cf9b003c6f8ad1b258c722213b0ae2cbebd5e1..8fa5098a215105fa34064ce773e07b4ad7caede8 100644
--- a/classes/plugininfo/lifecyclestep.php
+++ b/classes/plugininfo/lifecyclestep.php
@@ -27,8 +27,6 @@ use core\plugininfo\base;
 use tool_lifecycle\local\manager\step_manager;
 use tool_lifecycle\local\manager\workflow_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Pluginfo for life cycle step
  *
diff --git a/classes/plugininfo/lifecycletrigger.php b/classes/plugininfo/lifecycletrigger.php
index 2258f64e59aeb5e3250f3d2ca7f2d38eb2fe29f3..62b7176c047835ff1dadaea56fb53841e511e70f 100644
--- a/classes/plugininfo/lifecycletrigger.php
+++ b/classes/plugininfo/lifecycletrigger.php
@@ -30,8 +30,6 @@ use tool_lifecycle\local\manager\trigger_manager;
 use tool_lifecycle\local\manager\workflow_manager;
 use tool_usertours\step;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Pluginfo for life cycle trigger
  *
diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php
index 9c506dbbff571788ea05b65428ce9cc59c0813b3..d320f46cd62b0cdcf93d05a6652aac5c095b4aeb 100644
--- a/classes/privacy/provider.php
+++ b/classes/privacy/provider.php
@@ -35,8 +35,6 @@ use core_privacy\local\request\writer;
 use tool_lifecycle\local\manager\step_manager;
 use tool_lifecycle\local\manager\workflow_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Implementation of the privacy subsystem plugin provider for the Life Cycle tool.
  *
diff --git a/classes/processor.php b/classes/processor.php
index f152055be9e81c931a4d79e87e48a60feb97674f..c1a185d9247afa4d361d0155954656f971d1c319 100644
--- a/classes/processor.php
+++ b/classes/processor.php
@@ -35,9 +35,6 @@ use tool_lifecycle\local\response\step_interactive_response;
 use tool_lifecycle\local\response\step_response;
 use tool_lifecycle\local\response\trigger_response;
 
-
-defined('MOODLE_INTERNAL') || die;
-
 /**
  * Offers functionality to trigger, process and finish lifecycle processes.
  *
@@ -119,10 +116,15 @@ class processor {
 
                 $step = step_manager::get_step_instance_by_workflow_index($process->workflowid, $process->stepindex);
                 $lib = lib_manager::get_step_lib($step->subpluginname);
-                if ($process->waiting) {
-                    $result = $lib->process_waiting_course($process->id, $step->id, $course);
-                } else {
-                    $result = $lib->process_course($process->id, $step->id, $course);
+                try {
+                    if ($process->waiting) {
+                        $result = $lib->process_waiting_course($process->id, $step->id, $course);
+                    } else {
+                        $result = $lib->process_course($process->id, $step->id, $course);
+                    }
+                } catch (\Exception $e) {
+                    process_manager::insert_process_error($process, $e);
+                    break;
                 }
                 if ($result == step_response::waiting()) {
                     process_manager::set_process_waiting($process);
@@ -220,7 +222,9 @@ class processor {
         $sql = 'SELECT {course}.* from {course} '.
             'left join {tool_lifecycle_process} '.
             'ON {course}.id = {tool_lifecycle_process}.courseid '.
-            'WHERE {tool_lifecycle_process}.courseid is null AND ' . $where;
+            'LEFT JOIN {tool_lifecycle_proc_error} pe ON {course}.id = pe.courseid ' .
+            'WHERE {tool_lifecycle_process}.courseid is null AND ' .
+            'pe.courseid IS NULL AND '. $where;
         return $DB->get_recordset_sql($sql, $whereparams);
     }
 
diff --git a/classes/settings_type.php b/classes/settings_type.php
index d63e3404a9608474abe5efcfe65dd45ef5b29671..33aa445a8ed94443e6c66f3ed7dde00f308f14b4 100644
--- a/classes/settings_type.php
+++ b/classes/settings_type.php
@@ -22,8 +22,6 @@
  */
 namespace tool_lifecycle;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Defines available settings_types.
  * @package    tool_lifecycle
diff --git a/classes/task/lifecycle_cleanup_task.php b/classes/task/lifecycle_cleanup_task.php
index 5c31f11bcc651b7e479cf6c0e77a779a62e77747..eadeadf11f8b4ae5d2719a853b5940fab86654d8 100644
--- a/classes/task/lifecycle_cleanup_task.php
+++ b/classes/task/lifecycle_cleanup_task.php
@@ -23,8 +23,6 @@
  */
 namespace tool_lifecycle\task;
 
-defined('MOODLE_INTERNAL') || die;
-
 /**
  * Scheduled task for cleanup past delays
  *
diff --git a/classes/task/lifecycle_error_notify_task.php b/classes/task/lifecycle_error_notify_task.php
new file mode 100644
index 0000000000000000000000000000000000000000..c20aee73f7bdf1a02478243ce7ddbd1c14dd5d2e
--- /dev/null
+++ b/classes/task/lifecycle_error_notify_task.php
@@ -0,0 +1,77 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Scheduled task for notify admin upon process errors
+ *
+ * @package tool_lifecycle
+ * @copyright  2022 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_lifecycle\task;
+
+/**
+ * Scheduled task for notify admin upon process errors
+ *
+ * @package tool_lifecycle
+ * @copyright  2022 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class lifecycle_error_notify_task extends \core\task\scheduled_task {
+
+    /**
+     * Get a descriptive name for this task (shown to admins).
+     *
+     * @return string
+     * @throws \coding_exception
+     */
+    public function get_name() {
+        return get_string('lifecycle_error_notify_task', 'tool_lifecycle');
+    }
+
+    /**
+     * Do the job.
+     */
+    public function execute() {
+        global $DB, $CFG;
+
+        $lastrun = get_config('tool_lifecycle', 'adminerrornotifylastrun');
+        if (!$lastrun) {
+            $lastrun = 0;
+        }
+
+        $currenttime = time();
+
+        $errorcount = $DB->count_records_select('tool_lifecycle_proc_error', 'errortimecreated > :lastrun',
+                ['lastrun' => $lastrun]);
+
+        set_config('adminerrornotifylastrun', $currenttime, 'tool_lifecycle');
+
+        if (!$errorcount) {
+            return;
+        }
+
+        $obj = new \stdClass();
+        $obj->amount = $errorcount;
+        $obj->url = $CFG->wwwroot . '/admin/tool/lifecycle/errors.php';
+
+        email_to_user(get_admin(), \core_user::get_noreply_user(),
+            get_string('notifyerrorsemailsubject', 'tool_lifecycle', $obj),
+            get_string('notifyerrorsemailcontent', 'tool_lifecycle', $obj),
+            get_string('notifyerrorsemailcontenthtml', 'tool_lifecycle', $obj),
+        );
+    }
+}
diff --git a/classes/task/lifecycle_task.php b/classes/task/lifecycle_task.php
index 57fe110f7f2af6f059b6cef7c558804aea6cc433..91692d9aa05e7016714d6f4237a3e5f5f773e48c 100644
--- a/classes/task/lifecycle_task.php
+++ b/classes/task/lifecycle_task.php
@@ -27,8 +27,6 @@ use tool_lifecycle\local\manager\lib_manager;
 use tool_lifecycle\local\manager\step_manager;
 use tool_lifecycle\processor;
 
-defined('MOODLE_INTERNAL') || die;
-
 /**
  * Scheduled task for working on lifecycle processes
  *
diff --git a/classes/view_controller.php b/classes/view_controller.php
index 015830a605445a16ab82fac9e0a0cb81200b836c..8a4f0779d9ee191676710506b3826f273cf53e45 100644
--- a/classes/view_controller.php
+++ b/classes/view_controller.php
@@ -32,9 +32,7 @@ use tool_lifecycle\local\manager\step_manager;
 use tool_lifecycle\local\manager\trigger_manager;
 use tool_lifecycle\local\table\interaction_log_table;
 use tool_lifecycle\local\table\interaction_remaining_table;
-use tool_lifecycle\local\table\interaction_attention_table;
-
-defined('MOODLE_INTERNAL') || die();
+use tool_lifecycle\local\table\interaction_attention_table;;
 
 /**
  * Controller for view.php
diff --git a/db/install.xml b/db/install.xml
index d6877a526fa7c2e2ef70adfb40008d09f4d4c12d..c8940a4b4c2c8bf445ea60f59337dcd699b2acdf 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="admin/tool/lifecycle/db" VERSION="20190822" COMMENT="XMLDB file for Moodle tool/lifecycle"
+<XMLDB PATH="admin/tool/lifecycle/db" VERSION="20211122" COMMENT="XMLDB file for Moodle tool/lifecycle"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
 >
@@ -146,5 +146,24 @@
         <KEY NAME="userid_fk" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
       </KEYS>
     </TABLE>
+    <TABLE NAME="tool_lifecycle_proc_error" COMMENT="table containing all errored lifecycle processes">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="20" NOTNULL="true" SEQUENCE="true" COMMENT="id of the process"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="course id"/>
+        <FIELD NAME="workflowid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="id of the workflow"/>
+        <FIELD NAME="stepindex" TYPE="int" LENGTH="5" NOTNULL="true" SEQUENCE="false" COMMENT="sortindex of the step within the workflow"/>
+        <FIELD NAME="waiting" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="tells if the process is in status waiting"/>
+        <FIELD NAME="timestepchanged" TYPE="int" LENGTH="11" NOTNULL="true" SEQUENCE="false" COMMENT="unix timestamp - time the step instance of the process was changed last."/>
+        <FIELD NAME="errormessage" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Message of the error"/>
+        <FIELD NAME="errortrace" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="errorhash" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Where the error occured in the form 'path/to/filename.php:line'"/>
+        <FIELD NAME="errortimecreated" TYPE="int" LENGTH="11" NOTNULL="true" SEQUENCE="false" COMMENT="unix timestamp - time the error occured"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="courseid_fk" TYPE="foreign-unique" FIELDS="courseid" REFTABLE="course" REFFIELDS="id" COMMENT="Foreign key on course table"/>
+        <KEY NAME="workflowid_fk" TYPE="foreign" FIELDS="workflowid" REFTABLE="tool_lifecycle_workflow" REFFIELDS="id"/>
+      </KEYS>
+    </TABLE>
   </TABLES>
 </XMLDB>
\ No newline at end of file
diff --git a/db/tasks.php b/db/tasks.php
index f708e82cbe5ce7aa4347e05f7cec5acf579a3842..d061305a9538a25298d8ca90ba16e938ed48324f 100644
--- a/db/tasks.php
+++ b/db/tasks.php
@@ -44,5 +44,14 @@ $tasks = array(
         'day' => '*',
         'month' => '*',
         'dayofweek' => '0',
-    )
+    ),
+    array(
+        'classname' => 'tool_lifecycle\task\lifecycle_error_notify_task',
+        'blocking' => 0,
+        'minute' => 'R',
+        'hour' => '5',
+        'day' => '*',
+        'month' => '*',
+        'dayofweek' => '0',
+    ),
 );
diff --git a/db/upgrade.php b/db/upgrade.php
index 1e5a172354a00cdabea132fb5926a5b7746187fc..99accd3e4c9c3d589ea5f591be01bf2bb027f154 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -22,8 +22,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Update script for tool_lifecycle.
  * @param int $oldversion Version id of the previously installed version.
@@ -446,5 +444,36 @@ function xmldb_tool_lifecycle_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2020091800, 'tool', 'lifecycle');
     }
 
+    if ($oldversion < 2021112300) {
+
+        // Define table tool_lifecycle_proc_error to be created.
+        $table = new xmldb_table('tool_lifecycle_proc_error');
+
+        // Adding fields to table tool_lifecycle_proc_error.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('courseid', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('workflowid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('stepindex', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('waiting', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timestepchanged', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('errormessage', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
+        $table->add_field('errortrace', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
+        $table->add_field('errorhash', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('errortimecreated', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, null);
+
+        // Adding keys to table tool_lifecycle_proc_error.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
+        $table->add_key('courseid_fk', XMLDB_KEY_FOREIGN_UNIQUE, ['courseid'], 'course', ['id']);
+        $table->add_key('workflowid_fk', XMLDB_KEY_FOREIGN, ['workflowid'], 'tool_lifecycle_workflow', ['id']);
+
+        // Conditionally launch create table for tool_lifecycle_proc_error.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Lifecycle savepoint reached.
+        upgrade_plugin_savepoint(true, 2021112300, 'tool', 'lifecycle');
+    }
+
     return true;
 }
diff --git a/errors.php b/errors.php
new file mode 100644
index 0000000000000000000000000000000000000000..40c80bccf471728ec59bca390772f38eb9a26469
--- /dev/null
+++ b/errors.php
@@ -0,0 +1,70 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Displays the process errors
+ *
+ * @package tool_lifecycle
+ * @copyright  2021 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_lifecycle\local\manager\process_manager;
+use tool_lifecycle\local\table\process_errors_table;
+
+require_once(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+$PAGE->set_context(context_system::instance());
+require_login();
+require_capability('moodle/site:config', context_system::instance());
+
+admin_externalpage_setup('tool_lifecycle_process_errors');
+
+$PAGE->set_url(new \moodle_url('/admin/tool/lifecycle/errors.php'));
+
+// Action handling (delete, bulk-delete).
+$action = optional_param('action', null, PARAM_ALPHANUMEXT);
+if ($action) {
+    global $DB;
+    require_sesskey();
+    $ids = required_param_array('id', PARAM_INT);
+    if ($action == 'proceed') {
+        foreach ($ids as $id) {
+            process_manager::proceed_process_after_error($id);
+        }
+    } else if ($action == 'rollback') {
+        foreach ($ids as $id) {
+            process_manager::rollback_process_after_error($id);
+        }
+    } else {
+        throw new coding_exception("action must be either 'proceed' or 'rollback'");
+    }
+    redirect($PAGE->url);
+}
+
+$PAGE->set_title(get_string('process_errors_header', 'tool_lifecycle'));
+$PAGE->set_heading(get_string('process_errors_header', 'tool_lifecycle'));
+
+$table = new process_errors_table();
+$table->define_baseurl($PAGE->url);
+
+$PAGE->requires->js_call_amd('tool_lifecycle/tablebulkactions', 'init');
+
+echo $OUTPUT->header();
+$table->out(100, false);
+
+echo $OUTPUT->footer();
diff --git a/lang/de/tool_lifecycle.php b/lang/de/tool_lifecycle.php
index 5ce7b49b0715151ca750421d97f4260c13bd8d4a..fa77816ec52ad1a4da05b7a4bdffd9fba24a66de 100644
--- a/lang/de/tool_lifecycle.php
+++ b/lang/de/tool_lifecycle.php
@@ -175,3 +175,14 @@ $string['restore_trigger_does_not_exist'] = 'Der Trigger {$a} ist nicht installi
 $string['process_triggered_event'] = 'Ein Prozess wurde ausgelöst';
 $string['process_proceeded_event'] = 'Ein Prozess wurde fortgeführt';
 $string['process_rollback_event'] = 'Ein Prozess wurde zurückgesetzt';
+
+$string['courseid'] = 'Kurs-ID';
+$string['process_errors_header'] = 'Fehlermanagement';
+$string['proceed'] = 'Fortfahren';
+$string['forselected'] = 'Für alle ausgewählten Prozesse';
+$string['noprocesserrors'] = 'Es gibt keine fehlerhaften Prozesse, die behandelt werden müssen!';
+
+$string['lifecycle_error_notify_task'] = 'Benachrichtigt die Administratoren bei Fehlern in tool_lifecycle-Prozessen.';
+$string['notifyerrorsemailsubject'] = '{$a->amount} neue fehlerhafte tool_lifecycle Prozesse warten darauf, behandelt zu werden!';
+$string['notifyerrorsemailcontent'] = '{$a->amount} neue fehlerhafte tool_lifecycle Prozesse warten darauf, behandelt zu werden!' . "\n" . 'Bitte besuchen Sie {$a->url}.';
+$string['notifyerrorsemailcontenthtml'] = '{$a->amount} neue fehlerhafte tool_lifecycle Prozesse warten darauf, behandelt zu werden!<br>Bitte besuchen Sie <a href="{$a->url}">die Übersichtsseite</a>.';
diff --git a/lang/en/tool_lifecycle.php b/lang/en/tool_lifecycle.php
index dcf69f46e01aea6dc83a4e46ed13eb963e59a36f..420eb2665200454727ca2c6d5dc333c430b81316 100644
--- a/lang/en/tool_lifecycle.php
+++ b/lang/en/tool_lifecycle.php
@@ -210,3 +210,15 @@ $string['all_delays'] = 'All delays';
 $string['globally'] = 'Global delays';
 $string['delays_for_workflow'] = 'Delays for "{$a}"';
 $string['delete_all_delays'] = 'Delete all delays';
+
+$string['courseid'] = 'Course ID';
+$string['process_errors_header'] = 'Error handling';
+$string['proceed'] = 'Proceed';
+$string['rollback'] = 'Rollback';
+$string['forselected'] = 'For all selected processes';
+$string['noprocesserrors'] = 'There are no process errors to handle!';
+
+$string['lifecycle_error_notify_task'] = 'Notify the admin upon errors in tool_lifecycle processes';
+$string['notifyerrorsemailsubject'] = 'There are {$a->amount} new tool_lifecycle process errors waiting to be fixed!';
+$string['notifyerrorsemailcontent'] = 'There are {$a->amount} new tool_lifecycle process errors waiting to be fixed!' . "\n" . 'Please review them at {$a->url}.';
+$string['notifyerrorsemailcontenthtml'] = 'There are {$a->amount} new tool_lifecycle process errors waiting to be fixed!<br>Please review them at the <a href="{$a->url}">error handling overview</a>.';
diff --git a/lib.php b/lib.php
index 15820fb22191520f21b657c25417b148ea672e28..fa4532aec73b8b264e1b9030c9b3109285b48bcc 100644
--- a/lib.php
+++ b/lib.php
@@ -21,8 +21,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Adds a tool_lifecycle link to the course admin menu.
  *
diff --git a/renderer.php b/renderer.php
index 1b1ee40599f2a2c88d052607884b8cbf6fe55bcb..d332cb3fb95b0676d4d53b78b746085d37069db7 100644
--- a/renderer.php
+++ b/renderer.php
@@ -21,7 +21,6 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 /**
  * Renderer for life cycle
diff --git a/settings.php b/settings.php
index 71a26f829a060b2c7f39bffabce1824c70c10f15..ce6ad36f9395ad5b095bd4093de8c40272881ed1 100644
--- a/settings.php
+++ b/settings.php
@@ -50,6 +50,9 @@ if ($hassiteconfig) {
     $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_delayed_courses',
             get_string('delayed_courses_header', 'tool_lifecycle'),
             new moodle_url('/admin/tool/lifecycle/delayedcourses.php')));
+    $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_process_errors',
+        get_string('process_errors_header', 'tool_lifecycle'),
+        new moodle_url('/admin/tool/lifecycle/errors.php')));
 
     if ($ADMIN->fulltree) {
         $triggers = core_component::get_plugin_list('lifecycletrigger');
diff --git a/step/createbackup/db/upgrade.php b/step/createbackup/db/upgrade.php
index bfa92b839c48758c2d411cfdddee221c2c612813..d1583cd7ee90282436fb491f7b3cb0c54ff996c9 100644
--- a/step/createbackup/db/upgrade.php
+++ b/step/createbackup/db/upgrade.php
@@ -25,8 +25,6 @@
 use tool_lifecycle\local\manager\settings_manager;
 use tool_lifecycle\local\manager\step_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Update script for lifecycles subplugin createbackup.
  * @param int $oldversion Version id of the previously installed version.
diff --git a/step/deletecourse/db/upgrade.php b/step/deletecourse/db/upgrade.php
index 668a910e9621226fc85662b7eb4b48d522b1b301..c8467c5ef6473f293feee85f55c0f16112c9ece9 100644
--- a/step/deletecourse/db/upgrade.php
+++ b/step/deletecourse/db/upgrade.php
@@ -25,8 +25,6 @@
 use tool_lifecycle\local\manager\settings_manager;
 use tool_lifecycle\local\manager\step_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Update script for lifecycles subplugin deletecourse
  *
diff --git a/step/interactionlib.php b/step/interactionlib.php
index 0958cb165123df3882024d45e2104dedbe23ba63..a4909a52a1fe2c6e441a9b04992e3c233e72a215 100644
--- a/step/interactionlib.php
+++ b/step/interactionlib.php
@@ -31,8 +31,6 @@ use tool_lifecycle\local\entity\process;
 use tool_lifecycle\local\entity\step_subplugin;
 use tool_lifecycle\local\response\step_interactive_response;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Interface for the interactions of the subplugintype step.
  *
diff --git a/tests/backup_manager_test.php b/tests/backup_manager_test.php
index ab27a38133a40cd28e94f0f9b0868693c12f038a..fa6a79675960d3ee6960289e87d9519941d7aa3f 100644
--- a/tests/backup_manager_test.php
+++ b/tests/backup_manager_test.php
@@ -22,7 +22,6 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\manager\backup_manager;
 
diff --git a/tests/generator/lib.php b/tests/generator/lib.php
index 35a4ac7eccdba127fa24efea6e930463e47308e3..ae963a13385b0d985490c947b081558e4bef0f26 100644
--- a/tests/generator/lib.php
+++ b/tests/generator/lib.php
@@ -22,7 +22,6 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\process;
 use tool_lifecycle\local\entity\trigger_subplugin;
diff --git a/tests/manual_trigger_tools_test.php b/tests/manual_trigger_tools_test.php
index aad0a0268ae7b0b6a68823d3ed6b2c3108337e5a..3477d198f598c375a870ea7336797a06d27808ea 100644
--- a/tests/manual_trigger_tools_test.php
+++ b/tests/manual_trigger_tools_test.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tobias Reischmann, Jan Dageforde WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\action;
 use tool_lifecycle\local\entity\workflow;
diff --git a/tests/process_error_test.php b/tests/process_error_test.php
new file mode 100644
index 0000000000000000000000000000000000000000..5626b899dd68dfc3818dc5d6353a7b173207fd90
--- /dev/null
+++ b/tests/process_error_test.php
@@ -0,0 +1,116 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Checks whether process errors are properly inserted into the table.
+ *
+ * @package    tool_lifecycle
+ * @category   test
+ * @group      tool_lifecycle
+ * @copyright  2022 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_lifecycle;
+
+use tool_lifecycle\local\entity\trigger_subplugin;
+use tool_lifecycle\local\manager\settings_manager;
+use tool_lifecycle\local\manager\workflow_manager;
+use tool_lifecycle\local\manager\trigger_manager;
+use tool_lifecycle\local\manager\process_manager;
+
+/**
+ * Checks whether process errors are properly inserted into the table.
+ *
+ * @package    tool_lifecycle
+ * @category   test
+ * @group      tool_lifecycle
+ * @copyright  2022 Justus Dieckmann WWU
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class process_error_test extends \advanced_testcase {
+    /** Icon of the manual trigger. */
+    const MANUAL_TRIGGER1_ICON = 't/up';
+    /** Display name of the manual trigger. */
+    const MANUAL_TRIGGER1_DISPLAYNAME = 'Up';
+    /** Capability of the manual trigger. */
+    const MANUAL_TRIGGER1_CAPABILITY = 'moodle/course:manageactivities';
+
+
+    /** @var trigger_subplugin $trigger Instances of the triggers under test. */
+    private $trigger;
+    /** @var array $course Instance of the course under test. */
+    private $course;
+
+    /**
+     * Setup the testcase.
+     * @throws \coding_exception
+     * @throws \moodle_exception
+     */
+    public function setUp() : void {
+        global $USER, $DB;
+
+        // We do not need a sesskey check in theses tests.
+        $USER->ignoresesskey = true;
+
+        $this->resetAfterTest(true);
+        $generator = $this->getDataGenerator()->get_plugin_generator('tool_lifecycle');
+        $triggersettings = new \stdClass();
+        $triggersettings->icon = self::MANUAL_TRIGGER1_ICON;
+        $triggersettings->displayname = self::MANUAL_TRIGGER1_DISPLAYNAME;
+        $triggersettings->capability = self::MANUAL_TRIGGER1_CAPABILITY;
+        $manualworkflow = $generator->create_manual_workflow($triggersettings);
+        $step = $generator->create_step("instance1", "deletecourse", $manualworkflow->id);
+        settings_manager::save_settings($step->id, settings_type::STEP, "deletecourse",
+                array("maximumdeletionspercron" => 10)
+        );
+
+        workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $manualworkflow->id);
+
+        $this->course = $this->getDataGenerator()->create_course();
+        $this->getDataGenerator()->create_module('page', ['course' => $this->course->id]);
+
+        // Corrupt course.
+        $DB->execute('UPDATE {course_modules} SET instance = 0');
+        $this->trigger = trigger_manager::get_triggers_for_workflow($manualworkflow->id)[0];
+    }
+
+    /**
+     * Test if the correct process error was put into the table.
+     */
+    public function test_process_error_in_table() {
+        global $DB;
+        $process = process_manager::manually_trigger_process($this->course->id, $this->trigger->id);
+
+        // The delete course step really wants to print output.
+        ob_start();
+        $processor = new processor();
+        $processor->process_courses();
+        ob_end_clean();
+
+        $records = $DB->get_records('tool_lifecycle_proc_error');
+
+        $this->assertEquals(1, count($records));
+        $this->assertEquals(0, $DB->count_records('tool_lifecycle_process'));
+
+        $record = reset($records);
+
+        $this->assertEquals($this->course->id, $record->courseid);
+        $this->assertStringContainsString("Trying to get property 'id' of non-object", $record->errormessage);
+        $this->assertEquals($process->id, $record->id);
+    }
+
+}
diff --git a/tests/process_status_message_test.php b/tests/process_status_message_test.php
index e55fbe9ffb466d164207da8d4a0697912294c532..5ac9756ce3c1c69d785b9cce9f1f2fcbe80e514f 100644
--- a/tests/process_status_message_test.php
+++ b/tests/process_status_message_test.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tamara Gunkel, Jan Dageforde WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\action;
 use tool_lifecycle\local\entity\workflow;
diff --git a/tests/settings_manager_test.php b/tests/settings_manager_test.php
index 4444e33550f7048c18419133bd30aa4a61506bfb..a6c389f6a47124c268455eec79a76eb325148b8e 100644
--- a/tests/settings_manager_test.php
+++ b/tests/settings_manager_test.php
@@ -22,7 +22,6 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\step_subplugin;
 use tool_lifecycle\local\entity\trigger_subplugin;
diff --git a/trigger/categories/tests/generator/lib.php b/trigger/categories/tests/generator/lib.php
index 8d3bb50f763855c9d3b6796a7bd8447a89727f68..c8d410eca36623deaed95a623800dbe0c0a30ce2 100644
--- a/trigger/categories/tests/generator/lib.php
+++ b/trigger/categories/tests/generator/lib.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
diff --git a/trigger/categories/tests/trigger_test.php b/trigger/categories/tests/trigger_test.php
index 79f9e5fd638779fc54e6db645fbd03b444ade9df..077149e435340a2ff127c58ff7853154255f9ddd 100644
--- a/trigger/categories/tests/trigger_test.php
+++ b/trigger/categories/tests/trigger_test.php
@@ -22,7 +22,7 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-namespace tool_lifecycle\trigger;
+namespace lifecycletrigger_categories;
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\processor;
@@ -41,7 +41,7 @@ require_once(__DIR__ . '/generator/lib.php');
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_lifecycle_trigger_categories_testcase extends \advanced_testcase {
+class trigger_test extends \advanced_testcase {
 
     /** @var trigger_subplugin $excludetrigger Trigger instance that excludes a category. */
     private $excludetrigger;
diff --git a/trigger/delayedcourses/db/install.php b/trigger/delayedcourses/db/install.php
index 00f52e2f13d94d0134ff4694951a0a81c8a65540..d75f53f3b87c4e0dde9c1ce0517f432bd3f5f8c5 100644
--- a/trigger/delayedcourses/db/install.php
+++ b/trigger/delayedcourses/db/install.php
@@ -24,8 +24,6 @@
  */
 use tool_lifecycle\local\manager\trigger_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Register delayed courses as initial workflow.
  */
diff --git a/trigger/delayedcourses/tests/generator/lib.php b/trigger/delayedcourses/tests/generator/lib.php
index 1561e1dcf7ec94af3350c671f29a45db6ba89db8..b3cfb2ddb759dd535feea1cab7d51305805fa0af 100644
--- a/trigger/delayedcourses/tests/generator/lib.php
+++ b/trigger/delayedcourses/tests/generator/lib.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
diff --git a/trigger/delayedcourses/tests/trigger_test.php b/trigger/delayedcourses/tests/trigger_test.php
index fcb5e7da371acbafd591f481102f3a4bff3ae06c..89dd51d333de703c52b93909ac5ee19dc6c609c0 100644
--- a/trigger/delayedcourses/tests/trigger_test.php
+++ b/trigger/delayedcourses/tests/trigger_test.php
@@ -22,7 +22,7 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-namespace tool_lifecycle\trigger;
+namespace lifecycletrigger_delayedcourses;
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
@@ -43,7 +43,7 @@ require_once(__DIR__ . '/generator/lib.php');
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_lifecycle_trigger_delayedcourses_testcase extends \advanced_testcase {
+class trigger_test extends \advanced_testcase {
 
     /** @var trigger_subplugin $triggerinstance Instance of the delayedcourses trigger. */
     private $triggerinstance;
diff --git a/trigger/sitecourse/db/install.php b/trigger/sitecourse/db/install.php
index ef92e5e4632960d6bc1707e482e5eda9fdfcb954..c026243b7d724b1df90cad31085e3e7537796efc 100644
--- a/trigger/sitecourse/db/install.php
+++ b/trigger/sitecourse/db/install.php
@@ -23,8 +23,6 @@
  */
 use tool_lifecycle\local\manager\trigger_manager;
 
-defined('MOODLE_INTERNAL') || die();
-
 /**
  * Register site course trigger exclusion as initial workflow.
  */
diff --git a/trigger/sitecourse/tests/generator/lib.php b/trigger/sitecourse/tests/generator/lib.php
index 4942d8c675c2d326d5d22d310a9ec8368a7b5bef..6932024240770b1287578979b31ec0493cfcb50e 100644
--- a/trigger/sitecourse/tests/generator/lib.php
+++ b/trigger/sitecourse/tests/generator/lib.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
diff --git a/trigger/sitecourse/tests/trigger_test.php b/trigger/sitecourse/tests/trigger_test.php
index d5f602b77ca7a5b71d477666dba87ae6a317f097..655ccfa77261dd84b77f57cfbc47a55e399d67dc 100644
--- a/trigger/sitecourse/tests/trigger_test.php
+++ b/trigger/sitecourse/tests/trigger_test.php
@@ -22,10 +22,11 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-namespace tool_lifecycle\trigger;
+namespace lifecycletrigger_sitecourse;
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\response\trigger_response;
+use tool_lifecycle\trigger\sitecourse;
 
 defined('MOODLE_INTERNAL') || die();
 
@@ -40,7 +41,7 @@ require_once(__DIR__ . '/generator/lib.php');
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_lifecycle_trigger_sitecourse_testcase extends \advanced_testcase {
+class trigger_test extends \advanced_testcase {
 
     /** @var trigger_subplugin $triggerinstance Instance of the trigger. */
     private $triggerinstance;
diff --git a/trigger/startdatedelay/tests/generator/lib.php b/trigger/startdatedelay/tests/generator/lib.php
index c3bd5cee91680efb0410db4ee475d627d9f0cdf0..71291d5269c09f2243d2df39ab11884c9db12d57 100644
--- a/trigger/startdatedelay/tests/generator/lib.php
+++ b/trigger/startdatedelay/tests/generator/lib.php
@@ -22,7 +22,6 @@
  * @copyright  2018 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-defined('MOODLE_INTERNAL') || die();
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\local\entity\workflow;
diff --git a/trigger/startdatedelay/tests/trigger_test.php b/trigger/startdatedelay/tests/trigger_test.php
index 7466dd9b3e8652d449e6aae21518ff24cb69db12..6ca598fd0c48cea2baf916ff65ddea74502a1873 100644
--- a/trigger/startdatedelay/tests/trigger_test.php
+++ b/trigger/startdatedelay/tests/trigger_test.php
@@ -22,7 +22,7 @@
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-namespace tool_lifecycle\trigger;
+namespace lifecycletrigger_startdatedelay;
 
 use tool_lifecycle\local\entity\trigger_subplugin;
 use tool_lifecycle\processor;
@@ -40,7 +40,7 @@ require_once(__DIR__ . '/generator/lib.php');
  * @copyright  2017 Tobias Reischmann WWU
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_lifecycle_trigger_startdatedelay_testcase extends \advanced_testcase {
+class trigger_test extends \advanced_testcase {
 
     /** @var $triggerinstance trigger_subplugin Instance of the trigger. */
     private $triggerinstance;
diff --git a/version.php b/version.php
index 46092b8c42cf5b5f3c379d2bad970be330eb3819..814e1fb9f09a02bec29cfd2f6c825ab7706c1860 100644
--- a/version.php
+++ b/version.php
@@ -25,7 +25,7 @@
 defined('MOODLE_INTERNAL') || die;
 
 $plugin->maturity = MATURITY_BETA;
-$plugin->version  = 2021051700;
+$plugin->version  = 2021112300;
 $plugin->component = 'tool_lifecycle';
 $plugin->requires = 2017111300; // Require Moodle 3.4 (or above).
 $plugin->release = 'v3.11-r1';