├── 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 |
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 | {{# str }} stepname, tool_trigger {{/ str }}
40 | {{# str }} steptype, tool_trigger {{/ str }}
41 | {{# str }} stepclass, tool_trigger {{/ str }}
42 | {{# str }} manage, tool_trigger {{/ str }}
43 |
44 |
45 |
46 | {{#rows}}
47 |
48 | {{name}}
49 | {{typedesc}}
50 | {{stepdesc}}
51 |
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 |
57 |
58 | {{/rows}}
59 |
60 |
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 | 
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 | [](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("",{value:"",text:"Choose..."}));a.each(b,function(b,c){a("[name=stepclass]").append(a(" ",{value:c.class,text:c.name}))})}function o(a){f.call([{methodname:"tool_trigger_step_by_type",args:{steptype:a}}])[0].done(function(a){n(a)})}function p(){var b=a("[name=eventtomonitor]").val();return b}function q(a,b,c,d,e){if(c===void 0){c=""}if(d===void 0){d=""}if(e===void 0){e=0}w.setBody(x);w.setBody(g.loadFragment("tool_trigger","new_step_form",v,{steptype:a,stepclass:b,defaults:JSON.stringify(c),ajaxformdata:d,event:p(),existingsteps:JSON.stringify(i()),steporder:e}))}function r(){a("body").on("change","[name=type]",function(){o(this.value)});a("body").on("change","[name=stepclass]",function(){var b=a("[name=type]").val(),c=this.value;q(b,c,"","",-1)})}function s(b,c,d){var e=b[c],f=b[d];e.steporder=d;f.steporder=d;b[c]=f;b[d]=e;var g=a("tr.tool-trigger-step-table-row"),h=g.eq(c),i=g.eq(d),k=h.height(),m=i.height(),n=h.find("th").css("background-color"),o=i.find("th").css("background-color");a([h,i]).each(function(a,b){b[0].style.position="relative";b[0].style.top="0";b[0].style.transition="top 400ms";b.find("th,td").each(function(a,b){b.style.transition="background-color 400ms"})});window.setTimeout(function(){h[0].style.top=m+"px";h.find("td,th").each(function(a,b){b.style.backgroundColor=o});i[0].style.top=-1*k+"px";i.find("td,th").each(function(a,b){b.style.backgroundColor=n})});h.find(".tool-trigger-step-movedown").fadeTo(400,.1);i.find(".tool-trigger-step-moveup").fadeTo(400,.1).promise().always(function(){j(b);l(b)})}function t(){a(".tool-trigger-step-moveup").slice(1).removeClass("tool-trigger-initial-hidden").on("click",function(){var b=i(),c=a(this).data("steporder");if(0===c){return!0}s(b,c-1,c);return!0});a(".tool-trigger-step-movedown").slice(0,-1).removeClass("tool-trigger-initial-hidden").on("click",function(){var b=i(),c=a(this).data("steporder");if(c>=b.length-1){return!0}s(b,c,c+1);return!0});a(".tool-trigger-step-edit").removeClass("tool-trigger-initial-hidden").on("click",function(){w.setBody(x);w.show();var b=i(),c=a(this).data("steporder"),d=b[c];q(d.type,d.stepclass,d,void 0,c)});a(".tool-trigger-step-delete").removeClass("tool-trigger-initial-hidden").on("click",function(){var b=i(),c=a(this).data("steporder");b.splice(c,1);if(c<=b.length){b.slice(c).forEach(function(a){a.steporder=a.steporder-1})}j(b);a(this).closest("tr").fadeOut(function(){l(b)});return!0})}u.init=function(e){v=e;b.get_string("modaltitle","tool_trigger").then(function(b){c.create({type:c.types.SAVE_CANCEL,title:b,body:x,large:!0},a("#id_stepmodalbutton")).done(function(a){w=a;w.getRoot().on(d.save,m);w.getRoot().on(d.hidden,k);r();k()})});t()};return u});
2 | //# sourceMappingURL=step_select.min.js.map
3 |
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Psr7/FnStream.php:
--------------------------------------------------------------------------------
1 | methods = $methods;
28 |
29 | // Create the functions on the class
30 | foreach ($methods as $name => $fn) {
31 | $this->{'_fn_' . $name} = $fn;
32 | }
33 | }
34 |
35 | /**
36 | * Lazily determine which methods are not implemented.
37 | * @throws \BadMethodCallException
38 | */
39 | public function __get($name)
40 | {
41 | throw new \BadMethodCallException(str_replace('_fn_', '', $name)
42 | . '() is not implemented in the FnStream');
43 | }
44 |
45 | /**
46 | * The close method is called on the underlying stream only if possible.
47 | */
48 | public function __destruct()
49 | {
50 | if (isset($this->_fn_close)) {
51 | call_user_func($this->_fn_close);
52 | }
53 | }
54 |
55 | /**
56 | * Adds custom functionality to an underlying stream by intercepting
57 | * specific method calls.
58 | *
59 | * @param StreamInterface $stream Stream to decorate
60 | * @param array $methods Hash of method name to a closure
61 | *
62 | * @return FnStream
63 | */
64 | public static function decorate(StreamInterface $stream, array $methods)
65 | {
66 | // If any of the required methods were not provided, then simply
67 | // proxy to the decorated stream.
68 | foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
69 | $methods[$diff] = [$stream, $diff];
70 | }
71 |
72 | return new self($methods);
73 | }
74 |
75 | public function __toString()
76 | {
77 | return call_user_func($this->_fn___toString);
78 | }
79 |
80 | public function close()
81 | {
82 | return call_user_func($this->_fn_close);
83 | }
84 |
85 | public function detach()
86 | {
87 | return call_user_func($this->_fn_detach);
88 | }
89 |
90 | public function getSize()
91 | {
92 | return call_user_func($this->_fn_getSize);
93 | }
94 |
95 | public function tell()
96 | {
97 | return call_user_func($this->_fn_tell);
98 | }
99 |
100 | public function eof()
101 | {
102 | return call_user_func($this->_fn_eof);
103 | }
104 |
105 | public function isSeekable()
106 | {
107 | return call_user_func($this->_fn_isSeekable);
108 | }
109 |
110 | public function rewind()
111 | {
112 | call_user_func($this->_fn_rewind);
113 | }
114 |
115 | public function seek($offset, $whence = SEEK_SET)
116 | {
117 | call_user_func($this->_fn_seek, $offset, $whence);
118 | }
119 |
120 | public function isWritable()
121 | {
122 | return call_user_func($this->_fn_isWritable);
123 | }
124 |
125 | public function write($string)
126 | {
127 | return call_user_func($this->_fn_write, $string);
128 | }
129 |
130 | public function isReadable()
131 | {
132 | return call_user_func($this->_fn_isReadable);
133 | }
134 |
135 | public function read($length)
136 | {
137 | return call_user_func($this->_fn_read, $length);
138 | }
139 |
140 | public function getContents()
141 | {
142 | return call_user_func($this->_fn_getContents);
143 | }
144 |
145 | public function getMetadata($key = null)
146 | {
147 | return call_user_func($this->_fn_getMetadata, $key);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/edit.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Azure Search search engine 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 | $workflowid = optional_param('workflowid', 0, PARAM_INT);
31 |
32 | require_login();
33 |
34 | $url = new moodle_url("/admin/tool/trigger/edit.php", array('workflowid' => $workflowid));
35 | $context = context_system::instance();
36 |
37 | // Check for caps.
38 | require_capability('tool/trigger:manageworkflows', $context);
39 |
40 | // Set up the page.
41 | $PAGE->set_context($context);
42 | $PAGE->set_url($url);
43 | $PAGE->set_pagelayout('admin');
44 | if ($workflowid) {
45 | $pagetitlestr = get_string('editworkflow', 'tool_trigger');
46 | } else {
47 | $pagetitlestr = get_string('addworkflow', 'tool_trigger');
48 | }
49 | $PAGE->set_title($pagetitlestr);
50 | $PAGE->set_heading($pagetitlestr);
51 |
52 | if ($node = $PAGE->settingsnav->find('root', \navigation_node::TYPE_SITE_ADMIN)) {
53 | $PAGE->navbar->add($node->get_content(), $node->action());
54 | }
55 | foreach (array('tools', 'tool_trigger', 'tool_trigger_worfklowsettings') as $label) {
56 | if ($node = $PAGE->settingsnav->find($label, \navigation_node::TYPE_SETTING)) {
57 | $PAGE->navbar->add($node->get_content(), $node->action());
58 | }
59 | }
60 | $PAGE->navbar->add($pagetitlestr);
61 |
62 | // Load the javascript.
63 | $PAGE->requires->js_call_amd('tool_trigger/step_select', 'init', array($context->id));
64 |
65 | $eventlist = \tool_monitor\eventlist::get_all_eventlist();
66 |
67 | // Group the events by plugin.
68 | $pluginlist = \tool_monitor\eventlist::get_plugin_list($eventlist);
69 | $plugineventlist = [];
70 | foreach ($pluginlist as $plugin => $pluginname) {
71 | foreach ($eventlist[$plugin] as $event => $eventname) {
72 | // Filter out events which cannot be triggered for some reason.
73 | if (!$event::is_deprecated()) {
74 | $plugineventlist[$event] = "${pluginname}: ${eventname}";
75 | }
76 | }
77 | }
78 |
79 | // Get data ready for mform.
80 | $mform = new \tool_trigger\edit_form(
81 | null,
82 | ['plugineventlist' => $plugineventlist]
83 | );
84 |
85 | if ($mform->is_cancelled()) {
86 | // Handle form cancel operation.
87 | // Redirect back to workflow page.
88 | redirect(new moodle_url('/admin/tool/trigger/manage.php'));
89 |
90 | } else if ($mdata = $mform->get_data()) {
91 | // Process validated data.
92 | $workflowprocess = new \tool_trigger\workflow_process($mdata);
93 | $result = $workflowprocess->processform();
94 |
95 | $cache = \cache::make('tool_trigger', 'eventsubscriptions');
96 | $cache->purge();
97 |
98 | // Redirect back to workflow page and show success or failure.
99 | if ($result) {
100 | redirect(new moodle_url('/admin/tool/trigger/manage.php'), get_string('changessaved'));
101 | } else {
102 | redirect(new moodle_url('/admin/tool/trigger/manage.php'), get_string('errorsavingworkflow'));
103 | }
104 |
105 | } else {
106 | // This branch is executed if the form is submitted but the data doesn't validate,
107 | // or on the first display of the form.
108 |
109 | if ($workflowid) {
110 | $workflowprocess = new \tool_trigger\workflow_process(null);
111 | $mform->set_data($workflowprocess->to_form_defaults($workflowid));
112 | }
113 |
114 | // Build the page output.
115 | echo $OUTPUT->header();
116 | $mform->display();
117 | echo $OUTPUT->footer();
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Promise/Coroutine.php:
--------------------------------------------------------------------------------
1 | then(function ($v) { echo $v; });
37 | *
38 | * @param callable $generatorFn Generator function to wrap into a promise.
39 | *
40 | * @return Promise
41 | * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
42 | */
43 | final class Coroutine implements PromiseInterface
44 | {
45 | /**
46 | * @var PromiseInterface|null
47 | */
48 | private $currentPromise;
49 |
50 | /**
51 | * @var Generator
52 | */
53 | private $generator;
54 |
55 | /**
56 | * @var Promise
57 | */
58 | private $result;
59 |
60 | public function __construct(callable $generatorFn)
61 | {
62 | $this->generator = $generatorFn();
63 | $this->result = new Promise(function () {
64 | while (isset($this->currentPromise)) {
65 | $this->currentPromise->wait();
66 | }
67 | });
68 | $this->nextCoroutine($this->generator->current());
69 | }
70 |
71 | public function then(
72 | callable $onFulfilled = null,
73 | callable $onRejected = null
74 | ) {
75 | return $this->result->then($onFulfilled, $onRejected);
76 | }
77 |
78 | public function otherwise(callable $onRejected)
79 | {
80 | return $this->result->otherwise($onRejected);
81 | }
82 |
83 | public function wait($unwrap = true)
84 | {
85 | return $this->result->wait($unwrap);
86 | }
87 |
88 | public function getState()
89 | {
90 | return $this->result->getState();
91 | }
92 |
93 | public function resolve($value)
94 | {
95 | $this->result->resolve($value);
96 | }
97 |
98 | public function reject($reason)
99 | {
100 | $this->result->reject($reason);
101 | }
102 |
103 | public function cancel()
104 | {
105 | $this->currentPromise->cancel();
106 | $this->result->cancel();
107 | }
108 |
109 | private function nextCoroutine($yielded)
110 | {
111 | $this->currentPromise = promise_for($yielded)
112 | ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
113 | }
114 |
115 | /**
116 | * @internal
117 | */
118 | public function _handleSuccess($value)
119 | {
120 | unset($this->currentPromise);
121 | try {
122 | $next = $this->generator->send($value);
123 | if ($this->generator->valid()) {
124 | $this->nextCoroutine($next);
125 | } else {
126 | $this->result->resolve($value);
127 | }
128 | } catch (Exception $exception) {
129 | $this->result->reject($exception);
130 | } catch (Throwable $throwable) {
131 | $this->result->reject($throwable);
132 | }
133 | }
134 |
135 | /**
136 | * @internal
137 | */
138 | public function _handleFailure($reason)
139 | {
140 | unset($this->currentPromise);
141 | try {
142 | $nextYield = $this->generator->throw(exception_for($reason));
143 | // The throw was caught, so keep iterating on the coroutine
144 | $this->nextCoroutine($nextYield);
145 | } catch (Exception $exception) {
146 | $this->result->reject($exception);
147 | } catch (Throwable $throwable) {
148 | $this->result->reject($throwable);
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Psr7/Response.php:
--------------------------------------------------------------------------------
1 | 'Continue',
17 | 101 => 'Switching Protocols',
18 | 102 => 'Processing',
19 | 200 => 'OK',
20 | 201 => 'Created',
21 | 202 => 'Accepted',
22 | 203 => 'Non-Authoritative Information',
23 | 204 => 'No Content',
24 | 205 => 'Reset Content',
25 | 206 => 'Partial Content',
26 | 207 => 'Multi-status',
27 | 208 => 'Already Reported',
28 | 300 => 'Multiple Choices',
29 | 301 => 'Moved Permanently',
30 | 302 => 'Found',
31 | 303 => 'See Other',
32 | 304 => 'Not Modified',
33 | 305 => 'Use Proxy',
34 | 306 => 'Switch Proxy',
35 | 307 => 'Temporary Redirect',
36 | 400 => 'Bad Request',
37 | 401 => 'Unauthorized',
38 | 402 => 'Payment Required',
39 | 403 => 'Forbidden',
40 | 404 => 'Not Found',
41 | 405 => 'Method Not Allowed',
42 | 406 => 'Not Acceptable',
43 | 407 => 'Proxy Authentication Required',
44 | 408 => 'Request Time-out',
45 | 409 => 'Conflict',
46 | 410 => 'Gone',
47 | 411 => 'Length Required',
48 | 412 => 'Precondition Failed',
49 | 413 => 'Request Entity Too Large',
50 | 414 => 'Request-URI Too Large',
51 | 415 => 'Unsupported Media Type',
52 | 416 => 'Requested range not satisfiable',
53 | 417 => 'Expectation Failed',
54 | 418 => 'I\'m a teapot',
55 | 422 => 'Unprocessable Entity',
56 | 423 => 'Locked',
57 | 424 => 'Failed Dependency',
58 | 425 => 'Unordered Collection',
59 | 426 => 'Upgrade Required',
60 | 428 => 'Precondition Required',
61 | 429 => 'Too Many Requests',
62 | 431 => 'Request Header Fields Too Large',
63 | 451 => 'Unavailable For Legal Reasons',
64 | 500 => 'Internal Server Error',
65 | 501 => 'Not Implemented',
66 | 502 => 'Bad Gateway',
67 | 503 => 'Service Unavailable',
68 | 504 => 'Gateway Time-out',
69 | 505 => 'HTTP Version not supported',
70 | 506 => 'Variant Also Negotiates',
71 | 507 => 'Insufficient Storage',
72 | 508 => 'Loop Detected',
73 | 511 => 'Network Authentication Required',
74 | ];
75 |
76 | /** @var string */
77 | private $reasonPhrase = '';
78 |
79 | /** @var int */
80 | private $statusCode = 200;
81 |
82 | /**
83 | * @param int $status Status code
84 | * @param array $headers Response headers
85 | * @param string|null|resource|StreamInterface $body Response body
86 | * @param string $version Protocol version
87 | * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
88 | */
89 | public function __construct(
90 | $status = 200,
91 | array $headers = [],
92 | $body = null,
93 | $version = '1.1',
94 | $reason = null
95 | ) {
96 | $this->statusCode = (int) $status;
97 |
98 | if ($body !== '' && $body !== null) {
99 | $this->stream = stream_for($body);
100 | }
101 |
102 | $this->setHeaders($headers);
103 | if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
104 | $this->reasonPhrase = self::$phrases[$this->statusCode];
105 | } else {
106 | $this->reasonPhrase = (string) $reason;
107 | }
108 |
109 | $this->protocol = $version;
110 | }
111 |
112 | public function getStatusCode()
113 | {
114 | return $this->statusCode;
115 | }
116 |
117 | public function getReasonPhrase()
118 | {
119 | return $this->reasonPhrase;
120 | }
121 |
122 | public function withStatus($code, $reasonPhrase = '')
123 | {
124 | $new = clone $this;
125 | $new->statusCode = (int) $code;
126 | if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
127 | $reasonPhrase = self::$phrases[$new->statusCode];
128 | }
129 | $new->reasonPhrase = $reasonPhrase;
130 | return $new;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/classes/helper/processor_helper.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * trait to help in processing events.
19 | *
20 | * @package tool_trigger
21 | * @author Dmitrii Metelkin
22 | * @copyright 2019 Catalyst IT
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 | */
25 |
26 | namespace tool_trigger\helper;
27 |
28 | defined('MOODLE_INTERNAL') || die();
29 |
30 | trait processor_helper {
31 |
32 | /**
33 | * Returns an event from the log data.
34 | *
35 | * @param \stdClass $data Log data
36 | * @return \core\event\base
37 | */
38 | public function restore_event(\stdClass $data) {
39 | if (empty((array)$data)) {
40 | return null;
41 | }
42 | $extra = array('origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid);
43 | $data = (array)$data;
44 | $data['other'] = unserialize($data['other']);
45 | if ($data['other'] === false) {
46 | $data['other'] = array();
47 | }
48 | unset($data['origin']);
49 | unset($data['ip']);
50 | unset($data['realuserid']);
51 | unset($data['id']);
52 |
53 | if (!$event = \core\event\base::restore($data, $extra)) {
54 | return null;
55 | }
56 |
57 | return $event;
58 | }
59 |
60 | /**
61 | * Execute workflow step.
62 | *
63 | * @param \stdClass $step The `tool_trigger_steps` record for this step instance
64 | * @param \stdClass $trigger The `tool_trigger_queue` record for this execution
65 | * of the workflow.
66 | * @param \core\event\base $event The deserialized event object that triggered this execution
67 | * @param array $stepresults To pass to the step.
68 | *
69 | * @return array
70 | */
71 | public function execute_step($step, $trigger, $event, $stepresults) {
72 | $workflowmanager = new \tool_trigger\workflow_manager();
73 |
74 | $stepobj = $workflowmanager->validate_and_make_step($step->stepclass, $step->data);
75 |
76 | return $stepobj->execute($step, $trigger, $event, $stepresults);
77 | }
78 |
79 | /**
80 | * Get steps for the given workflow.
81 | *
82 | * @param int $workflowid Workflow ID.
83 | *
84 | * @return array
85 | */
86 | public function get_workflow_steps($workflowid) {
87 | global $DB;
88 |
89 | return $DB->get_records('tool_trigger_steps', ['workflowid' => $workflowid], 'steporder');
90 | }
91 |
92 | /**
93 | * Update workflow record.
94 | *
95 | * @param \stdClass $workflow Workflow record.
96 | */
97 | public function update_workflow_record(\stdClass $workflow) {
98 | global $DB;
99 |
100 | if (!empty($workflow->id)) {
101 | $DB->update_record('tool_trigger_workflows', $workflow);
102 | }
103 | }
104 |
105 | /**
106 | * Insert queue records in DB.
107 | *
108 | * @param array $records A list of queue records to insert.
109 | */
110 | public function insert_queue_records(array $records) {
111 | global $DB;
112 |
113 | $DB->insert_records('tool_trigger_queue', $records);
114 | }
115 |
116 | /**
117 | * Update queue record.
118 | *
119 | * @param \stdClass $queue Queue record.
120 | */
121 | public function update_queue_record(\stdClass $queue) {
122 | global $DB;
123 |
124 | if (!empty($queue->id)) {
125 | $DB->update_record('tool_trigger_queue', $queue);
126 | }
127 | }
128 |
129 | /**
130 | * Get event record from DB.
131 | *
132 | * @param int $eventid Event record ID.
133 | *
134 | * @return mixed
135 | */
136 | public function get_event_record($eventid) {
137 | global $DB;
138 |
139 | return $DB->get_record('tool_trigger_events', ['id' => $eventid]);
140 | }
141 | }
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Psr7/PumpStream.php:
--------------------------------------------------------------------------------
1 | source = $source;
46 | $this->size = isset($options['size']) ? $options['size'] : null;
47 | $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
48 | $this->buffer = new BufferStream();
49 | }
50 |
51 | public function __toString()
52 | {
53 | try {
54 | return copy_to_string($this);
55 | } catch (\Exception $e) {
56 | return '';
57 | }
58 | }
59 |
60 | public function close()
61 | {
62 | $this->detach();
63 | }
64 |
65 | public function detach()
66 | {
67 | $this->tellPos = false;
68 | $this->source = null;
69 | }
70 |
71 | public function getSize()
72 | {
73 | return $this->size;
74 | }
75 |
76 | public function tell()
77 | {
78 | return $this->tellPos;
79 | }
80 |
81 | public function eof()
82 | {
83 | return !$this->source;
84 | }
85 |
86 | public function isSeekable()
87 | {
88 | return false;
89 | }
90 |
91 | public function rewind()
92 | {
93 | $this->seek(0);
94 | }
95 |
96 | public function seek($offset, $whence = SEEK_SET)
97 | {
98 | throw new \RuntimeException('Cannot seek a PumpStream');
99 | }
100 |
101 | public function isWritable()
102 | {
103 | return false;
104 | }
105 |
106 | public function write($string)
107 | {
108 | throw new \RuntimeException('Cannot write to a PumpStream');
109 | }
110 |
111 | public function isReadable()
112 | {
113 | return true;
114 | }
115 |
116 | public function read($length)
117 | {
118 | $data = $this->buffer->read($length);
119 | $readLen = strlen($data);
120 | $this->tellPos += $readLen;
121 | $remaining = $length - $readLen;
122 |
123 | if ($remaining) {
124 | $this->pump($remaining);
125 | $data .= $this->buffer->read($remaining);
126 | $this->tellPos += strlen($data) - $readLen;
127 | }
128 |
129 | return $data;
130 | }
131 |
132 | public function getContents()
133 | {
134 | $result = '';
135 | while (!$this->eof()) {
136 | $result .= $this->read(1000000);
137 | }
138 |
139 | return $result;
140 | }
141 |
142 | public function getMetadata($key = null)
143 | {
144 | if (!$key) {
145 | return $this->metadata;
146 | }
147 |
148 | return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
149 | }
150 |
151 | private function pump($length)
152 | {
153 | if ($this->source) {
154 | do {
155 | $data = call_user_func($this->source, $length);
156 | if ($data === false || $data === null) {
157 | $this->source = null;
158 | return;
159 | }
160 | $this->buffer->write($data);
161 | $length -= strlen($data);
162 | } while ($length > 0);
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Psr7/CachingStream.php:
--------------------------------------------------------------------------------
1 | remoteStream = $stream;
31 | $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
32 | }
33 |
34 | public function getSize()
35 | {
36 | return max($this->stream->getSize(), $this->remoteStream->getSize());
37 | }
38 |
39 | public function rewind()
40 | {
41 | $this->seek(0);
42 | }
43 |
44 | public function seek($offset, $whence = SEEK_SET)
45 | {
46 | if ($whence == SEEK_SET) {
47 | $byte = $offset;
48 | } elseif ($whence == SEEK_CUR) {
49 | $byte = $offset + $this->tell();
50 | } elseif ($whence == SEEK_END) {
51 | $size = $this->remoteStream->getSize();
52 | if ($size === null) {
53 | $size = $this->cacheEntireStream();
54 | }
55 | $byte = $size + $offset;
56 | } else {
57 | throw new \InvalidArgumentException('Invalid whence');
58 | }
59 |
60 | $diff = $byte - $this->stream->getSize();
61 |
62 | if ($diff > 0) {
63 | // Read the remoteStream until we have read in at least the amount
64 | // of bytes requested, or we reach the end of the file.
65 | while ($diff > 0 && !$this->remoteStream->eof()) {
66 | $this->read($diff);
67 | $diff = $byte - $this->stream->getSize();
68 | }
69 | } else {
70 | // We can just do a normal seek since we've already seen this byte.
71 | $this->stream->seek($byte);
72 | }
73 | }
74 |
75 | public function read($length)
76 | {
77 | // Perform a regular read on any previously read data from the buffer
78 | $data = $this->stream->read($length);
79 | $remaining = $length - strlen($data);
80 |
81 | // More data was requested so read from the remote stream
82 | if ($remaining) {
83 | // If data was written to the buffer in a position that would have
84 | // been filled from the remote stream, then we must skip bytes on
85 | // the remote stream to emulate overwriting bytes from that
86 | // position. This mimics the behavior of other PHP stream wrappers.
87 | $remoteData = $this->remoteStream->read(
88 | $remaining + $this->skipReadBytes
89 | );
90 |
91 | if ($this->skipReadBytes) {
92 | $len = strlen($remoteData);
93 | $remoteData = substr($remoteData, $this->skipReadBytes);
94 | $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
95 | }
96 |
97 | $data .= $remoteData;
98 | $this->stream->write($remoteData);
99 | }
100 |
101 | return $data;
102 | }
103 |
104 | public function write($string)
105 | {
106 | // When appending to the end of the currently read stream, you'll want
107 | // to skip bytes from being read from the remote stream to emulate
108 | // other stream wrappers. Basically replacing bytes of data of a fixed
109 | // length.
110 | $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
111 | if ($overflow > 0) {
112 | $this->skipReadBytes += $overflow;
113 | }
114 |
115 | return $this->stream->write($string);
116 | }
117 |
118 | public function eof()
119 | {
120 | return $this->stream->eof() && $this->remoteStream->eof();
121 | }
122 |
123 | /**
124 | * Close both the remote stream and buffer stream
125 | */
126 | public function close()
127 | {
128 | $this->remoteStream->close() && $this->stream->close();
129 | }
130 |
131 | private function cacheEntireStream()
132 | {
133 | $target = new FnStream(['write' => 'strlen']);
134 | copy_to_stream($this, $target);
135 |
136 | return $this->tell();
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/classes/import_form.php:
--------------------------------------------------------------------------------
1 | .
16 |
17 | /**
18 | * Import workflow form 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 | require_once("$CFG->libdir/formslib.php");
30 |
31 | /**
32 | * Import workflow form class.
33 | *
34 | * @package tool_trigger
35 | * @copyright Matt Porritt
36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 | */
38 | class import_form extends \moodleform {
39 |
40 | /**
41 | * Imported worklow JSON files must be at least this version,
42 | * to be compatible with the import plugin.
43 | * Update this value when the workflow schema changes.
44 | *
45 | * @var int
46 | */
47 | private $importversion = 2018082400;
48 |
49 | /**
50 | * Build form for importing woekflows.
51 | *
52 | * {@inheritDoc}
53 | * @see \moodleform::definition()
54 | */
55 | public function definition() {
56 |
57 | $mform = $this->_form;
58 |
59 | // Workflow file.
60 | $mform->addElement('filepicker', 'userfile', get_string('workflowfile', 'tool_trigger'), null,
61 | array('maxbytes' => 256000, 'accepted_types' => '.json'));
62 | $mform->addRule('userfile', get_string('required'), 'required');
63 | }
64 |
65 | /**
66 | * Validate uploaded JSON file.
67 | *
68 | * @param array $data array of ("fieldname"=>value) of submitted data
69 | * @param array $files array of uploaded files "element_name"=>tmp_file_path
70 | * @return array of "element_name"=>"error_description" if there are errors,
71 | * or an empty array if everything is OK (true allowed for backwards compatibility too).
72 | */
73 | public function validation($data, $files) {
74 | global $USER;
75 |
76 | $validationerrors = array();
77 |
78 | // Get the file from the filestystem. $files will always be empty.
79 | $fs = get_file_storage();
80 |
81 | $context = \context_user::instance($USER->id);
82 | $itemid = $data['userfile'];
83 |
84 | // This is how core gets files in this case.
85 | if (!$files = $fs->get_area_files($context->id, 'user', 'draft', $itemid, 'id DESC', false)) {
86 | $validationerrors['nofile'] = get_string('noworkflowfile', 'tool_trigger');
87 | return $validationerrors;
88 | }
89 | $file = reset($files);
90 |
91 | // Check if file is valid JSON.
92 | $contentjson = $file->get_content();
93 | $contentobj = json_decode($contentjson);
94 |
95 | if (!$contentobj) {
96 | $validationerrors['invalidjson'] = get_string('invalidjson', 'tool_trigger');
97 | return $validationerrors;
98 | }
99 |
100 | // Check if file version is compatible.
101 | $versioncompatible = $this->is_version_compatible($contentobj->pluginversion);
102 | if (!$versioncompatible) {
103 | $validationerrors['invalidversion'] = get_string('invalidversion', 'tool_trigger');
104 | return $validationerrors;
105 | }
106 |
107 | return $validationerrors;
108 | }
109 |
110 | /**
111 | * Get the errors returned during form validation.
112 | *
113 | * @return array|mixed
114 | */
115 | public function get_errors() {
116 | $form = $this->_form;
117 | $errors = $form->_errors;
118 |
119 | return $errors;
120 | }
121 |
122 | /**
123 | * Check if the version of the workflow import file
124 | * is compatible with the installed version of the plugin.
125 | *
126 | * @param string $pluginversion
127 | * @return boolean
128 | */
129 | private function is_version_compatible($pluginversion) {
130 | if ((int)$pluginversion < $this->importversion) {
131 | return false;
132 | } else {
133 | return true;
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/guzzle/GuzzleHttp/Psr7/LimitStream.php:
--------------------------------------------------------------------------------
1 | stream = $stream;
33 | $this->setLimit($limit);
34 | $this->setOffset($offset);
35 | }
36 |
37 | public function eof()
38 | {
39 | // Always return true if the underlying stream is EOF
40 | if ($this->stream->eof()) {
41 | return true;
42 | }
43 |
44 | // No limit and the underlying stream is not at EOF
45 | if ($this->limit == -1) {
46 | return false;
47 | }
48 |
49 | return $this->stream->tell() >= $this->offset + $this->limit;
50 | }
51 |
52 | /**
53 | * Returns the size of the limited subset of data
54 | * {@inheritdoc}
55 | */
56 | public function getSize()
57 | {
58 | if (null === ($length = $this->stream->getSize())) {
59 | return null;
60 | } elseif ($this->limit == -1) {
61 | return $length - $this->offset;
62 | } else {
63 | return min($this->limit, $length - $this->offset);
64 | }
65 | }
66 |
67 | /**
68 | * Allow for a bounded seek on the read limited stream
69 | * {@inheritdoc}
70 | */
71 | public function seek($offset, $whence = SEEK_SET)
72 | {
73 | if ($whence !== SEEK_SET || $offset < 0) {
74 | throw new \RuntimeException(sprintf(
75 | 'Cannot seek to offset % with whence %s',
76 | $offset,
77 | $whence
78 | ));
79 | }
80 |
81 | $offset += $this->offset;
82 |
83 | if ($this->limit !== -1) {
84 | if ($offset > $this->offset + $this->limit) {
85 | $offset = $this->offset + $this->limit;
86 | }
87 | }
88 |
89 | $this->stream->seek($offset);
90 | }
91 |
92 | /**
93 | * Give a relative tell()
94 | * {@inheritdoc}
95 | */
96 | public function tell()
97 | {
98 | return $this->stream->tell() - $this->offset;
99 | }
100 |
101 | /**
102 | * Set the offset to start limiting from
103 | *
104 | * @param int $offset Offset to seek to and begin byte limiting from
105 | *
106 | * @throws \RuntimeException if the stream cannot be seeked.
107 | */
108 | public function setOffset($offset)
109 | {
110 | $current = $this->stream->tell();
111 |
112 | if ($current !== $offset) {
113 | // If the stream cannot seek to the offset position, then read to it
114 | if ($this->stream->isSeekable()) {
115 | $this->stream->seek($offset);
116 | } elseif ($current > $offset) {
117 | throw new \RuntimeException("Could not seek to stream offset $offset");
118 | } else {
119 | $this->stream->read($offset - $current);
120 | }
121 | }
122 |
123 | $this->offset = $offset;
124 | }
125 |
126 | /**
127 | * Set the limit of bytes that the decorator allows to be read from the
128 | * stream.
129 | *
130 | * @param int $limit Number of bytes to allow to be read from the stream.
131 | * Use -1 for no limit.
132 | */
133 | public function setLimit($limit)
134 | {
135 | $this->limit = $limit;
136 | }
137 |
138 | public function read($length)
139 | {
140 | if ($this->limit == -1) {
141 | return $this->stream->read($length);
142 | }
143 |
144 | // Check if the current position is less than the total allowed
145 | // bytes + original offset
146 | $remaining = ($this->offset + $this->limit) - $this->stream->tell();
147 | if ($remaining > 0) {
148 | // Only return the amount of requested data, ensuring that the byte
149 | // limit is not exceeded
150 | return $this->stream->read(min($remaining, $length));
151 | }
152 |
153 | return '';
154 | }
155 | }
156 |
--------------------------------------------------------------------------------