├── pix └── catalyst-logo.png ├── guzzle ├── GuzzleHttp │ ├── Exception │ │ ├── GuzzleException.php │ │ ├── TooManyRedirectsException.php │ │ ├── TransferException.php │ │ ├── ClientException.php │ │ ├── ServerException.php │ │ ├── SeekException.php │ │ ├── ConnectException.php │ │ └── BadResponseException.php │ ├── functions_include.php │ ├── Psr7 │ │ ├── functions_include.php │ │ ├── NoSeekStream.php │ │ ├── LazyOpenStream.php │ │ ├── DroppingStream.php │ │ ├── InflateStream.php │ │ ├── StreamWrapper.php │ │ ├── BufferStream.php │ │ ├── StreamDecoratorTrait.php │ │ ├── Request.php │ │ ├── FnStream.php │ │ ├── Response.php │ │ ├── PumpStream.php │ │ ├── CachingStream.php │ │ └── LimitStream.php │ ├── Promise │ │ ├── functions_include.php │ │ ├── CancellationException.php │ │ ├── PromisorInterface.php │ │ ├── AggregateException.php │ │ ├── TaskQueueInterface.php │ │ ├── RejectionException.php │ │ ├── TaskQueue.php │ │ ├── FulfilledPromise.php │ │ ├── RejectedPromise.php │ │ ├── PromiseInterface.php │ │ └── Coroutine.php │ ├── Handler │ │ ├── CurlFactoryInterface.php │ │ ├── CurlHandler.php │ │ ├── Proxy.php │ │ └── EasyHandle.php │ ├── Cookie │ │ ├── SessionCookieJar.php │ │ ├── FileCookieJar.php │ │ └── CookieJarInterface.php │ ├── ClientInterface.php │ ├── PrepareBodyMiddleware.php │ ├── TransferStats.php │ └── RetryMiddleware.php ├── readme_moodle.txt ├── LICENSE ├── Psr │ └── Http │ │ └── Message │ │ └── ResponseInterface.php └── README.md ├── thirdpartylibs.xml ├── styles.css ├── tests ├── fixtures │ ├── Test_login_failed_workflow_20180826_0058.json │ └── user_event_fixture.php ├── fail_filter_step_test.php ├── tool_trigger_testcase.php ├── logdump_action_step_test.php └── json_export_test.php ├── db ├── messages.php ├── events.php ├── install.php ├── caches.php ├── access.php ├── tasks.php └── services.php ├── sample_workflows ├── Login_Failure_Logging_20180905_0706.json ├── User_enrolment_20180905_0706.json └── Course_Completed_20180905_0706.json ├── amd └── build │ ├── import_workflow.min.js │ └── step_select.min.js ├── version.php ├── export.php ├── classes ├── task │ ├── learn.php │ ├── cleanup_history.php │ └── cleanup.php ├── steps │ ├── actions │ │ ├── base_action_step.php │ │ ├── logdump_action_step.php │ │ └── assign_cohort_action_step.php │ ├── filters │ │ ├── base_filter_step.php │ │ └── fail_filter_step.php │ └── lookups │ │ └── base_lookup_step.php ├── workflow.php ├── helper │ └── processor_helper.php └── import_form.php ├── manage.php ├── templates ├── trigger_fields.mustache └── workflow_steps.mustache ├── README.md ├── settings.php ├── cli ├── export_fields.php └── import_csv_records.php ├── .travis.yml ├── stepdetails.php ├── manageworkflow.php └── edit.php /pix/catalyst-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marinaglancy/moodle-tool_trigger/master/pix/catalyst-logo.png -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Exception/GuzzleException.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | guzzle 5 | Guzzle, an extensible PHP HTTP client 6 | 6.3.0 7 | MIT 8 | 1.0 9 | 10 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .tool-trigger-step-moveup, 2 | .tool-trigger-step-movedown, 3 | .tool-trigger-step-edit, 4 | .tool-trigger-step-delete { 5 | cursor: pointer; 6 | } 7 | 8 | .tool-trigger-initial-hidden { 9 | visibility: hidden; 10 | } 11 | 12 | .fieldtitle { 13 | width: 100%; 14 | display: block; 15 | font-size: .85em; 16 | } 17 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/PromisorInterface.php: -------------------------------------------------------------------------------- 1 | stream = $stream; 16 | $msg = $msg ?: 'Could not seek the stream to position ' . $pos; 17 | parent::__construct($msg); 18 | } 19 | 20 | /** 21 | * @return StreamInterface 22 | */ 23 | public function getStream() 24 | { 25 | return $this->stream; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Handler/CurlFactoryInterface.php: -------------------------------------------------------------------------------- 1 | A workflow to use as a test fixture for the worklfow import process.<\\\/p>

It is triggered on a user login failed event
<\\\/p>\",\"format\":\"1\"}","event":"\\core\\event\\user_login_failed","steps":{"46":{"id":"46","name":"Test fixture user lookup","description":"A user step that gets user profile imformation","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\user_lookup_step","data":"{\"useridfield\":\"userid\",\"outputprefix\":\"user_\",\"nodeleted\":\"1\",\"stepdesc\":\"User lookup\",\"typedesc\":\"Lookup\"}","steporder":"0"},"47":{"id":"47","name":"Test fixture cron log","description":"A step that dumps workflow output to the cron log.","type":"actions","stepclass":"\\tool_trigger\\steps\\actions\\logdump_action_step","data":"{\"stepdesc\":\"Cron log\",\"typedesc\":\"Action\"}","steporder":"1"}},"moodleversion":"2018082400","pluginversion":"2018082401"} -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/LazyOpenStream.php: -------------------------------------------------------------------------------- 1 | filename = $filename; 27 | $this->mode = $mode; 28 | } 29 | 30 | /** 31 | * Creates the underlying stream lazily when required. 32 | * 33 | * @return StreamInterface 34 | */ 35 | protected function createStream() 36 | { 37 | return stream_for(try_fopen($this->filename, $this->mode)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /guzzle/readme_moodle.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run this script from the guzzle folder to update Guzzle 3 | # Update version.php verion and release 4 | # Manually test that the GUzzle can be used with no issues. 5 | 6 | GUZZLEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | 8 | echo Enter version number of Guzzle to update to. e.g. 6.3.0 9 | read varversion 10 | 11 | 12 | find $GUZZLEDIR -mindepth 1 -maxdepth 1 | grep -v readme_moodle.txt | xargs rm -rf 13 | wget -O $GUZZLEDIR/guzzle.zip "https://github.com/guzzle/guzzle/releases/download/$varversion/guzzle.zip" 14 | unzip $GUZZLEDIR/guzzle.zip -d $GUZZLEDIR 15 | sed -i -e 's/require/require_once/g' $GUZZLEDIR/autoloader.php 16 | sed -i -e 's#GuzzleHttp/functions.php#GuzzleHttp/functions_include.php#g' $GUZZLEDIR/autoloader.php 17 | sed -i -e 's#GuzzleHttp/Psr7/functions.php#GuzzleHttp/Psr7/functions_include.php#g' $GUZZLEDIR/autoloader.php 18 | sed -i -e 's#GuzzleHttp/Promise/functions.php#GuzzleHttp/Promise/functions_include.php#g' $GUZZLEDIR/autoloader.php 19 | rm $GUZZLEDIR/guzzle.zip 20 | echo 21 | echo 'Go update version.php' 22 | echo $GUZZLEDIR 23 | -------------------------------------------------------------------------------- /db/messages.php: -------------------------------------------------------------------------------- 1 | . 16 | /** 17 | * Defines message providers (types of message sent) for the Re-engagement activity. 18 | * 19 | * @package tool_trigger 20 | * @author Dan Marsden 21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 | */ 23 | 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | $messageproviders = array( 27 | 'tool_trigger' => array(), 28 | ); -------------------------------------------------------------------------------- /guzzle/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2016 Michael Dowling, https://github.com/mtdowling 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /db/events.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file definies observers needed by the tool. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | // List of observers. 28 | $observers = array( 29 | array( 30 | 'eventname' => '*', 31 | 'callback' => '\tool_trigger\event_processor::process_event', 32 | ), 33 | ); 34 | -------------------------------------------------------------------------------- /sample_workflows/Login_Failure_Logging_20180905_0706.json: -------------------------------------------------------------------------------- 1 | {"name":"Login Failure Logging","description":"{\"text\":\"

This workflow sends details of failed login attempts to an external system via HTTP Post.
<\\\/p>\",\"format\":\"1\"}","event":"\\core\\event\\user_login_failed","steps":{"96":{"id":"96","name":"User Lookup","description":"Lookup the details of the user who failed to login.","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\user_lookup_step","data":"{\"useridfield\":\"userid\",\"outputprefix\":\"user_\",\"nodeleted\":\"1\",\"stepdesc\":\"User lookup\",\"typedesc\":\"Lookup\"}","steporder":"0"},"98":{"id":"98","name":"HTTP Post","description":"Let an external system know about the failed login for this user.\r\nYou will need to update the URL to the correct one for your setup.","type":"actions","stepclass":"\\tool_trigger\\steps\\actions\\http_post_action_step","data":"{\"url\":\"http:\\\/\\\/localhost:8000\\\/yj323byj\",\"httpheaders\":\"Content-Type: application\\\/json\\r\\nAccept: application\\\/json\",\"httpparams\":\"user[0]={user_id}&user[1]={user_username}&user[2]={user_firstname}&user[3]={user_lastname}\",\"jsonencode\":\"1\",\"stepdesc\":\"HTTP Post\",\"typedesc\":\"Action\"}","steporder":"1"}},"moodleversion":"2018083100","pluginversion":"2018082401"} -------------------------------------------------------------------------------- /amd/build/import_workflow.min.js: -------------------------------------------------------------------------------- 1 | define ("tool_trigger/import_workflow",["jquery","core/str","core/modal_factory","core/modal_events","core/templates","core/ajax","core/fragment","core/notification"],function(a,b,c,d,e,f,g,h){var k={},l,m,n="

Loading...

";function i(){var a={jsonformdata:JSON.stringify({})};m.setBody(n);m.setBody(g.loadFragment("tool_trigger","new_import_form",l,a))}function j(a){a.preventDefault();var c=m.getRoot().find("form").serialize();m.setBody(n);f.call([{methodname:"tool_trigger_process_import_form",args:{jsonformdata:JSON.stringify(c)}}])[0].done(function(a){var b=JSON.parse(a);if("success"==b.errorcode){location.reload(!0)}else{Object.keys(b.message).forEach(function(a){h.addNotification({message:b.message[a],type:"error"})})}m.hide()}).fail(function(){h.addNotification({message:b.get_string("errorimportworkflow","tool_trigger"),type:"error"});m.hide()})}k.init=function(e){l=e;b.get_string("importmodaltitle","tool_trigger").then(function(b){c.create({type:c.types.SAVE_CANCEL,title:b,body:n,large:!0},a("[name=importbtn]")).done(function(a){m=a;m.getRoot().on(d.save,j);m.getRoot().on(d.hidden,i);i()})})};return k}); 2 | //# sourceMappingURL=import_workflow.min.js.map 3 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/DroppingStream.php: -------------------------------------------------------------------------------- 1 | stream = $stream; 23 | $this->maxLength = $maxLength; 24 | } 25 | 26 | public function write($string) 27 | { 28 | $diff = $this->maxLength - $this->stream->getSize(); 29 | 30 | // Begin returning 0 when the underlying stream is too large. 31 | if ($diff <= 0) { 32 | return 0; 33 | } 34 | 35 | // Write the stream or a subset of the stream if needed. 36 | if (strlen($string) < $diff) { 37 | return $this->stream->write($string); 38 | } 39 | 40 | return $this->stream->write(substr($string, 0, $diff)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin version and other meta-data are defined here. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $plugin->component = 'tool_trigger'; 28 | $plugin->release = 2020072800; 29 | $plugin->version = 2020072800; 30 | $plugin->requires = 2016052300; 31 | $plugin->maturity = MATURITY_BETA; 32 | $plugin->dependencies = array('tool_monitor' => 2015051101); 33 | -------------------------------------------------------------------------------- /db/install.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Post installation steps for trigger plugin. 19 | * 20 | * @package tool_trigger 21 | * @copyright Catalyst IT 22 | * @author Matt Porritt 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | /** 29 | * Add events fields from fixture file to database. 30 | */ 31 | function xmldb_tool_trigger_install() { 32 | $learnprocess = new \tool_trigger\learn_process(); 33 | $learnprocess->process_fixtures(); 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /db/caches.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Trigger cache definitions. 19 | * 20 | * @package tool_trigger 21 | * @copyright 2018 Catalyst IT 22 | * @author Dan Marsden 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | $definitions = array( 29 | 'eventsubscriptions' => array( 30 | 'mode' => cache_store::MODE_APPLICATION, 31 | 'simplekeys' => true, 32 | 'simpledata' => true, 33 | 'staticacceleration' => true, 34 | 'staticaccelerationsize' => 30 35 | ) 36 | ); 37 | -------------------------------------------------------------------------------- /tests/fail_filter_step_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * "Fail" filter step's unit tests. 19 | * 20 | * @package tool_trigger 21 | * @author Aaron Wells 22 | * @copyright Catalyst IT 2018 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | class tool_trigger_fail_filter_step_testcase extends basic_testcase { 31 | public function test_execute() { 32 | $step = new \tool_trigger\steps\filters\fail_filter_step(); 33 | list($status) = $step->execute(null, null, null, null); 34 | $this->assertFalse($status); 35 | } 36 | } -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Capabilities. 19 | * 20 | * This files lists capabilities related to tool_trigger. 21 | * 22 | * @package tool_trigger 23 | * @copyright Matt Porritt 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | $capabilities = array( 30 | 31 | 'tool/trigger:manageworkflows' => array( 32 | 'riskbitmask' => RISK_CONFIG | RISK_PERSONAL | RISK_SPAM, 33 | 'captype' => 'write', 34 | 'contextlevel' => CONTEXT_SYSTEM, 35 | 'archetypes' => array( 36 | 'manager' => CAP_ALLOW 37 | ), 38 | ), 39 | ); 40 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/RejectionException.php: -------------------------------------------------------------------------------- 1 | reason = $reason; 21 | 22 | $message = 'The promise was rejected'; 23 | 24 | if ($description) { 25 | $message .= ' with reason: ' . $description; 26 | } elseif (is_string($reason) 27 | || (is_object($reason) && method_exists($reason, '__toString')) 28 | ) { 29 | $message .= ' with reason: ' . $this->reason; 30 | } elseif ($reason instanceof \JsonSerializable) { 31 | $message .= ' with reason: ' 32 | . json_encode($this->reason, JSON_PRETTY_PRINT); 33 | } 34 | 35 | parent::__construct($message); 36 | } 37 | 38 | /** 39 | * Returns the rejection reason. 40 | * 41 | * @return mixed 42 | */ 43 | public function getReason() 44 | { 45 | return $this->reason; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Handler/CurlHandler.php: -------------------------------------------------------------------------------- 1 | factory = isset($options['handle_factory']) 29 | ? $options['handle_factory'] 30 | : new CurlFactory(3); 31 | } 32 | 33 | public function __invoke(RequestInterface $request, array $options) 34 | { 35 | if (isset($options['delay'])) { 36 | usleep($options['delay'] * 1000); 37 | } 38 | 39 | $easy = $this->factory->create($request, $options); 40 | curl_exec($easy->handle); 41 | $easy->errno = curl_errno($easy->handle); 42 | 43 | return CurlFactory::finish($this, $easy, $this->factory); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /export.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This page lets admins download workflows to JSON. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(__DIR__ . '/../../../config.php'); 26 | require_once($CFG->libdir.'/adminlib.php'); 27 | 28 | require_login(); 29 | $context = context_system::instance(); 30 | require_capability('tool/trigger:manageworkflows', $context); 31 | 32 | $workflowid = required_param('workflowid', PARAM_INT); 33 | 34 | $workflowrecord = \tool_trigger\workflow_manager::get_workflow_data_with_steps($workflowid); 35 | if (!$workflowrecord) { 36 | print_error('invaliditemid'); 37 | } 38 | 39 | $jsonexporter = new \tool_trigger\json\json_export($workflowrecord); 40 | $jsonexporter->download_file(); 41 | -------------------------------------------------------------------------------- /sample_workflows/User_enrolment_20180905_0706.json: -------------------------------------------------------------------------------- 1 | {"name":"User enrolment","description":"{\"text\":\"

This workflow will send an email to a user when they have been enrolled in a course.<\\\/p>

The email includes some basic information about the course
<\\\/p>\",\"format\":\"1\"}","event":"\\core\\event\\user_enrolment_created","steps":{"78":{"id":"78","name":"Course Lookup","description":"Lookup information about the course","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\course_lookup_step","data":"{\"courseidfield\":\"courseid\",\"outputprefix\":\"course_\",\"stepdesc\":\"Course lookup\",\"typedesc\":\"Lookup\"}","steporder":"0"},"79":{"id":"79","name":"User Lookup","description":"Lookup information about the user","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\user_lookup_step","data":"{\"useridfield\":\"relateduserid\",\"outputprefix\":\"user_\",\"nodeleted\":\"1\",\"stepdesc\":\"User lookup\",\"typedesc\":\"Lookup\"}","steporder":"1"},"80":{"id":"80","name":"Email user","description":"Email the user that they have been enrolled in a course.","type":"actions","stepclass":"\\tool_trigger\\steps\\actions\\email_action_step","data":"{\"emailto\":\"{user_email}\",\"emailsubject\":\"New enrolment: {course_shortname}\",\"emailcontent\":\"Hi {user_firstname} {user_lastname}! \\r\\nYou have just been enrolled in the following course: \\r\\n{course_fullname} ({course_shortname})\\r\\n\\r\\nCourse Summary:\\r\\n{course_summary}\",\"stepdesc\":\"Email\",\"typedesc\":\"Action\"}","steporder":"2"}},"moodleversion":"2018083100","pluginversion":"2018082401"} -------------------------------------------------------------------------------- /classes/task/learn.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Task to learn from processed events. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_trigger\task; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | /** 29 | * Task to learn from processed events. 30 | */ 31 | class learn extends \core\task\scheduled_task { 32 | 33 | /** 34 | * Get a descriptive name for this task. 35 | * 36 | * @return string 37 | */ 38 | public function get_name() { 39 | return get_string('tasklearn', 'tool_trigger'); 40 | } 41 | 42 | /** 43 | * Processes events. 44 | */ 45 | public function execute() { 46 | // Only run task if plugin learning mode is set. 47 | $islearning = get_config('tool_trigger', 'learning'); 48 | if (!$islearning) { 49 | return; 50 | } 51 | 52 | mtrace(get_string('tasklearnstart', 'tool_trigger')); 53 | $processor = new \tool_trigger\learn_process(); 54 | $processor->process(); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/InflateStream.php: -------------------------------------------------------------------------------- 1 | read(10); 25 | $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); 26 | // Skip the header, that is 10 + length of filename + 1 (nil) bytes 27 | $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); 28 | $resource = StreamWrapper::getResource($stream); 29 | stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); 30 | $this->stream = new Stream($resource); 31 | } 32 | 33 | /** 34 | * @param StreamInterface $stream 35 | * @param $header 36 | * @return int 37 | */ 38 | private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) 39 | { 40 | $filename_header_length = 0; 41 | 42 | if (substr(bin2hex($header), 6, 2) === '08') { 43 | // we have a filename, read until nil 44 | $filename_header_length = 1; 45 | while ($stream->read(1) !== chr(0)) { 46 | $filename_header_length++; 47 | } 48 | } 49 | 50 | return $filename_header_length; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /classes/steps/actions/base_action_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Base action step class. 19 | * 20 | * An action is a workflow step that executes an external API, or performs 21 | * some other "output" action. 22 | * 23 | * @package tool_trigger 24 | * @copyright Matt Porritt 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | namespace tool_trigger\steps\actions; 29 | 30 | use tool_trigger\steps\base\base_step; 31 | 32 | defined('MOODLE_INTERNAL') || die; 33 | 34 | /** 35 | * Base action step class. 36 | * 37 | * @package tool_trigger 38 | * @copyright Matt Porritt 39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 | */ 41 | abstract class base_action_step extends base_step { 42 | /** 43 | * {@inheritDoc} 44 | * @see \tool_trigger\steps\base\base_step::get_step_type() 45 | */ 46 | public static function get_step_type() { 47 | return base_step::STEPTYPE_ACTION; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | * @see \tool_trigger\steps\base\base_step::get_step_type_desc() 53 | */ 54 | public static function get_step_type_desc() { 55 | return get_string('action', 'tool_trigger'); 56 | } 57 | } -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Handler/Proxy.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Trigger workflow settings. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(dirname(__FILE__) . '/../../../config.php'); 26 | require_once($CFG->libdir . '/adminlib.php'); 27 | 28 | defined('MOODLE_INTERNAL') || die(); 29 | 30 | require_login(); 31 | 32 | admin_externalpage_setup('tool_trigger_worfklowsettings', '', null, '', array('pagelayout' => 'report')); 33 | 34 | $context = context_system::instance(); 35 | 36 | // Check for caps. 37 | require_capability('tool/trigger:manageworkflows', $context); 38 | 39 | // Load the javascript. 40 | $PAGE->requires->js_call_amd('tool_trigger/import_workflow', 'init', array($context->id)); 41 | 42 | // Build the page output. 43 | echo $OUTPUT->header(); 44 | echo $OUTPUT->heading(get_string('workflowoverview', 'tool_trigger')); 45 | 46 | // Render the rule list. 47 | $manageurl = new moodle_url('/admin/tool/trigger/manage.php'); 48 | $renderable = new \tool_trigger\output\manageworkflows\renderable('tooltrigger', $manageurl); 49 | $renderer = $PAGE->get_renderer('tool_trigger', 'manageworkflows'); 50 | echo $renderer->render($renderable); 51 | 52 | echo $OUTPUT->footer(); 53 | -------------------------------------------------------------------------------- /classes/steps/filters/base_filter_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Base filter step class. 19 | * 20 | * A filter is a workflow step that applies a test to the workflow instance's 21 | * data, and halts execution of further steps if the test does not pass. 22 | * 23 | * @package tool_trigger 24 | * @copyright Matt Porritt 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | namespace tool_trigger\steps\filters; 29 | 30 | use tool_trigger\steps\base\base_step; 31 | 32 | defined('MOODLE_INTERNAL') || die; 33 | 34 | /** 35 | * Base filter step class. 36 | * 37 | * @package tool_trigger 38 | * @copyright Matt Porritt 39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 | */ 41 | abstract class base_filter_step extends base_step { 42 | /** 43 | * {@inheritDoc} 44 | * @see \tool_trigger\steps\base\base_step::get_step_type() 45 | */ 46 | public static function get_step_type() { 47 | return base_step::STEPTYPE_FILTER; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | * @see \tool_trigger\steps\base\base_step::get_step_type_desc() 53 | */ 54 | public static function get_step_type_desc() { 55 | return get_string('filter', 'tool_trigger'); 56 | } 57 | } -------------------------------------------------------------------------------- /classes/steps/lookups/base_lookup_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Base lookup step class. 19 | * 20 | * A lookup is a step that adds or mutates data to the workflow instance's 21 | * data. (It should communicate these changes via the second field of its 22 | * execute() method.) 23 | * 24 | * @package tool_trigger 25 | * @copyright Matt Porritt 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | 29 | namespace tool_trigger\steps\lookups; 30 | 31 | use tool_trigger\steps\base\base_step; 32 | 33 | defined('MOODLE_INTERNAL') || die; 34 | 35 | /** 36 | * Base lookup step class. 37 | * 38 | * @package tool_trigger 39 | * @copyright Matt Porritt 40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 | */ 42 | abstract class base_lookup_step extends base_step { 43 | /** 44 | * {@inheritDoc} 45 | * @see \tool_trigger\steps\base\base_step::get_step_type() 46 | */ 47 | public static function get_step_type() { 48 | return base_step::STEPTYPE_LOOKUP; 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | * @see \tool_trigger\steps\base\base_step::get_step_type_desc() 54 | */ 55 | public static function get_step_type_desc() { 56 | return get_string('lookup', 'tool_trigger'); 57 | } 58 | } -------------------------------------------------------------------------------- /db/tasks.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file defines tasks performed by the tool. 19 | * 20 | * @package tool_monitor 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | // List of tasks. 28 | $tasks = array( 29 | array( 30 | 'classname' => 'tool_trigger\task\process_workflows', 31 | 'blocking' => 0, 32 | 'minute' => '*', 33 | 'hour' => '*', 34 | 'day' => '*', 35 | 'dayofweek' => '*', 36 | 'month' => '*' 37 | ), 38 | array( 39 | 'classname' => 'tool_trigger\task\cleanup', 40 | 'blocking' => 0, 41 | 'minute' => 'R', 42 | 'hour' => '*', 43 | 'day' => '*', 44 | 'dayofweek' => '*', 45 | 'month' => '*' 46 | ), 47 | array( 48 | 'classname' => 'tool_trigger\task\learn', 49 | 'blocking' => 0, 50 | 'minute' => 'R', 51 | 'hour' => 'R', 52 | 'day' => '*', 53 | 'dayofweek' => '*', 54 | 'month' => '*' 55 | ), 56 | array( 57 | 'classname' => 'tool_trigger\task\cleanup_history', 58 | 'blocking' => 0, 59 | 'minute' => 'R', 60 | 'hour' => '0', 61 | 'day' => '*', 62 | 'dayofweek' => '*', 63 | 'month' => '*' 64 | ) 65 | ); 66 | -------------------------------------------------------------------------------- /templates/trigger_fields.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template tool_trigger/trigger_fields 19 | 20 | Displays form element containg all available fields. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * none 30 | 31 | Example context (json): 32 | { 33 | } 34 | 35 | }} 36 |

37 |
38 |

{{# str }} availablefields, tool_trigger {{/ str }}

39 |
40 |
41 | {{#nofields}} 42 |

{{# str }} noavailablefields, tool_trigger {{/ str }}

43 | {{/nofields}} 44 | {{^nofields}} 45 |
{{# str }} eventfields, tool_trigger {{/ str }}:
46 | {{/nofields}} 47 |
48 | {{#fields}} 49 | {{ field }} 50 | {{/fields}} 51 |
52 | {{#steps}} 53 |
{{ stepname }}:
54 |
55 | {{#fields}} 56 | {{ field }} 57 | {{/fields}} 58 |
59 | {{/steps}} 60 |
61 |
62 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/TaskQueue.php: -------------------------------------------------------------------------------- 1 | run(); 12 | */ 13 | class TaskQueue implements TaskQueueInterface 14 | { 15 | private $enableShutdown = true; 16 | private $queue = []; 17 | 18 | public function __construct($withShutdown = true) 19 | { 20 | if ($withShutdown) { 21 | register_shutdown_function(function () { 22 | if ($this->enableShutdown) { 23 | // Only run the tasks if an E_ERROR didn't occur. 24 | $err = error_get_last(); 25 | if (!$err || ($err['type'] ^ E_ERROR)) { 26 | $this->run(); 27 | } 28 | } 29 | }); 30 | } 31 | } 32 | 33 | public function isEmpty() 34 | { 35 | return !$this->queue; 36 | } 37 | 38 | public function add(callable $task) 39 | { 40 | $this->queue[] = $task; 41 | } 42 | 43 | public function run() 44 | { 45 | /** @var callable $task */ 46 | while ($task = array_shift($this->queue)) { 47 | $task(); 48 | } 49 | } 50 | 51 | /** 52 | * The task queue will be run and exhausted by default when the process 53 | * exits IFF the exit is not the result of a PHP E_ERROR error. 54 | * 55 | * You can disable running the automatic shutdown of the queue by calling 56 | * this function. If you disable the task queue shutdown process, then you 57 | * MUST either run the task queue (as a result of running your event loop 58 | * or manually using the run() method) or wait on each outstanding promise. 59 | * 60 | * Note: This shutdown will occur before any destructors are triggered. 61 | */ 62 | public function disableShutdown() 63 | { 64 | $this->enableShutdown = false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Cookie/SessionCookieJar.php: -------------------------------------------------------------------------------- 1 | sessionKey = $sessionKey; 26 | $this->storeSessionCookies = $storeSessionCookies; 27 | $this->load(); 28 | } 29 | 30 | /** 31 | * Saves cookies to session when shutting down 32 | */ 33 | public function __destruct() 34 | { 35 | $this->save(); 36 | } 37 | 38 | /** 39 | * Save cookies to the client session 40 | */ 41 | public function save() 42 | { 43 | $json = []; 44 | foreach ($this as $cookie) { 45 | /** @var SetCookie $cookie */ 46 | if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { 47 | $json[] = $cookie->toArray(); 48 | } 49 | } 50 | 51 | $_SESSION[$this->sessionKey] = json_encode($json); 52 | } 53 | 54 | /** 55 | * Load the contents of the client session into the data array 56 | */ 57 | protected function load() 58 | { 59 | if (!isset($_SESSION[$this->sessionKey])) { 60 | return; 61 | } 62 | $data = json_decode($_SESSION[$this->sessionKey], true); 63 | if (is_array($data)) { 64 | foreach ($data as $cookie) { 65 | $this->setCookie(new SetCookie($cookie)); 66 | } 67 | } elseif (strlen($data)) { 68 | throw new \RuntimeException("Invalid cookie data"); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/FulfilledPromise.php: -------------------------------------------------------------------------------- 1 | value = $value; 22 | } 23 | 24 | public function then( 25 | callable $onFulfilled = null, 26 | callable $onRejected = null 27 | ) { 28 | // Return itself if there is no onFulfilled function. 29 | if (!$onFulfilled) { 30 | return $this; 31 | } 32 | 33 | $queue = queue(); 34 | $p = new Promise([$queue, 'run']); 35 | $value = $this->value; 36 | $queue->add(static function () use ($p, $value, $onFulfilled) { 37 | if ($p->getState() === self::PENDING) { 38 | try { 39 | $p->resolve($onFulfilled($value)); 40 | } catch (\Throwable $e) { 41 | $p->reject($e); 42 | } catch (\Exception $e) { 43 | $p->reject($e); 44 | } 45 | } 46 | }); 47 | 48 | return $p; 49 | } 50 | 51 | public function otherwise(callable $onRejected) 52 | { 53 | return $this->then(null, $onRejected); 54 | } 55 | 56 | public function wait($unwrap = true, $defaultDelivery = null) 57 | { 58 | return $unwrap ? $this->value : null; 59 | } 60 | 61 | public function getState() 62 | { 63 | return self::FULFILLED; 64 | } 65 | 66 | public function resolve($value) 67 | { 68 | if ($value !== $this->value) { 69 | throw new \LogicException("Cannot resolve a fulfilled promise"); 70 | } 71 | } 72 | 73 | public function reject($reason) 74 | { 75 | throw new \LogicException("Cannot reject a fulfilled promise"); 76 | } 77 | 78 | public function cancel() 79 | { 80 | // pass 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /db/services.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Admin tool trigger web service external functions and service definitions. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | // Define the web service functions to install. 28 | $functions = array( 29 | 'tool_trigger_step_by_type' => array( 30 | 'classname' => 'tool_trigger_external', 31 | 'methodname' => 'step_by_type', 32 | 'classpath' => 'admin/tool/trigger/externallib.php', 33 | 'description' => 'Returns all steps matching supplied type', 34 | 'type' => 'read', 35 | 'capabilities' => 'tool/trigger:manageworkflows', 36 | 'ajax' => true 37 | ), 38 | 'tool_trigger_validate_form' => array( 39 | 'classname' => 'tool_trigger_external', 40 | 'methodname' => 'validate_form', 41 | 'classpath' => 'admin/tool/trigger/externallib.php', 42 | 'description' => 'Checks to see if a form contains valid data', 43 | 'type' => 'read', 44 | 'capabilities' => 'tool/trigger:manageworkflows', 45 | 'ajax' => true 46 | ), 47 | 'tool_trigger_process_import_form' => array( 48 | 'classname' => 'tool_trigger_external', 49 | 'methodname' => 'process_import_form', 50 | 'classpath' => 'admin/tool/trigger/externallib.php', 51 | 'description' => 'Creates a new workflow.', 52 | 'type' => 'write', 53 | 'capabilities' => 'tool/trigger:manageworkflows', 54 | 'ajax' => true 55 | ), 56 | ); 57 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/RejectedPromise.php: -------------------------------------------------------------------------------- 1 | reason = $reason; 22 | } 23 | 24 | public function then( 25 | callable $onFulfilled = null, 26 | callable $onRejected = null 27 | ) { 28 | // If there's no onRejected callback then just return self. 29 | if (!$onRejected) { 30 | return $this; 31 | } 32 | 33 | $queue = queue(); 34 | $reason = $this->reason; 35 | $p = new Promise([$queue, 'run']); 36 | $queue->add(static function () use ($p, $reason, $onRejected) { 37 | if ($p->getState() === self::PENDING) { 38 | try { 39 | // Return a resolved promise if onRejected does not throw. 40 | $p->resolve($onRejected($reason)); 41 | } catch (\Throwable $e) { 42 | // onRejected threw, so return a rejected promise. 43 | $p->reject($e); 44 | } catch (\Exception $e) { 45 | // onRejected threw, so return a rejected promise. 46 | $p->reject($e); 47 | } 48 | } 49 | }); 50 | 51 | return $p; 52 | } 53 | 54 | public function otherwise(callable $onRejected) 55 | { 56 | return $this->then(null, $onRejected); 57 | } 58 | 59 | public function wait($unwrap = true, $defaultDelivery = null) 60 | { 61 | if ($unwrap) { 62 | throw exception_for($this->reason); 63 | } 64 | } 65 | 66 | public function getState() 67 | { 68 | return self::REJECTED; 69 | } 70 | 71 | public function resolve($value) 72 | { 73 | throw new \LogicException("Cannot resolve a rejected promise"); 74 | } 75 | 76 | public function reject($reason) 77 | { 78 | if ($reason !== $this->reason) { 79 | throw new \LogicException("Cannot reject a rejected promise"); 80 | } 81 | } 82 | 83 | public function cancel() 84 | { 85 | // pass 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /classes/task/cleanup_history.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Cleanup old events/processed tasks. 19 | * 20 | * @package tool_trigger 21 | * @copyright Catalyst IT 22 | * @author Peter Burnett 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace tool_trigger\task; 27 | 28 | defined('MOODLE_INTERNAL') || die(); 29 | /** 30 | * Task to cleanup old queue. 31 | */ 32 | class cleanup_history extends \core\task\scheduled_task { 33 | 34 | /** 35 | * Get a descriptive name for this task. 36 | * 37 | * @return string 38 | */ 39 | public function get_name() { 40 | return get_string('taskcleanuphistory', 'tool_trigger'); 41 | } 42 | 43 | /** 44 | * Cleans up run data that is too old. 45 | */ 46 | public function execute() { 47 | global $DB; 48 | 49 | $duration = get_config('tool_trigger', 'historyduration'); 50 | if (empty($duration)) { 51 | mtrace(get_string('taskemptyhistoryconfig', 'tool_trigger')); 52 | } 53 | $lookback = time() - $duration; 54 | 55 | // Conditions: Delete steps executed older than duration, 56 | // But have to check if any steps within duration that rely on that step 57 | // To be safe for now. Keep all run steps if any in run are over duration. 58 | $sql = "DELETE FROM {tool_trigger_run_hist} rh 59 | WHERE rh.executed < :lookback1 60 | AND ( 61 | SELECT COUNT(id) as count 62 | FROM {tool_trigger_run_hist} sub 63 | WHERE sub.runid = rh.runid 64 | AND sub.executed > :lookback2 65 | ) = 0"; 66 | $DB->execute($sql, ['lookback1' => $lookback, 'lookback2' => $lookback]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sample_workflows/Course_Completed_20180905_0706.json: -------------------------------------------------------------------------------- 1 | {"name":"Course Completed","description":"{\"text\":\"

This workflow sends an email to a student when they have successfully completed a course.<\\\/p>

The workflow will also send the completion details to an external system via HTTP Post.
<\\\/p>\",\"format\":\"1\"}","event":"\\core\\event\\course_completed","steps":{"93":{"id":"93","name":"Course Filter","description":"We don't want this workflow to complete for the Maths 101 course.","type":"filters","stepclass":"\\tool_trigger\\steps\\filters\\numcompare_filter_step","data":"{\"field1\":\"courseid\",\"operator\":\"==\",\"field2\":\"6\",\"stepdesc\":\"Number comparison\",\"typedesc\":\"Filter\"}","steporder":"0"},"87":{"id":"87","name":"Course Lookup","description":"Lookup the course details, based on the course ID in the triggered event.","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\course_lookup_step","data":"{\"courseidfield\":\"courseid\",\"outputprefix\":\"course_\",\"stepdesc\":\"Course lookup\",\"typedesc\":\"Lookup\"}","steporder":"1"},"88":{"id":"88","name":"User Lookup","description":"Lookup the details of the student who completed the course based on the related user ID in the event.","type":"lookups","stepclass":"\\tool_trigger\\steps\\lookups\\user_lookup_step","data":"{\"useridfield\":\"relateduserid\",\"outputprefix\":\"user_\",\"nodeleted\":\"1\",\"stepdesc\":\"User lookup\",\"typedesc\":\"Lookup\"}","steporder":"2"},"91":{"id":"91","name":"Email User","description":"Send the user a congratulations email for completing the course.","type":"actions","stepclass":"\\tool_trigger\\steps\\actions\\email_action_step","data":"{\"emailto\":\"{user_email}\",\"emailsubject\":\"Course complete: {course_shortname}\",\"emailcontent\":\"Congratulations {user_firstname} {user_lastname}\\r\\n\\r\\nYou have successfully completed the course:\\r\\n{course_fullname} ({course_shortname})\\r\\nKeep up the good work.\",\"stepdesc\":\"Email\",\"typedesc\":\"Action\"}","steporder":"3"},"92":{"id":"92","name":"HTTP Post","description":"Let an external system know about the course completion information for this user.","type":"actions","stepclass":"\\tool_trigger\\steps\\actions\\http_post_action_step","data":"{\"url\":\"http:\\\/\\\/localhost:8000\\\/tl4arbtl\",\"httpheaders\":\"Content-Type: application\\\/json\\r\\nAccept: application\\\/json\",\"httpparams\":\"action=course_completion&course[0]={course_id}&course[0]={course_shortname}&user[0]={user_id}&user[1]={user_username}&user[2]={user_firstname}&user[3]={user_lastname}\",\"jsonencode\":\"1\",\"stepdesc\":\"HTTP Post\",\"typedesc\":\"Action\"}","steporder":"4"}},"moodleversion":"2018083100","pluginversion":"2018082401"} -------------------------------------------------------------------------------- /classes/steps/actions/logdump_action_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace tool_trigger\steps\actions; 18 | 19 | defined('MOODLE_INTERNAL') || die; 20 | 21 | /** 22 | * Action step that just does a var_dump to the logs. 23 | * 24 | * @package tool_trigger 25 | * @author Aaron Wells 26 | * @copyright Catalyst IT, 2018 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class logdump_action_step extends base_action_step { 30 | 31 | /** 32 | * The fields supplied by this step. 33 | * 34 | * @var array 35 | */ 36 | private static $stepfields = array( 37 | 'vardump', 38 | ); 39 | 40 | public function form_definition_extra( 41 | $form, 42 | $mform, 43 | $customdata) { 44 | $mform->addElement('html', self::get_step_desc()); 45 | } 46 | 47 | public static function get_step_desc() { 48 | return get_string('step_action_logdump_desc', 'tool_trigger'); 49 | } 50 | 51 | public static function get_step_name() { 52 | return get_string('step_action_logdump_name', 'tool_trigger'); 53 | } 54 | 55 | public function execute($step, $trigger, $event, $stepresults) { 56 | mtrace('logdump step "' . $step->name); 57 | ob_start(); 58 | var_dump($event->get_data()); 59 | var_dump($event->get_logextra()); 60 | var_dump($stepresults); 61 | $o = ob_get_contents(); 62 | ob_end_clean(); 63 | mtrace($o); 64 | $stepresults['vardump'] = $o; 65 | return [true, $stepresults]; 66 | } 67 | 68 | /** 69 | * Get a list of fields this step provides. 70 | * 71 | * @return array $stepfields The fields this step provides. 72 | */ 73 | public static function get_fields() { 74 | return self::$stepfields; 75 | 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /templates/workflow_steps.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template tool_trigger/workflow_steps 19 | 20 | Displays step table in workflow edit form. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * none 30 | 31 | Example context (json): 32 | { 33 | } 34 | 35 | }} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {{#rows}} 47 | 48 | 49 | 50 | 51 | 57 | 58 | {{/rows}} 59 | 60 |
{{# str }} stepname, tool_trigger {{/ str }}{{# str }} steptype, tool_trigger {{/ str }}{{# str }} stepclass, tool_trigger {{/ str }}{{# str }} manage, tool_trigger {{/ str }}
{{name}}{{typedesc}}{{stepdesc}} 52 | {{# pix }} t/up, core, {{# str }} movestepup, tool_trigger {{/ str }} {{/ pix }} 53 | {{# pix }} t/down, core, {{# str }} movestepdown, tool_trigger {{/ str }} {{/ pix }} 54 | {{# pix }} t/edit, core, {{# str }} editstep, tool_trigger {{/ str }} {{/ pix }} 55 | {{# pix }} t/delete, core, {{# str }} deletestep, tool_trigger {{/ str }} {{/ pix }} 56 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /classes/steps/filters/fail_filter_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Base filter step class. 19 | * 20 | * A filter is a workflow step that applies a test to the workflow instance's 21 | * data, and halts execution of further steps if the test does not pass. 22 | * 23 | * @package tool_trigger 24 | * @copyright Matt Porritt 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | namespace tool_trigger\steps\filters; 29 | 30 | defined('MOODLE_INTERNAL') || die; 31 | 32 | /** 33 | * Fail filter step class. 34 | * 35 | * @package tool_trigger 36 | * @copyright Matt Porritt 37 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 | */ 39 | class fail_filter_step extends base_filter_step { 40 | 41 | /** 42 | * {@inheritDoc} 43 | * @see \tool_trigger\steps\base\base_step::execute() 44 | */ 45 | public function execute($step, $trigger, $event, $previousstepresult) { 46 | return [false, $previousstepresult]; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | * @see \tool_trigger\steps\base\base_step::get_step_desc() 52 | */ 53 | public static function get_step_desc() { 54 | return get_string('step_filter_fail_desc', 'tool_trigger'); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | * @see \tool_trigger\steps\base\base_step::get_step_name() 60 | */ 61 | public static function get_step_name() { 62 | return get_string('step_filter_fail_name', 'tool_trigger'); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | * @see \tool_trigger\steps\base\base_step::add_extra_form_fields() 68 | */ 69 | public function form_definition_extra($form, $mform, $customdata) { 70 | $mform->addElement('html', self::get_step_desc()); 71 | } 72 | 73 | /** 74 | * Get a list of fields this step provides. 75 | * 76 | * @return array $stepfields The fields this step provides. 77 | */ 78 | public static function get_fields() { 79 | return false; 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /guzzle/Psr/Http/Message/ResponseInterface.php: -------------------------------------------------------------------------------- 1 | 6 | One of the main use cases of this plugin is to allow Moodle events to trigger actions in external systems. 7 | 8 | Each workflow is made up of a series of *steps*. Steps can be things like: 9 | * Using event data to *lookup* user and course information 10 | * *Filtering* data based on a set of conditions 11 | * Performing an *action* like sending an email or Posting data to a HTTP endpoint or external API. 12 | 13 | The plugin is designed to be extensible and contributions are welcome to extend the available actions. 14 | 15 | More configuration documentation can be found at the following link: 16 | 17 | * https://github.com/catalyst/moodle-tool_trigger/wiki 18 | 19 | More Information on Moodle events can be found in the Moodle documentation at the following link: 20 | 21 | * https://docs.moodle.org/dev/Event_2 22 | 23 | ## Supported Moodle Versions 24 | This plugin currently supports Moodle: 25 | 26 | * 3.4 27 | * 3.5 28 | * 3.6 29 | * 3.7 30 | * 3.8 31 | 32 | ## Moodle Plugin Installation 33 | The following sections outline how to install the Moodle plugin. 34 | 35 | ### Command Line Installation 36 | To install the plugin in Moodle via the command line: (assumes a Linux based system) 37 | 38 | 1. Get the code from GitHub or the Moodle Plugin Directory. 39 | 2. Copy or clone code into: `/admin/tool/trigger` 40 | 3. Run the upgrade: `sudo -u www-data php admin/cli/upgrade` **Note:** the user may be different to www-data on your system. 41 | 42 | ### User Interface Installation 43 | To install the plugin in Moodle via the Moodle User Interface: 44 | 45 | 1. Log into your Moodle as an Administrator. 46 | 2. Navigate to: *Site administration > Plugins > Install Plugins* 47 | 3. Install plugin from Moodle Plugin directory or via zip upload. 48 | 49 | ## Plugin Setup 50 | Plugin setup and configuration documentation can be found at the following link: 51 | 52 | * https://github.com/catalyst/moodle-tool_trigger/wiki 53 | 54 | ## Roadmap 55 | 56 | Please see the current GitHub issues for the project roadmap: https://github.com/catalyst/moodle-tool_trigger/issues 57 | 58 | # Crafted by Catalyst IT 59 | 60 | This plugin was developed by Catalyst IT Australia: 61 | 62 | https://www.catalyst-au.net/ 63 | 64 | ![Catalyst IT](/pix/catalyst-logo.png?raw=true) 65 | 66 | 67 | # Contributing and Support 68 | 69 | Issues, and pull requests using github are welcome and encouraged! 70 | 71 | https://github.com/catalyst/moodle-tool_trigger/issues 72 | 73 | If you would like commercial support or would like to sponsor additional improvements 74 | to this plugin please contact us: 75 | 76 | https://www.catalyst-au.net/contact-us 77 | -------------------------------------------------------------------------------- /classes/task/cleanup.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Cleanup old events/processed tasks. 19 | * 20 | * @package tool_trigger 21 | * @copyright Catalyst IT 22 | * @author Dan Marsden 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace tool_trigger\task; 27 | 28 | defined('MOODLE_INTERNAL') || die(); 29 | /** 30 | * Task to cleanup old queue. 31 | */ 32 | class cleanup extends \core\task\scheduled_task { 33 | 34 | /** 35 | * Get a descriptive name for this task. 36 | * 37 | * @return string 38 | */ 39 | public function get_name() { 40 | return get_string('taskcleanup', 'tool_trigger'); 41 | } 42 | 43 | /** 44 | * Processes workflows. 45 | */ 46 | public function execute() { 47 | global $DB; 48 | $timetocleanup = get_config('tool_trigger', 'timetocleanup'); 49 | if (empty($timetocleanup)) { 50 | return; 51 | } 52 | $timetocleanup = time() - $timetocleanup; 53 | 54 | // Delete events first so that we don't accidentally create new queue items. 55 | // Only delete events that do not have an unfinished queue still waiting. 56 | $sql = " 57 | DELETE 58 | FROM {tool_trigger_events} e 59 | WHERE NOT EXISTS ( 60 | SELECT 1 61 | FROM {tool_trigger_queue} q 62 | WHERE q.eventid = e.id 63 | AND q.status = :statusready 64 | ) 65 | AND e.timecreated < :timetocleanup"; 66 | $DB->execute($sql, [ 67 | 'statusready' => \tool_trigger\task\process_workflows::STATUS_READY_TO_RUN, 68 | 'timetocleanup' => $timetocleanup 69 | ]); 70 | 71 | // Now delete processed queue items. 72 | $sql = "DELETE 73 | FROM {tool_trigger_queue} q 74 | WHERE q.status <> :statusready 75 | AND q.timemodified < :timetocleanup"; 76 | $DB->execute($sql, [ 77 | 'statusready' => \tool_trigger\task\process_workflows::STATUS_READY_TO_RUN, 78 | 'timetocleanup' => $timetocleanup 79 | ]); 80 | } 81 | } -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Cookie/FileCookieJar.php: -------------------------------------------------------------------------------- 1 | filename = $cookieFile; 27 | $this->storeSessionCookies = $storeSessionCookies; 28 | 29 | if (file_exists($cookieFile)) { 30 | $this->load($cookieFile); 31 | } 32 | } 33 | 34 | /** 35 | * Saves the file when shutting down 36 | */ 37 | public function __destruct() 38 | { 39 | $this->save($this->filename); 40 | } 41 | 42 | /** 43 | * Saves the cookies to a file. 44 | * 45 | * @param string $filename File to save 46 | * @throws \RuntimeException if the file cannot be found or created 47 | */ 48 | public function save($filename) 49 | { 50 | $json = []; 51 | foreach ($this as $cookie) { 52 | /** @var SetCookie $cookie */ 53 | if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { 54 | $json[] = $cookie->toArray(); 55 | } 56 | } 57 | 58 | $jsonStr = \GuzzleHttp\json_encode($json); 59 | if (false === file_put_contents($filename, $jsonStr)) { 60 | throw new \RuntimeException("Unable to save file {$filename}"); 61 | } 62 | } 63 | 64 | /** 65 | * Load cookies from a JSON formatted file. 66 | * 67 | * Old cookies are kept unless overwritten by newly loaded ones. 68 | * 69 | * @param string $filename Cookie file to load. 70 | * @throws \RuntimeException if the file cannot be loaded. 71 | */ 72 | public function load($filename) 73 | { 74 | $json = file_get_contents($filename); 75 | if (false === $json) { 76 | throw new \RuntimeException("Unable to load file {$filename}"); 77 | } elseif ($json === '') { 78 | return; 79 | } 80 | 81 | $data = \GuzzleHttp\json_decode($json, true); 82 | if (is_array($data)) { 83 | foreach (json_decode($json, true) as $cookie) { 84 | $this->setCookie(new SetCookie($cookie)); 85 | } 86 | } elseif (strlen($data)) { 87 | throw new \RuntimeException("Invalid cookie file: {$filename}"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin administration pages are defined here. 19 | * 20 | * @package tool_trigger 21 | * @category admin 22 | * @copyright Matt Porritt 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | if ($hassiteconfig) { 29 | $ADMIN->add('tools', new admin_category('tool_trigger', get_string('pluginname', 'tool_trigger'))); 30 | 31 | $settings = new admin_settingpage('tool_trigger_settings', 32 | get_string('pluginsettings', 'tool_trigger')); 33 | 34 | // Cleanup settings. 35 | $settings->add(new admin_setting_heading('tool_trigger/cleanupsettings', 36 | get_string('cleanupsettings', 'tool_trigger'), 37 | get_string('cleanupsettingsdesc', 'tool_trigger'))); 38 | 39 | $settings->add(new admin_setting_configduration('tool_trigger/timetocleanup', 40 | get_string('timetocleanup', 'tool_trigger'), 41 | get_string('timetocleanup_help', 'tool_trigger'), DAYSECS, DAYSECS)); 42 | 43 | // Learning mode settings. 44 | $settings->add(new admin_setting_heading('tool_trigger/learningsettings', 45 | get_string('learningsettings', 'tool_trigger'), 46 | get_string('learningsettingsdesc', 'tool_trigger'))); 47 | 48 | $settings->add(new admin_setting_configcheckbox('tool_trigger/learning', 49 | get_string('learning', 'tool_trigger'), 50 | get_string('learning_help', 'tool_trigger'), 0)); 51 | 52 | $settings->add(new admin_setting_heading('tool_trigger/historysettings', 53 | get_string('historysettings', 'tool_trigger'), 54 | get_string('historysettingsdesc', 'tool_trigger'))); 55 | 56 | $settings->add(new admin_setting_configduration('tool_trigger/historyduration', 57 | get_string('historyduration', 'tool_trigger'), 58 | get_string('historydurationdesc', 'tool_trigger'), 1 * WEEKSECS, WEEKSECS)); 59 | 60 | $workflowsettings = new admin_externalpage('tool_trigger_worfklowsettings', 61 | get_string('manage', 'tool_trigger'), 62 | new moodle_url('/admin/tool/trigger/manage.php')); 63 | 64 | $ADMIN->add('tool_trigger', $settings); 65 | $ADMIN->add('tool_trigger', $workflowsettings); 66 | 67 | $settings = null; 68 | } 69 | -------------------------------------------------------------------------------- /cli/export_fields.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This script exports fields to the JSON fixture files. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | define('CLI_SCRIPT', 1); 26 | 27 | require(__DIR__.'/../../../../config.php'); 28 | require_once($CFG->libdir.'/clilib.php'); 29 | 30 | // Get cli options. 31 | list($options, $unrecognized) = cli_get_params(array('help' => false), array('h' => 'help')); 32 | 33 | if ($unrecognized) { 34 | $unrecognized = implode("\n ", $unrecognized); 35 | cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); 36 | } 37 | 38 | // Help information. 39 | if ($options['help']) { 40 | $help = <<get_event_fields_events(); 61 | $results = array(); 62 | $count = 0; 63 | 64 | // Iterrate through each event getting fields. 65 | foreach ($eventnames as $eventname) { 66 | // Convert fields from JSON and add to array indexed by event name. 67 | $fieldsjson = $learnprocess->get_event_fields_json($eventname); 68 | $fields = json_decode($fieldsjson->jsonfields, true); 69 | $results[$eventname] = $fields; 70 | $count++; 71 | } 72 | 73 | // Convert results into JSON. 74 | $resultsjson = json_encode($results, JSON_PRETTY_PRINT); 75 | 76 | print_string('cli_writingfile', 'tool_trigger', $count); 77 | echo "\n"; 78 | 79 | // Write results to file. 80 | $filename = $CFG->dirroot . '/admin/tool/trigger/db/fixtures/event_fields.json'; 81 | $fp = fopen($filename, 'w'); 82 | 83 | if ($fp) { 84 | fwrite($fp, $resultsjson); 85 | fclose($fp); 86 | print_string('cli_filesummary', 'tool_trigger', $filename); 87 | echo "\n"; 88 | } else { 89 | print_string('cli_filefail', 'tool_trigger', $filename); 90 | echo "\n"; 91 | } 92 | 93 | exit(0); 94 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/ClientInterface.php: -------------------------------------------------------------------------------- 1 | headers)) { 48 | throw new \RuntimeException('No headers have been received'); 49 | } 50 | 51 | // HTTP-version SP status-code SP reason-phrase 52 | $startLine = explode(' ', array_shift($this->headers), 3); 53 | $headers = \GuzzleHttp\headers_from_lines($this->headers); 54 | $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); 55 | 56 | if (!empty($this->options['decode_content']) 57 | && isset($normalizedKeys['content-encoding']) 58 | ) { 59 | $headers['x-encoded-content-encoding'] 60 | = $headers[$normalizedKeys['content-encoding']]; 61 | unset($headers[$normalizedKeys['content-encoding']]); 62 | if (isset($normalizedKeys['content-length'])) { 63 | $headers['x-encoded-content-length'] 64 | = $headers[$normalizedKeys['content-length']]; 65 | 66 | $bodyLength = (int) $this->sink->getSize(); 67 | if ($bodyLength) { 68 | $headers[$normalizedKeys['content-length']] = $bodyLength; 69 | } else { 70 | unset($headers[$normalizedKeys['content-length']]); 71 | } 72 | } 73 | } 74 | 75 | // Attach a response to the easy handle with the parsed headers. 76 | $this->response = new Response( 77 | $startLine[1], 78 | $headers, 79 | $this->sink, 80 | substr($startLine[0], 5), 81 | isset($startLine[2]) ? (string) $startLine[2] : null 82 | ); 83 | } 84 | 85 | public function __get($name) 86 | { 87 | $msg = $name === 'handle' 88 | ? 'The EasyHandle has been released' 89 | : 'Invalid property: ' . $name; 90 | throw new \BadMethodCallException($msg); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Promise/PromiseInterface.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Tool trigger test case. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | global $CFG; 28 | 29 | /** 30 | * Tool trigger test case. 31 | * 32 | * @package tool_trigger 33 | * @copyright Matt Porritt 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | 37 | abstract class tool_trigger_testcase extends advanced_testcase { 38 | 39 | /** 40 | * Helper function to create a test workflow. 41 | * 42 | * @param int $realtime Is realtime workflow? 43 | * @param array $steps A list of steps. 44 | * @return int $workflowid The id of the created workflow. 45 | */ 46 | public function create_workflow($realtime = 0, $steps = [], $debug = 0) { 47 | if (empty($steps)) { 48 | $steps = [ 49 | [ 50 | 'id' => 0, 51 | 'type' => 'lookups', 52 | 'stepclass' => '\tool_trigger\steps\lookups\user_lookup_step', 53 | 'steporder' => '0', 54 | 'name' => 'Get user data', 55 | 'description' => 'Get user data', 56 | 'useridfield' => 'userid', 57 | 'outputprefix' => 'user_' 58 | ], 59 | ]; 60 | } 61 | 62 | $mdata = new \stdClass(); 63 | $mdata->workflowid = 0; 64 | $mdata->workflowname = 'Email me about login'; 65 | $mdata->workflowdescription = 'When a user logs in, email me.'; 66 | $mdata->eventtomonitor = '\core\event\user_loggedin'; 67 | $mdata->workflowactive = 1; 68 | $mdata->workflowrealtime = $realtime; 69 | $mdata->workflowdebug = $debug; 70 | $mdata->draftmode = 0; 71 | $mdata->isstepschanged = 1; 72 | $mdata->stepjson = json_encode($steps); 73 | 74 | // Insert it into the database. (It seems like it'll be more robust to do this 75 | // by calling workflow_process rather than doing it by hand.) 76 | $workflowprocess = new \tool_trigger\workflow_process($mdata); 77 | $workflowid = $workflowprocess->processform(0, true); 78 | 79 | // We now need to purge the event caches, so the new workflow is picked up. 80 | \cache_helper::purge_by_definition('tool_trigger', 'eventsubscriptions'); 81 | 82 | return $workflowid; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/logdump_action_step_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for logdump_action_step 19 | * 20 | * @package tool_trigger 21 | * @author Aaron Wells 22 | * @copyright Catalyst IT 2018 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | class tool_trigger_logdump_action_step_testcase extends basic_testcase { 31 | public function test_execute() { 32 | 33 | // Don't overload var_dump by xdebug to solve the unit test when we run it with xdebug. 34 | ini_set('xdebug.overload_var_dump', 0); 35 | 36 | $user = \core_user::get_user_by_username('admin'); 37 | $context = context_user::instance($user->id); 38 | $event = \core\event\user_profile_viewed::create([ 39 | 'objectid' => $user->id, 40 | 'relateduserid' => $user->id, 41 | 'context' => $context, 42 | 'other' => [ 43 | 'courseid' => 1, 44 | 'courseshortname' => 'short name', 45 | 'coursefullname' => 'full name' 46 | ] 47 | ]); 48 | 49 | $prevstepresults = [ 50 | 'foo' => 'bar', 51 | 'baz' => 'bax' 52 | ]; 53 | 54 | // Just check for a portion of the var_dump of the event, because it's looong! 55 | $eventdump = << 57 | string(31) "\\core\\event\\user_profile_viewed" 58 | ["component"]=> 59 | string(4) "core" 60 | ["action"]=> 61 | string(6) "viewed" 62 | ["target"]=> 63 | string(12) "user_profile" 64 | ["objecttable"]=> 65 | string(4) "user" 66 | ["objectid"]=> 67 | string(1) "{$user->id}" 68 | EOD; 69 | 70 | $stepresultsdump = <<<'EOD' 71 | array(2) { 72 | ["foo"]=> 73 | string(3) "bar" 74 | ["baz"]=> 75 | string(3) "bax" 76 | } 77 | EOD; 78 | 79 | $step = new \tool_trigger\steps\actions\logdump_action_step(); 80 | // Look for the var_dump of the event object and the stepresults object, in the output. 81 | $this->expectOutputRegex('/' . preg_quote($eventdump) . '.*' . preg_quote($stepresultsdump) . '/ms', '/'); 82 | list($status, $stepresults) = $step->execute((object)['name' => 'logdump step'], null, $event, $prevstepresults); 83 | $this->assertTrue($status); 84 | 85 | // The logdump step also adds the var_dump()s to a datafield called "vardump". 86 | $this->assertContains($eventdump, $stepresults['vardump']); 87 | $this->assertContains($stepresultsdump, $stepresults['vardump']); 88 | } 89 | } -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/StreamWrapper.php: -------------------------------------------------------------------------------- 1 | isReadable()) { 33 | $mode = $stream->isWritable() ? 'r+' : 'r'; 34 | } elseif ($stream->isWritable()) { 35 | $mode = 'w'; 36 | } else { 37 | throw new \InvalidArgumentException('The stream must be readable, ' 38 | . 'writable, or both.'); 39 | } 40 | 41 | return fopen('guzzle://stream', $mode, null, stream_context_create([ 42 | 'guzzle' => ['stream' => $stream] 43 | ])); 44 | } 45 | 46 | /** 47 | * Registers the stream wrapper if needed 48 | */ 49 | public static function register() 50 | { 51 | if (!in_array('guzzle', stream_get_wrappers())) { 52 | stream_wrapper_register('guzzle', __CLASS__); 53 | } 54 | } 55 | 56 | public function stream_open($path, $mode, $options, &$opened_path) 57 | { 58 | $options = stream_context_get_options($this->context); 59 | 60 | if (!isset($options['guzzle']['stream'])) { 61 | return false; 62 | } 63 | 64 | $this->mode = $mode; 65 | $this->stream = $options['guzzle']['stream']; 66 | 67 | return true; 68 | } 69 | 70 | public function stream_read($count) 71 | { 72 | return $this->stream->read($count); 73 | } 74 | 75 | public function stream_write($data) 76 | { 77 | return (int) $this->stream->write($data); 78 | } 79 | 80 | public function stream_tell() 81 | { 82 | return $this->stream->tell(); 83 | } 84 | 85 | public function stream_eof() 86 | { 87 | return $this->stream->eof(); 88 | } 89 | 90 | public function stream_seek($offset, $whence) 91 | { 92 | $this->stream->seek($offset, $whence); 93 | 94 | return true; 95 | } 96 | 97 | public function stream_stat() 98 | { 99 | static $modeMap = [ 100 | 'r' => 33060, 101 | 'r+' => 33206, 102 | 'w' => 33188 103 | ]; 104 | 105 | return [ 106 | 'dev' => 0, 107 | 'ino' => 0, 108 | 'mode' => $modeMap[$this->mode], 109 | 'nlink' => 0, 110 | 'uid' => 0, 111 | 'gid' => 0, 112 | 'rdev' => 0, 113 | 'size' => $this->stream->getSize() ?: 0, 114 | 'atime' => 0, 115 | 'mtime' => 0, 116 | 'ctime' => 0, 117 | 'blksize' => 0, 118 | 'blocks' => 0 119 | ]; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | # We use trusty as currently there is an issue with java and moodle-plugin-ci mustache linting which 4 | # causes causes tests to fail on xenial and bionic. (See https://github.com/blackboard-open-source/moodle-plugin-ci/issues/91). 5 | dist: xenial 6 | 7 | addons: 8 | firefox: latest 9 | chrome: stable 10 | postgresql: "9.6" 11 | 12 | services: 13 | - mysql 14 | 15 | php: 16 | - 7.0 17 | - 7.1 18 | - 7.2 19 | - 7.3 20 | env: 21 | global: 22 | - MOODLE_BRANCH=master 23 | - IGNORE_PATHS=tests/fixtures 24 | matrix: 25 | - DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE 26 | - DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE 27 | - DB=pgsql MOODLE_BRANCH=MOODLE_36_STABLE 28 | - DB=mysqli MOODLE_BRANCH=MOODLE_36_STABLE 29 | - DB=pgsql MOODLE_BRANCH=MOODLE_37_STABLE 30 | - DB=mysqli MOODLE_BRANCH=MOODLE_37_STABLE 31 | - DB=pgsql MOODLE_BRANCH=MOODLE_38_STABLE 32 | - DB=mysqli MOODLE_BRANCH=MOODLE_38_STABLE 33 | - DB=pgsql MOODLE_BRANCH=MOODLE_39_STABLE 34 | - DB=mysqli MOODLE_BRANCH=MOODLE_39_STABLE 35 | - DB=pgsql MOODLE_BRANCH=master 36 | - DB=mysqli MOODLE_BRANCH=master 37 | 38 | dist: xenial 39 | 40 | matrix: 41 | exclude: 42 | - php: 7.0 43 | env: DB=pgsql MOODLE_BRANCH=master 44 | - php: 7.0 45 | env: DB=mysqli MOODLE_BRANCH=master 46 | - php: 7.1 47 | env: DB=pgsql MOODLE_BRANCH=master 48 | - php: 7.1 49 | env: DB=mysqli MOODLE_BRANCH=master 50 | - php: 7.1 51 | env: DB=pgsql MOODLE_BRANCH=MOODLE_39_STABLE 52 | - php: 7.1 53 | env: DB=mysqli MOODLE_BRANCH=MOODLE_39_STABLE 54 | - php: 7.0 55 | env: DB=pgsql MOODLE_BRANCH=MOODLE_39_STABLE 56 | - php: 7.0 57 | env: DB=mysqli MOODLE_BRANCH=MOODLE_39_STABLE 58 | - php: 7.0 59 | env: DB=pgsql MOODLE_BRANCH=MOODLE_38_STABLE 60 | - php: 7.0 61 | env: DB=mysqli MOODLE_BRANCH=MOODLE_38_STABLE 62 | - php: 7.0 63 | env: DB=pgsql MOODLE_BRANCH=MOODLE_37_STABLE 64 | - php: 7.0 65 | env: DB=mysqli MOODLE_BRANCH=MOODLE_37_STABLE 66 | - php: 7.3 67 | env: DB=pgsql MOODLE_BRANCH=MOODLE_36_STABLE 68 | - php: 7.3 69 | env: DB=mysqli MOODLE_BRANCH=MOODLE_36_STABLE 70 | - php: 7.3 71 | env: DB=pgsql MOODLE_BRANCH=MOODLE_35_STABLE 72 | - php: 7.3 73 | env: DB=mysqli MOODLE_BRANCH=MOODLE_35_STABLE 74 | 75 | cache: 76 | directories: 77 | - $HOME/.composer/cache 78 | - $HOME/.npm 79 | 80 | before_install: 81 | - export MOODLE_VERSION=$(echo "$MOODLE_BRANCH" | cut -d'_' -f 2) 82 | - sudo apt-get update 83 | - phpenv config-rm xdebug.ini 84 | - if [ "$MOODLE_VERSION" = 36 ] || [ "$MOODLE_VERSION" -le 34 ]; then NVMVERSION=8.9; else NVMVERSION=14.0.0; fi 85 | - nvm install $NVMVERSION; 86 | - nvm use $NVMVERSION; 87 | - cd ../.. 88 | - composer selfupdate 89 | - composer create-project -n --no-dev --prefer-dist blackboard-open-source/moodle-plugin-ci ci dev-master 90 | - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" 91 | 92 | install: 93 | - moodle-plugin-ci install 94 | 95 | script: 96 | - moodle-plugin-ci phplint 97 | - moodle-plugin-ci codechecker 98 | - moodle-plugin-ci validate 99 | - moodle-plugin-ci savepoints 100 | - moodle-plugin-ci mustache 101 | # Grunt is currently broken due to a new way of building JS on 3.8+ 102 | # - moodle-plugin-ci grunt 103 | - moodle-plugin-ci phpunit 104 | - moodle-plugin-ci behat 105 | -------------------------------------------------------------------------------- /tests/json_export_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * JSON export unit tests. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | global $CFG; 28 | 29 | /** 30 | * JSON export unit tests. 31 | * 32 | * @package tool_trigger 33 | * @copyright Matt Porritt 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | 37 | class tool_trigger_json_export_testcase extends advanced_testcase { 38 | 39 | public function setup() { 40 | $this->resetAfterTest(true); 41 | } 42 | 43 | /** 44 | * Test filename creations 45 | */ 46 | public function test_set_filename() { 47 | $workflowobj = new \stdClass(); // Create workflow object. 48 | $workflowobj->name = '__testworkflow__'; 49 | $workflowobj->description = 'test workflow description'; 50 | $workflowobj->event = '\mod_scorm\event\user_report_viewed'; 51 | $workflowobj->steps = array ( 52 | 358000 => array( 53 | 'id' => 358000, 54 | 'name' => 'a', 55 | 'description' => 's', 56 | 'type' => 'lookups', 57 | 'stepclass' => '/tool_trigger/steps/lookups/user_lookup_step', 58 | 'data' => '{"useridfield":"userid","outputprefix":"user_","nodeleted":"1",' 59 | .'"stepdesc":"User lookup","typedesc":"Lookup"}', 60 | 'steporder' => 0, 61 | ), 62 | 358001 => array( 63 | 'id' => 358001, 64 | 'name' => 's', 65 | 'description' => 's', 66 | 'type' => 'lookups', 67 | 'stepclass' => '/tool_trigger/steps/lookups/course_lookup_step', 68 | 'data' => '{"courseidfield":"courseid","outputprefix":"course_","stepdesc":"Course lookup","typedesc":"Lookup"}', 69 | 'steporder' => 1 70 | ) 71 | 72 | ); 73 | $workflowobj->moodleversion = 2018080300; 74 | $workflowobj->pluginversion = 2018080500; 75 | 76 | $now = 1533446590; 77 | $expected = '__testworkflow___20180805_0523.json'; 78 | 79 | $jsonclass = new \tool_trigger\json\json_export($workflowobj); 80 | 81 | // We're testing a private method, so we need to setup reflector magic. 82 | $method = new ReflectionMethod('\tool_trigger\json\json_export', 'get_filename'); 83 | $method->setAccessible(true); // Allow accessing of private method. 84 | $proxy = $method->invoke($jsonclass, $workflowobj->name, $now); // Get result of invoked method. 85 | 86 | $this->assertEquals($expected, $proxy); 87 | 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /guzzle/README.md: -------------------------------------------------------------------------------- 1 | Guzzle, PHP HTTP client 2 | ======================= 3 | 4 | [![Build Status](https://travis-ci.org/guzzle/guzzle.svg?branch=master)](https://travis-ci.org/guzzle/guzzle) 5 | 6 | Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and 7 | trivial to integrate with web services. 8 | 9 | - Simple interface for building query strings, POST requests, streaming large 10 | uploads, streaming large downloads, using HTTP cookies, uploading JSON data, 11 | etc... 12 | - Can send both synchronous and asynchronous requests using the same interface. 13 | - Uses PSR-7 interfaces for requests, responses, and streams. This allows you 14 | to utilize other PSR-7 compatible libraries with Guzzle. 15 | - Abstracts away the underlying HTTP transport, allowing you to write 16 | environment and transport agnostic code; i.e., no hard dependency on cURL, 17 | PHP streams, sockets, or non-blocking event loops. 18 | - Middleware system allows you to augment and compose client behavior. 19 | 20 | ```php 21 | $client = new \GuzzleHttp\Client(); 22 | $res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); 23 | echo $res->getStatusCode(); 24 | // 200 25 | echo $res->getHeaderLine('content-type'); 26 | // 'application/json; charset=utf8' 27 | echo $res->getBody(); 28 | // '{"id": 1420053, "name": "guzzle", ...}' 29 | 30 | // Send an asynchronous request. 31 | $request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org'); 32 | $promise = $client->sendAsync($request)->then(function ($response) { 33 | echo 'I completed! ' . $response->getBody(); 34 | }); 35 | $promise->wait(); 36 | ``` 37 | 38 | ## Help and docs 39 | 40 | - [Documentation](http://guzzlephp.org/) 41 | - [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle) 42 | - [Gitter](https://gitter.im/guzzle/guzzle) 43 | 44 | 45 | ## Installing Guzzle 46 | 47 | The recommended way to install Guzzle is through 48 | [Composer](http://getcomposer.org). 49 | 50 | ```bash 51 | # Install Composer 52 | curl -sS https://getcomposer.org/installer | php 53 | ``` 54 | 55 | Next, run the Composer command to install the latest stable version of Guzzle: 56 | 57 | ```bash 58 | php composer.phar require guzzlehttp/guzzle 59 | ``` 60 | 61 | After installing, you need to require Composer's autoloader: 62 | 63 | ```php 64 | require 'vendor/autoload.php'; 65 | ``` 66 | 67 | You can then later update Guzzle using composer: 68 | 69 | ```bash 70 | composer.phar update 71 | ``` 72 | 73 | 74 | ## Version Guidance 75 | 76 | | Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version | 77 | |---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------| 78 | | 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 | 79 | | 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 | 80 | | 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 | 81 | | 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 | 82 | 83 | [guzzle-3-repo]: https://github.com/guzzle/guzzle3 84 | [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x 85 | [guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 86 | [guzzle-6-repo]: https://github.com/guzzle/guzzle 87 | [guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/ 88 | [guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/ 89 | [guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/ 90 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/PrepareBodyMiddleware.php: -------------------------------------------------------------------------------- 1 | nextHandler = $nextHandler; 23 | } 24 | 25 | /** 26 | * @param RequestInterface $request 27 | * @param array $options 28 | * 29 | * @return PromiseInterface 30 | */ 31 | public function __invoke(RequestInterface $request, array $options) 32 | { 33 | $fn = $this->nextHandler; 34 | 35 | // Don't do anything if the request has no body. 36 | if ($request->getBody()->getSize() === 0) { 37 | return $fn($request, $options); 38 | } 39 | 40 | $modify = []; 41 | 42 | // Add a default content-type if possible. 43 | if (!$request->hasHeader('Content-Type')) { 44 | if ($uri = $request->getBody()->getMetadata('uri')) { 45 | if ($type = Psr7\mimetype_from_filename($uri)) { 46 | $modify['set_headers']['Content-Type'] = $type; 47 | } 48 | } 49 | } 50 | 51 | // Add a default content-length or transfer-encoding header. 52 | if (!$request->hasHeader('Content-Length') 53 | && !$request->hasHeader('Transfer-Encoding') 54 | ) { 55 | $size = $request->getBody()->getSize(); 56 | if ($size !== null) { 57 | $modify['set_headers']['Content-Length'] = $size; 58 | } else { 59 | $modify['set_headers']['Transfer-Encoding'] = 'chunked'; 60 | } 61 | } 62 | 63 | // Add the expect header if needed. 64 | $this->addExpectHeader($request, $options, $modify); 65 | 66 | return $fn(Psr7\modify_request($request, $modify), $options); 67 | } 68 | 69 | private function addExpectHeader( 70 | RequestInterface $request, 71 | array $options, 72 | array &$modify 73 | ) { 74 | // Determine if the Expect header should be used 75 | if ($request->hasHeader('Expect')) { 76 | return; 77 | } 78 | 79 | $expect = isset($options['expect']) ? $options['expect'] : null; 80 | 81 | // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 82 | if ($expect === false || $request->getProtocolVersion() < 1.1) { 83 | return; 84 | } 85 | 86 | // The expect header is unconditionally enabled 87 | if ($expect === true) { 88 | $modify['set_headers']['Expect'] = '100-Continue'; 89 | return; 90 | } 91 | 92 | // By default, send the expect header when the payload is > 1mb 93 | if ($expect === null) { 94 | $expect = 1048576; 95 | } 96 | 97 | // Always add if the body cannot be rewound, the size cannot be 98 | // determined, or the size is greater than the cutoff threshold 99 | $body = $request->getBody(); 100 | $size = $body->getSize(); 101 | 102 | if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { 103 | $modify['set_headers']['Expect'] = '100-Continue'; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/TransferStats.php: -------------------------------------------------------------------------------- 1 | request = $request; 35 | $this->response = $response; 36 | $this->transferTime = $transferTime; 37 | $this->handlerErrorData = $handlerErrorData; 38 | $this->handlerStats = $handlerStats; 39 | } 40 | 41 | /** 42 | * @return RequestInterface 43 | */ 44 | public function getRequest() 45 | { 46 | return $this->request; 47 | } 48 | 49 | /** 50 | * Returns the response that was received (if any). 51 | * 52 | * @return ResponseInterface|null 53 | */ 54 | public function getResponse() 55 | { 56 | return $this->response; 57 | } 58 | 59 | /** 60 | * Returns true if a response was received. 61 | * 62 | * @return bool 63 | */ 64 | public function hasResponse() 65 | { 66 | return $this->response !== null; 67 | } 68 | 69 | /** 70 | * Gets handler specific error data. 71 | * 72 | * This might be an exception, a integer representing an error code, or 73 | * anything else. Relying on this value assumes that you know what handler 74 | * you are using. 75 | * 76 | * @return mixed 77 | */ 78 | public function getHandlerErrorData() 79 | { 80 | return $this->handlerErrorData; 81 | } 82 | 83 | /** 84 | * Get the effective URI the request was sent to. 85 | * 86 | * @return UriInterface 87 | */ 88 | public function getEffectiveUri() 89 | { 90 | return $this->request->getUri(); 91 | } 92 | 93 | /** 94 | * Get the estimated time the request was being transferred by the handler. 95 | * 96 | * @return float Time in seconds. 97 | */ 98 | public function getTransferTime() 99 | { 100 | return $this->transferTime; 101 | } 102 | 103 | /** 104 | * Gets an array of all of the handler specific transfer data. 105 | * 106 | * @return array 107 | */ 108 | public function getHandlerStats() 109 | { 110 | return $this->handlerStats; 111 | } 112 | 113 | /** 114 | * Get a specific handler statistic from the handler by name. 115 | * 116 | * @param string $stat Handler specific transfer stat to retrieve. 117 | * 118 | * @return mixed|null 119 | */ 120 | public function getHandlerStat($stat) 121 | { 122 | return isset($this->handlerStats[$stat]) 123 | ? $this->handlerStats[$stat] 124 | : null; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/BufferStream.php: -------------------------------------------------------------------------------- 1 | hwm = $hwm; 29 | } 30 | 31 | public function __toString() 32 | { 33 | return $this->getContents(); 34 | } 35 | 36 | public function getContents() 37 | { 38 | $buffer = $this->buffer; 39 | $this->buffer = ''; 40 | 41 | return $buffer; 42 | } 43 | 44 | public function close() 45 | { 46 | $this->buffer = ''; 47 | } 48 | 49 | public function detach() 50 | { 51 | $this->close(); 52 | } 53 | 54 | public function getSize() 55 | { 56 | return strlen($this->buffer); 57 | } 58 | 59 | public function isReadable() 60 | { 61 | return true; 62 | } 63 | 64 | public function isWritable() 65 | { 66 | return true; 67 | } 68 | 69 | public function isSeekable() 70 | { 71 | return false; 72 | } 73 | 74 | public function rewind() 75 | { 76 | $this->seek(0); 77 | } 78 | 79 | public function seek($offset, $whence = SEEK_SET) 80 | { 81 | throw new \RuntimeException('Cannot seek a BufferStream'); 82 | } 83 | 84 | public function eof() 85 | { 86 | return strlen($this->buffer) === 0; 87 | } 88 | 89 | public function tell() 90 | { 91 | throw new \RuntimeException('Cannot determine the position of a BufferStream'); 92 | } 93 | 94 | /** 95 | * Reads data from the buffer. 96 | */ 97 | public function read($length) 98 | { 99 | $currentLength = strlen($this->buffer); 100 | 101 | if ($length >= $currentLength) { 102 | // No need to slice the buffer because we don't have enough data. 103 | $result = $this->buffer; 104 | $this->buffer = ''; 105 | } else { 106 | // Slice up the result to provide a subset of the buffer. 107 | $result = substr($this->buffer, 0, $length); 108 | $this->buffer = substr($this->buffer, $length); 109 | } 110 | 111 | return $result; 112 | } 113 | 114 | /** 115 | * Writes data to the buffer. 116 | */ 117 | public function write($string) 118 | { 119 | $this->buffer .= $string; 120 | 121 | // TODO: What should happen here? 122 | if (strlen($this->buffer) >= $this->hwm) { 123 | return false; 124 | } 125 | 126 | return strlen($string); 127 | } 128 | 129 | public function getMetadata($key = null) 130 | { 131 | if ($key == 'hwm') { 132 | return $this->hwm; 133 | } 134 | 135 | return $key ? null : []; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/RetryMiddleware.php: -------------------------------------------------------------------------------- 1 | decider = $decider; 38 | $this->nextHandler = $nextHandler; 39 | $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; 40 | } 41 | 42 | /** 43 | * Default exponential backoff delay function. 44 | * 45 | * @param $retries 46 | * 47 | * @return int 48 | */ 49 | public static function exponentialDelay($retries) 50 | { 51 | return (int) pow(2, $retries - 1); 52 | } 53 | 54 | /** 55 | * @param RequestInterface $request 56 | * @param array $options 57 | * 58 | * @return PromiseInterface 59 | */ 60 | public function __invoke(RequestInterface $request, array $options) 61 | { 62 | if (!isset($options['retries'])) { 63 | $options['retries'] = 0; 64 | } 65 | 66 | $fn = $this->nextHandler; 67 | return $fn($request, $options) 68 | ->then( 69 | $this->onFulfilled($request, $options), 70 | $this->onRejected($request, $options) 71 | ); 72 | } 73 | 74 | private function onFulfilled(RequestInterface $req, array $options) 75 | { 76 | return function ($value) use ($req, $options) { 77 | if (!call_user_func( 78 | $this->decider, 79 | $options['retries'], 80 | $req, 81 | $value, 82 | null 83 | )) { 84 | return $value; 85 | } 86 | return $this->doRetry($req, $options, $value); 87 | }; 88 | } 89 | 90 | private function onRejected(RequestInterface $req, array $options) 91 | { 92 | return function ($reason) use ($req, $options) { 93 | if (!call_user_func( 94 | $this->decider, 95 | $options['retries'], 96 | $req, 97 | null, 98 | $reason 99 | )) { 100 | return \GuzzleHttp\Promise\rejection_for($reason); 101 | } 102 | return $this->doRetry($req, $options); 103 | }; 104 | } 105 | 106 | private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) 107 | { 108 | $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); 109 | 110 | return $this($request, $options); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /classes/workflow.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Workflow class. 19 | * 20 | * @package tool_trigger 21 | * @copyright Matt Porritt 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_trigger; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | /** 30 | * Worklfow class. 31 | * 32 | * @package tool_trigger 33 | * @copyright Matt Porritt 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class workflow { 37 | 38 | /** 39 | * @var \stdClass The rule object form database. 40 | */ 41 | public $workflow; 42 | 43 | /** 44 | * @var int The workflow ID. 45 | */ 46 | public $id; 47 | 48 | /** 49 | * @var string The event name. 50 | */ 51 | public $event; 52 | 53 | /** 54 | * @var int Is this workflow enabled. 55 | */ 56 | public $active; 57 | 58 | /** 59 | * @var int Is this workflow realtime. 60 | */ 61 | public $realtime; 62 | 63 | /** 64 | * @var int Is this workflow in draft mode. 65 | */ 66 | public $draft; 67 | 68 | /** 69 | * @var int When was this workflow last triggered. 70 | */ 71 | public $lasttriggered; 72 | 73 | /** 74 | * @var string 75 | */ 76 | public $descriptiontext; 77 | 78 | /** 79 | * @var int 80 | */ 81 | public $descriptionformat; 82 | 83 | /** 84 | * @var int 85 | */ 86 | public $numsteps; 87 | 88 | /** 89 | * Constructor. 90 | * 91 | * @param \stdClass $workflow 92 | */ 93 | public function __construct($workflow) { 94 | $this->workflow = $workflow; 95 | $this->id = $workflow->id; 96 | $this->event = $workflow->event; 97 | $this->active = $workflow->enabled; 98 | $this->realtime = $workflow->realtime; 99 | $this->debug = $workflow->debug; 100 | $this->draft = $workflow->draft; 101 | $this->lasttriggered = $workflow->timetriggered; 102 | 103 | $description = json_decode($workflow->description); 104 | $this->descriptiontext = $description->text; 105 | $this->descriptionformat = $description->format; 106 | 107 | // Optional extra field for storing the steps count. 108 | if (isset($workflow->numsteps)) { 109 | $this->numsteps = $workflow->numsteps; 110 | } 111 | } 112 | 113 | /** 114 | * Get name of workflow. 115 | * 116 | * @param \stdClass $context 117 | * @return \string 118 | */ 119 | public function get_name($context) { 120 | return format_text($this->workflow->name, FORMAT_HTML, array('context' => $context)); 121 | } 122 | 123 | /** 124 | * Get description of workflow. 125 | * 126 | * @param \stdClass $context 127 | * @return \string 128 | */ 129 | public function get_description($context) { 130 | return format_text($this->descriptiontext, $this->descriptionformat, array('context' => $context)); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /classes/steps/actions/assign_cohort_action_step.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Cohort assignment action step class. 19 | * 20 | * @package tool_trigger 21 | * @copyright Paul Damiani 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace tool_trigger\steps\actions; 26 | 27 | defined('MOODLE_INTERNAL') || die; 28 | require_once($CFG->dirroot.'/cohort/lib.php'); 29 | 30 | class assign_cohort_action_step extends base_action_step 31 | { 32 | 33 | use \tool_trigger\helper\datafield_manager; 34 | 35 | /** 36 | * The fields supplied by this step. 37 | * 38 | * @var array 39 | */ 40 | private static $stepfields = array(); 41 | 42 | /** 43 | * Returns the step name. 44 | * 45 | * @return string human readable step name. 46 | */ 47 | public static function get_step_name() { 48 | return get_string('assigncohortactionstepname', 'tool_trigger'); 49 | } 50 | 51 | /** 52 | * Returns the step name. 53 | * 54 | * @return string human readable step name. 55 | */ 56 | public static function get_step_desc() { 57 | return get_string('assigncohortactionstepdesc', 'tool_trigger'); 58 | } 59 | 60 | protected function init() { 61 | $this->useridfield = $this->data['useridfield']; 62 | $this->cohortidfield = $this->data['cohortidfield']; 63 | } 64 | 65 | /** 66 | * @param $step 67 | * @param $trigger 68 | * @param $event 69 | * @param $stepresults - result of previousstep to include in processing this step. 70 | * @return array if execution was succesful and the response from the execution. 71 | */ 72 | public function execute($step, $trigger, $event, $stepresults) { 73 | 74 | $datafields = $this->get_datafields($event, $stepresults); 75 | 76 | cohort_add_member($this->cohortidfield, $datafields[$this->useridfield]); 77 | 78 | return array(true, $stepresults); 79 | } 80 | 81 | /** 82 | * {@inheritDoc} 83 | * @see \tool_trigger\steps\base\base_step::add_extra_form_fields() 84 | */ 85 | public function form_definition_extra($form, $mform, $customdata) { 86 | $mform->addElement('text', 'useridfield', get_string('step_lookup_user_useridfield', 'tool_trigger')); 87 | $mform->setType('useridfield', PARAM_ALPHANUMEXT); 88 | $mform->addRule('useridfield', get_string('required'), 'required'); 89 | $mform->setDefault('useridfield', 'userid'); 90 | 91 | $mform->addElement('text', 'cohortidfield', get_string('cohortidfield', 'tool_trigger')); 92 | $mform->setType('cohortidfield', PARAM_INT); 93 | $mform->addRule('cohortidfield', get_string('required'), 'required'); 94 | $mform->setDefault('cohortidfield', '0'); 95 | } 96 | 97 | /** 98 | * Get a list of fields this step provides. 99 | * 100 | * @return array $stepfields The fields this step provides. 101 | */ 102 | public static function get_fields() { 103 | return self::$stepfields; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /stepdetails.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Trigger Historical step details. 19 | * 20 | * @package tool_trigger 21 | * @copyright Peter Burnett 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require_once(__DIR__ . '/../../../config.php'); 26 | require_once($CFG->libdir . '/adminlib.php'); 27 | admin_externalpage_setup('tool_trigger_worfklowsettings', '', null, '', array('pagelayout' => 'report')); 28 | 29 | $context = context_system::instance(); 30 | $PAGE->set_url(new moodle_url('/admin/tool/trigger/stepdetails.php')); 31 | 32 | // Get page information from DB. 33 | $stepid = required_param('id', PARAM_INT); 34 | $step = $DB->get_record('tool_trigger_run_hist', ['id' => $stepid]); 35 | $eventdata = json_decode($DB->get_field('tool_trigger_workflow_hist', 'event', ['id' => $step->runid])); 36 | $processor = new \tool_trigger\event_processor(); 37 | $event = $processor->restore_event($eventdata); 38 | // Re-encode the JSON data to make it presentable. 39 | $results = json_encode(json_decode($step->results), JSON_PRETTY_PRINT); 40 | 41 | $pagecontent = ''; 42 | if (!$step) { 43 | $pagecontent .= $OUTPUT->heading(get_string('stepnotfound', 'tool_trigger')); 44 | } else { 45 | // Output workflow id, run id, triggering event name + desc. 46 | // Then step id, step results. 47 | $pagecontent .= html_writer::tag('h4', get_string('workflowid', 'tool_trigger', $step->workflowid)); 48 | $pagecontent .= html_writer::tag('h4', get_string('triggernumberembed', 'tool_trigger', $step->runid)); 49 | $pagecontent .= html_writer::tag('h4', get_string('eventdescription', 'tool_trigger') . ': '); 50 | $pagecontent .= html_writer::tag('pre', var_export($event->get_name(), true)); 51 | $pagecontent .= html_writer::tag('pre', var_export($event->get_description(), true)) . '
'; 52 | $pagecontent .= html_writer::tag('h4', get_string('sttepidembed', 'tool_trigger', $step->id)); 53 | $pagecontent .= html_writer::tag('h4', get_string('stepresults', 'tool_trigger')); 54 | $pagecontent .= html_writer::tag('pre', var_export($results, true)); 55 | } 56 | 57 | $backbutton = new single_button(new moodle_url('/admin/tool/trigger/history.php', 58 | ['workflow' => $step->workflowid, 'run' => $step->runid]), 59 | get_string('back')); 60 | 61 | // Manually inject navigation nodes based on the page params. 62 | $workflowurl = new moodle_url('/admin/tool/trigger/history.php', ['workflow' => $step->workflowid]); 63 | $PAGE->navbar->add(get_string('workflowviewhistory', 'tool_trigger'), $workflowurl); 64 | 65 | $runurl = new moodle_url('/admin/tool/trigger/history.php', 66 | ['workflow' => $step->workflowid, 'run' => $step->runid]); 67 | $PAGE->navbar->add(get_string('viewdetailedrun', 'tool_trigger'), $runurl); 68 | 69 | $stepdetailsurl = new moodle_url('/admin/tool/trigger/stepdetails.php', ['id' => $stepid]); 70 | $PAGE->navbar->add(get_string('viewstepinfo', 'tool_trigger'), $stepdetailsurl); 71 | 72 | // Now output the page. 73 | echo $OUTPUT->header(); 74 | echo $OUTPUT->heading(get_string('viewstepinfo', 'tool_trigger')); 75 | echo '
'; 76 | echo $pagecontent; 77 | echo $OUTPUT->render($backbutton); 78 | echo $OUTPUT->footer(); 79 | -------------------------------------------------------------------------------- /manageworkflow.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This page lets admins manage workflows. 19 | * 20 | * @package tool_trigger 21 | * @copyright 2014 onwards Ankit Agarwal 22 | * @copyright 2018 onwards Catalyst IT 23 | * @author Aaron Wells 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | require_once(__DIR__ . '/../../../config.php'); 28 | require_once($CFG->libdir.'/adminlib.php'); 29 | 30 | require_login(); 31 | $context = context_system::instance(); 32 | require_capability('tool/trigger:manageworkflows', $context); 33 | 34 | $workflowid = required_param('workflowid', PARAM_INT); 35 | $action = required_param('action', PARAM_ALPHA); 36 | $confirm = optional_param('confirm', false, PARAM_BOOL); 37 | $status = optional_param('status', 0, PARAM_BOOL); 38 | 39 | if (!in_array($action, ['delete', 'copy'])) { 40 | print_error('invalidaction'); 41 | } 42 | 43 | $workflow = \tool_trigger\workflow_manager::get_workflow($workflowid); 44 | if (!$workflow) { 45 | print_error('invaliditemid'); 46 | } 47 | $workflowname = $workflow->get_name($context); 48 | 49 | // Set up the page. 50 | $url = new moodle_url( 51 | '/admin/tool/trigger/manageworkflow.php', 52 | ['workflowid' => $workflowid] 53 | ); 54 | $PAGE->set_context($context); 55 | $PAGE->set_url($url); 56 | $PAGE->set_pagelayout('admin'); 57 | $PAGE->set_title($workflowname); 58 | $PAGE->set_heading($workflowname); 59 | 60 | require_sesskey(); 61 | 62 | $workflowmanager = new \tool_trigger\workflow_manager(); 63 | 64 | switch ($action) { 65 | case 'copy': 66 | $newworkflow = $workflowmanager->copy_workflow($workflow); 67 | redirect( 68 | new moodle_url('/admin/tool/trigger/manage.php'), 69 | get_string('workflowcopysuccess', 'tool_trigger'), 70 | \core\output\notification::NOTIFY_SUCCESS 71 | ); 72 | break; 73 | case 'delete': 74 | if ($confirm) { 75 | $workflowmanager->delete_workflow($workflowid); 76 | redirect( 77 | new moodle_url('/admin/tool/trigger/manage.php'), 78 | get_string('workflowdeletesuccess', 'tool_trigger'), 79 | \core\output\notification::NOTIFY_SUCCESS 80 | ); 81 | } else { 82 | echo $OUTPUT->header(); 83 | 84 | $confirmurl = new moodle_url( 85 | $CFG->wwwroot. '/admin/tool/trigger/manageworkflow.php', 86 | [ 87 | 'workflowid' => $workflowid, 88 | 'action' => 'delete', 89 | 'confirm' => true, 90 | 'sesskey' => sesskey() 91 | ] 92 | ); 93 | $cancelurl = new moodle_url($CFG->wwwroot. '/admin/tool/trigger/manage.php'); 94 | $strconfirm = get_string('workflowdeleteareyousure', 'tool_trigger', $workflow->get_name($context)); 95 | 96 | echo $OUTPUT->confirm($strconfirm, $confirmurl, $cancelurl); 97 | echo $OUTPUT->footer(); 98 | exit(); 99 | } 100 | break; 101 | default: 102 | } 103 | -------------------------------------------------------------------------------- /tests/fixtures/user_event_fixture.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * A lot of our tests need to go through a similar set of steps to 19 | * create an event. This is a trait that does so. 20 | * 21 | * @package tool_trigger 22 | * @author Aaron Wells 23 | * @copyright Catalyst IT 2018 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | global $CFG; 30 | 31 | trait tool_trigger_user_event_fixture { 32 | public $user1 = null; 33 | public $user2 = null; 34 | public $course = null; 35 | public $event = null; 36 | 37 | /** 38 | * Create a "user_profile_viewed" event, of user1 viewing user2's 39 | * profile. And then run everything else as the cron user. 40 | */ 41 | public function setup_user_event() { 42 | $this->resetAfterTest(true); 43 | 44 | // Data for fields that are not automatically filled in by the data generator. 45 | $extrauserdata = [ 46 | 'description' => '

My description

', 47 | 'descriptionformat' => FORMAT_HTML, 48 | 'url' => 'https://www.example.com', 49 | 'picture' => 1 50 | ]; 51 | 52 | $this->user1 = $this->getDataGenerator()->create_user($extrauserdata); 53 | $this->user2 = $this->getDataGenerator()->create_user($extrauserdata); 54 | $this->course = $this->getDataGenerator()->create_course(); 55 | 56 | $this->setUser($this->user1); 57 | 58 | $this->event = \core\event\user_profile_viewed::create([ 59 | 'objectid' => $this->user2->id, 60 | 'relateduserid' => $this->user2->id, 61 | 'context' => context_user::instance($this->user2->id), 62 | 'other' => [ 63 | 'courseid' => $this->course->id, 64 | 'courseshortname' => $this->course->shortname, 65 | 'coursefullname' => $this->course->fullname 66 | ] 67 | ]); 68 | 69 | $this->event->trigger(); 70 | 71 | // Run as the cron user . 72 | cron_setup_user(); 73 | } 74 | 75 | /** 76 | * A helper function to create a user profile field. 77 | * 78 | * @param string $shortname Profile filed shortname. 79 | * @param string $datatype Data type for the field. E.g. text, textarea, checkbox, menu, datetime. 80 | * @param bool $forceunique Should the field store unique data. 81 | * 82 | * @return \stdClass 83 | */ 84 | public function add_user_custom_profile_field($shortname, $datatype, $forceunique = false) { 85 | global $DB; 86 | 87 | // Create a new profile field. 88 | $data = new \stdClass(); 89 | $data->shortname = $shortname; 90 | $data->datatype = $datatype; 91 | $data->name = 'Test ' . $shortname; 92 | $data->description = 'This is a test field'; 93 | $data->required = false; 94 | $data->locked = false; 95 | $data->forceunique = $forceunique; 96 | $data->signup = false; 97 | $data->visible = '0'; 98 | $data->categoryid = '0'; 99 | 100 | $DB->insert_record('user_info_field', $data); 101 | 102 | return $data; 103 | } 104 | } -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/StreamDecoratorTrait.php: -------------------------------------------------------------------------------- 1 | stream = $stream; 18 | } 19 | 20 | /** 21 | * Magic method used to create a new stream if streams are not added in 22 | * the constructor of a decorator (e.g., LazyOpenStream). 23 | * 24 | * @param string $name Name of the property (allows "stream" only). 25 | * 26 | * @return StreamInterface 27 | */ 28 | public function __get($name) 29 | { 30 | if ($name == 'stream') { 31 | $this->stream = $this->createStream(); 32 | return $this->stream; 33 | } 34 | 35 | throw new \UnexpectedValueException("$name not found on class"); 36 | } 37 | 38 | public function __toString() 39 | { 40 | try { 41 | if ($this->isSeekable()) { 42 | $this->seek(0); 43 | } 44 | return $this->getContents(); 45 | } catch (\Exception $e) { 46 | // Really, PHP? https://bugs.php.net/bug.php?id=53648 47 | trigger_error('StreamDecorator::__toString exception: ' 48 | . (string) $e, E_USER_ERROR); 49 | return ''; 50 | } 51 | } 52 | 53 | public function getContents() 54 | { 55 | return copy_to_string($this); 56 | } 57 | 58 | /** 59 | * Allow decorators to implement custom methods 60 | * 61 | * @param string $method Missing method name 62 | * @param array $args Method arguments 63 | * 64 | * @return mixed 65 | */ 66 | public function __call($method, array $args) 67 | { 68 | $result = call_user_func_array([$this->stream, $method], $args); 69 | 70 | // Always return the wrapped object if the result is a return $this 71 | return $result === $this->stream ? $this : $result; 72 | } 73 | 74 | public function close() 75 | { 76 | $this->stream->close(); 77 | } 78 | 79 | public function getMetadata($key = null) 80 | { 81 | return $this->stream->getMetadata($key); 82 | } 83 | 84 | public function detach() 85 | { 86 | return $this->stream->detach(); 87 | } 88 | 89 | public function getSize() 90 | { 91 | return $this->stream->getSize(); 92 | } 93 | 94 | public function eof() 95 | { 96 | return $this->stream->eof(); 97 | } 98 | 99 | public function tell() 100 | { 101 | return $this->stream->tell(); 102 | } 103 | 104 | public function isReadable() 105 | { 106 | return $this->stream->isReadable(); 107 | } 108 | 109 | public function isWritable() 110 | { 111 | return $this->stream->isWritable(); 112 | } 113 | 114 | public function isSeekable() 115 | { 116 | return $this->stream->isSeekable(); 117 | } 118 | 119 | public function rewind() 120 | { 121 | $this->seek(0); 122 | } 123 | 124 | public function seek($offset, $whence = SEEK_SET) 125 | { 126 | $this->stream->seek($offset, $whence); 127 | } 128 | 129 | public function read($length) 130 | { 131 | return $this->stream->read($length); 132 | } 133 | 134 | public function write($string) 135 | { 136 | return $this->stream->write($string); 137 | } 138 | 139 | /** 140 | * Implement in subclasses to dynamically create streams when requested. 141 | * 142 | * @return StreamInterface 143 | * @throws \BadMethodCallException 144 | */ 145 | protected function createStream() 146 | { 147 | throw new \BadMethodCallException('Not implemented'); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /cli/import_csv_records.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This script takes Postgres log files and inserts them 19 | * into the learnt events table. 20 | * 21 | * @package tool_trigger 22 | * @copyright Matt Porritt 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', 1); 27 | 28 | require(__DIR__.'/../../../../config.php'); 29 | require_once($CFG->libdir.'/clilib.php'); 30 | 31 | global $DB; 32 | 33 | // Get cli options. 34 | list($options, $unrecognized) = cli_get_params( 35 | array( 36 | 'source' => '', 37 | 'help' => false 38 | ), 39 | array( 40 | 'h' => 'help' 41 | ) 42 | ); 43 | 44 | if ($unrecognized) { 45 | $unrecognized = implode("\n ", $unrecognized); 46 | cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); 47 | } 48 | 49 | // Help information. 50 | if ($options['help'] || !($options['source'])) { 51 | $help = <<{$parameterarray[$i]} = $value; 98 | } 99 | 100 | // Insert reocrd into database. 101 | $DB->insert_record('tool_trigger_learn_events', $record); 102 | $count++; 103 | } 104 | 105 | } 106 | 107 | echo 'Processed: ' . $count . ' records' . "\n"; 108 | fclose($fp); 109 | } else { 110 | echo 'Unable to open file at location: ' . $filename; 111 | echo "\n"; 112 | exit(1); 113 | } 114 | 115 | exit(0); 116 | -------------------------------------------------------------------------------- /guzzle/GuzzleHttp/Psr7/Request.php: -------------------------------------------------------------------------------- 1 | method = strtoupper($method); 44 | $this->uri = $uri; 45 | $this->setHeaders($headers); 46 | $this->protocol = $version; 47 | 48 | if (!$this->hasHeader('Host')) { 49 | $this->updateHostFromUri(); 50 | } 51 | 52 | if ($body !== '' && $body !== null) { 53 | $this->stream = stream_for($body); 54 | } 55 | } 56 | 57 | public function getRequestTarget() 58 | { 59 | if ($this->requestTarget !== null) { 60 | return $this->requestTarget; 61 | } 62 | 63 | $target = $this->uri->getPath(); 64 | if ($target == '') { 65 | $target = '/'; 66 | } 67 | if ($this->uri->getQuery() != '') { 68 | $target .= '?' . $this->uri->getQuery(); 69 | } 70 | 71 | return $target; 72 | } 73 | 74 | public function withRequestTarget($requestTarget) 75 | { 76 | if (preg_match('#\s#', $requestTarget)) { 77 | throw new InvalidArgumentException( 78 | 'Invalid request target provided; cannot contain whitespace' 79 | ); 80 | } 81 | 82 | $new = clone $this; 83 | $new->requestTarget = $requestTarget; 84 | return $new; 85 | } 86 | 87 | public function getMethod() 88 | { 89 | return $this->method; 90 | } 91 | 92 | public function withMethod($method) 93 | { 94 | $new = clone $this; 95 | $new->method = strtoupper($method); 96 | return $new; 97 | } 98 | 99 | public function getUri() 100 | { 101 | return $this->uri; 102 | } 103 | 104 | public function withUri(UriInterface $uri, $preserveHost = false) 105 | { 106 | if ($uri === $this->uri) { 107 | return $this; 108 | } 109 | 110 | $new = clone $this; 111 | $new->uri = $uri; 112 | 113 | if (!$preserveHost) { 114 | $new->updateHostFromUri(); 115 | } 116 | 117 | return $new; 118 | } 119 | 120 | private function updateHostFromUri() 121 | { 122 | $host = $this->uri->getHost(); 123 | 124 | if ($host == '') { 125 | return; 126 | } 127 | 128 | if (($port = $this->uri->getPort()) !== null) { 129 | $host .= ':' . $port; 130 | } 131 | 132 | if (isset($this->headerNames['host'])) { 133 | $header = $this->headerNames['host']; 134 | } else { 135 | $header = 'Host'; 136 | $this->headerNames['host'] = 'Host'; 137 | } 138 | // Ensure Host is the first header. 139 | // See: http://tools.ietf.org/html/rfc7230#section-5.4 140 | $this->headers = [$header => [$host]] + $this->headers; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /amd/build/step_select.min.js: -------------------------------------------------------------------------------- 1 | define ("tool_trigger/step_select",["jquery","core/str","core/modal_factory","core/modal_events","core/templates","core/ajax","core/fragment","core/notification"],function(a,b,c,d,e,f,g,h){var u={},v,w,x="

Loading...

";function i(){var b=a("[name=stepjson]").val(),c=[];if(""!==b){c=JSON.parse(b)}return c}function j(b){a("[name=stepjson]").val(JSON.stringify(b));a("[name=isstepschanged]").val(1)}function k(){var a={jsonformdata:JSON.stringify({})};w.setBody(x);w.setBody(g.loadFragment("tool_trigger","new_base_form",v,a))}function l(b){var c=b.map(function(a,b){return{name:a.name,typedesc:a.typedesc,stepdesc:a.stepdesc,steporder:b}});e.render("tool_trigger/workflow_steps",{rows:c}).then(function(b){a("#steps-table").html(b);t()}).fail(function(){h.exception({message:"Error updating steps table"})})}function m(b){b.preventDefault();var c=w.getRoot().find("form"),d=c.serializeArray().reduce(function(a,b){if("sesskey"!==b.name&&!b.name.startsWith("_qf__")){a[b.name]=b.value}return a},{});d.stepdesc=a("[name=stepclass] option:selected").text();d.typedesc=a("[name=type] option:selected").text();f.call([{methodname:"tool_trigger_validate_form",args:{stepclass:d.stepclass,jsonformdata:JSON.stringify(c.serialize())}}])[0].done(function(){var a=i();if(0<=d.steporder){a[d.steporder]=d}else{a.push(d);d.steporder=a.length-1}j(a);l(a);w.hide()}).fail(function(){q(d.type,d.stepclass,"",c.serialize())})}function n(b){a("[name=stepclass]").empty().append(a("