diff --git a/classes/event/block_acclaim_issue_badge.php b/classes/event/block_acclaim_create_pending_badge.php similarity index 95% rename from classes/event/block_acclaim_issue_badge.php rename to classes/event/block_acclaim_create_pending_badge.php index 81da7a7..5fd54e0 100755 --- a/classes/event/block_acclaim_issue_badge.php +++ b/classes/event/block_acclaim_create_pending_badge.php @@ -23,7 +23,7 @@ namespace block_acclaim\event; defined('MOODLE_INTERNAL') || die(); -class block_acclaim_issue_badge extends \core\event\base { +class block_acclaim_create_pending_badge extends \core\event\base { protected function init() { $this->data['crud'] = 'c'; // c(reate), r(ead), u(pdate), d(elete) diff --git a/classes/group_observers.php b/classes/group_observers.php index 2917459..a5b5187 100755 --- a/classes/group_observers.php +++ b/classes/group_observers.php @@ -28,23 +28,9 @@ require_once($CFG->dirroot . '/blocks/acclaim/lib.php'); class group_observers { - public static function block_acclaim_issue_badge($event) + public static function block_acclaim_create_pending_badge($event) { - global $DB; - $course = block_acclaim_get_block_course($event->courseid); - $expires_timestamp = ""; - - if($course->expiration){ - $expires_timestamp = $course->expiration; - } - - $data = block_acclaim_create_data_array($event, $course->badgeid, $expires_timestamp); - $url = block_acclaim_get_issue_badge_url(); - $token = block_acclaim_get_request_token(); - $return_code = block_acclaim_issue_badge_request($data, $url, $token); - if($return_code != 201){ - error_log("failed to issue badge, return code: ".$return_code); - } + block_acclaim_create_pending_badge($event); } } diff --git a/classes/task/issue_badges.php b/classes/task/issue_badges.php new file mode 100644 index 0000000..eb4752e --- /dev/null +++ b/classes/task/issue_badges.php @@ -0,0 +1,44 @@ +. + +/** + * Create request to issue a new credential + * + * @package block_acclaim + * @copyright 2014 Yancy Ribbens + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace block_acclaim\task; + +defined('MOODLE_INTERNAL') || die(); + +class issue_badges extends \core\task\scheduled_task { + public function get_name() { + return get_string('issuecredentials', 'block_acclaim'); + } + + public function execute() { + global $CFG; + require_once($CFG->libdir. '/filelib.php'); + require_once($CFG->dirroot . '/blocks/acclaim/lib.php'); + $url = block_acclaim_get_issue_badge_url(); + $token = block_acclaim_get_request_token(); + $curl = new \curl; + block_acclaim_issue_badge($curl, time(), $url, $token); + } +} diff --git a/db/events.php b/db/events.php index 7ddcfb7..8aa7885 100755 --- a/db/events.php +++ b/db/events.php @@ -29,6 +29,6 @@ $observers = array( array( 'eventname' => '\core\event\course_completed', - 'callback' => '\block_acclaim\group_observers::block_acclaim_issue_badge', + 'callback' => '\block_acclaim\group_observers::block_acclaim_create_pending_badge', ), ); diff --git a/db/install.xml b/db/install.xml index b1b6498..d04a622 100755 --- a/db/install.xml +++ b/db/install.xml @@ -4,7 +4,7 @@ xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" > - +
@@ -19,5 +19,20 @@
+ + + + + + + + + + + + + +
+
diff --git a/db/tasks.php b/db/tasks.php new file mode 100644 index 0000000..8110f32 --- /dev/null +++ b/db/tasks.php @@ -0,0 +1,37 @@ +. + +/** + * Scheduled task to issue badges + * + * @package block_acclaim + * @copyright 2014 Yancy Ribbens + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$tasks = [ + [ + 'classname' => 'block_acclaim\task\issue_badges', + 'blocking' => 0, + 'minute' => '*/5', + 'hour' => '*', + 'day' => '*', + 'month' => '*', + 'dayofweek' => '*', + ], +]; diff --git a/lang/en/block_acclaim.php b/lang/en/block_acclaim.php index 4a2b8fb..2cedc54 100755 --- a/lang/en/block_acclaim.php +++ b/lang/en/block_acclaim.php @@ -24,4 +24,4 @@ $string['acclaim'] = 'Acclaim'; $string['acclaim:addinstance'] = 'Add a new Acclaim block'; $string['acclaim:myaddinstance'] = 'Add a new Acclaim block to the My Moodle page'; -$string['privacy:metadata'] = 'The Acclaim block only only stores course and badge_template details'; +$string['privacy:metadata'] = 'The Acclaim block only only stores course and badge_template details. User data is stored in a temporary table and removed once issued'; diff --git a/lib.php b/lib.php index e46d84d..70395bc 100755 --- a/lib.php +++ b/lib.php @@ -54,7 +54,7 @@ function block_acclaim_get_badge_info($course_id,$field) global $DB; $return_val = ""; - $course = $DB->get_record('block_acclaim', array('courseid' => $course_id)); + $course = $DB->get_record('block_acclaim_courses', array('courseid' => $course_id)); if(!empty($course)){ $return_val = $course->$field; @@ -66,18 +66,18 @@ function block_acclaim_get_badge_info($course_id,$field) function block_acclaim_get_block_course($course_id) { global $DB; - $course = $DB->get_record('block_acclaim', array('courseid' => $course_id), '*', MUST_EXIST); + $course = $DB->get_record('block_acclaim_courses', array('courseid' => $course_id), '*', MUST_EXIST); return $course; } -function block_acclaim_write_badge_to_issue($fromform) +function block_acclaim_set_course_badge_template($fromform) { global $DB; $fromform = block_acclaim_update_form_with_badge_name($fromform); - $DB->delete_records('block_acclaim', array('courseid' => $fromform->courseid)); + $DB->delete_records('block_acclaim_courses', array('courseid' => $fromform->courseid)); - return $DB->insert_record('block_acclaim', $fromform); + return $DB->insert_record('block_acclaim_courses', $fromform); } function block_acclaim_get_issue_badge_url() @@ -124,33 +124,78 @@ function block_acclaim_update_form_with_badge_name($fromform) return $fromform; } -function block_acclaim_create_data_array($event,$badge_id,$timestamp) +function block_acclaim_create_pending_badge($event) +{ + global $DB; + $course = block_acclaim_get_block_course($event->courseid); + $pending_badge = block_acclaim_create_pending_badge_obj($event, $course); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); +} + +function block_acclaim_create_pending_badge_obj($event, $course) { $user_id = $event->relateduserid; + $badge_template_id = $course->badgeid; $course_id = $event->courseid; $user = block_acclaim_return_user($user_id); $firstname = $user->firstname; $lastname = $user->lastname; $email = $user->email; - $expires_at = block_acclaim_convert_time_stamp($timestamp); - $date_time = block_acclaim_convert_time_stamp(time()); + $expires_at = $course->expiration; - $data = array( - 'badge_template_id' => $badge_id, - 'issued_to_first_name' => $firstname, - 'issued_to_last_name' => $lastname, - 'expires_at' => $expires_at, - 'recipient_email' => $email, - 'issued_at' => $date_time - ); + $pending_badge = new stdClass(); + $pending_badge->badgetemplateid = $badge_template_id; + $pending_badge->firstname = $firstname; + $pending_badge->lastname = $lastname; + $pending_badge->expiration = $expires_at; + $pending_badge->recipientemail = $email; - return $data; + return $pending_badge; } -function block_acclaim_issue_badge_request($data, $url, $username) -{ - $curl = new curl; - $curl->post($url, $data, array( "CURLOPT_USERPWD" => $username . ":" )); +function block_acclaim_issue_badge($curl, $time, $url, $token){ + global $DB; + + $datetime = block_acclaim_convert_time_stamp($time); + + $pending_badges = $DB->get_records('block_acclaim_pending_badges'); + + foreach ($pending_badges as &$badge) { + + $payload = [ + 'badge_template_id' => $badge->badgetemplateid, + 'issued_to_first_name' => $badge->firstname, + 'issued_to_last_name' => $badge->lastname, + 'recipient_email' => $badge->recipientemail, + 'issued_at' => $datetime + ]; + + if($badge->expiration){ + $payload['expires_at'] = + block_acclaim_convert_time_stamp($badge->expiration); + } + + $curl->post( + $url, $payload, array( "CURLOPT_USERPWD" => $token. ":" ) + ); + + if ($curl->info["http_code"] == 201) { + // The badge has been issued so we remove it from pending. + $DB->delete_records('block_acclaim_pending_badges', array('id' => $badge->id)); + } elseif ($curl->info["http_code"] == 422) { + // Acclaim can not issue the badge so we remove this from pending + // so it will not try again. This could happen for example if the + // user already has been issued a badge. + error_log(print_r($curl->response, true)); + $DB->delete_records('block_acclaim_pending_badges', array('id' => $badge->id)); + } else { + // some other issue is preventing the badge from being issued + // for example site down or token incorrectly entered. The + // record is left as pending to try again in the future. + error_log(print_r($curl->response, true)); + } + }; + return $curl->info["http_code"]; } diff --git a/tests/acclaim_test.php b/tests/acclaim_test.php index 918f0dd..8174b5c 100755 --- a/tests/acclaim_test.php +++ b/tests/acclaim_test.php @@ -31,11 +31,18 @@ class acclaim_lib_test extends advanced_testcase{ function setUp(){ - $this->resetAfterTest(true); + $this->resetAfterTest(true); } - public function mock_event($id) + public function mock_event() { + global $DB; + $user = new stdClass(); + $user->firstname = "Richard"; + $user->lastname = "Kimble"; + $user->email = "richard.kimble@fugative.me"; + $user_id = $DB->insert_record('user', $user); + $event = new stdClass(); $event->eventname = " \core\event\course_completed"; $event->component = "core"; @@ -49,14 +56,14 @@ public function mock_event($id) $event->contextlevel = "50"; $event->contextinstanceid = "14"; $event->userid = 2; - $event->courseid = "123"; - $event->relateduserid = $id; + $event->courseid = "38"; + $event->relateduserid = $user_id; $event->anonymous = "0"; $event->timecreated = "1409543537"; return $event; } - private function mock_form() + private function mock_form($time = 0) { $badgenames = [ "123" => "mos", @@ -76,10 +83,58 @@ private function mock_form() return $fromform; } + private function create_pending_badge($expiration = 0) + { + $pending_badge = new stdClass(); + $pending_badge->badgetemplateid = '123'; + $pending_badge->firstname = 'Richard'; + $pending_badge->lastname = 'Kimble'; + $pending_badge->expiration = $expiration; + $pending_badge->recipientemail = 'richard.kimble@fugative.me'; + return $pending_badge; + } + + private function assert_curl( + $return_code, $url, $token, $time, $expires_at = 0, $times = 1){ + + $expected_payload = [ + 'badge_template_id' => '123', + 'issued_to_first_name' => 'Richard', + 'issued_to_last_name' => 'Kimble', + 'recipient_email' => 'richard.kimble@fugative.me', + 'issued_at' => block_acclaim_convert_time_stamp($time) + ]; + + if($expires_at != 0){ + $expected_payload['expires_at'] = + block_acclaim_convert_time_stamp($expires_at); + } + + $mock_curl = $this->getMockBuilder(curl::class) + ->setMethods(['post']) + ->getMock(); + + $mock_curl->info = array("http_code" => $return_code); + $mock_curl->expects($this->exactly( $times )) + ->method('post') + ->with( + $url, + $expected_payload, + array("CURLOPT_USERPWD" => $token.":") + ); + return $mock_curl; + } + + private function assert_pending_badge_count($expected_count){ + global $DB; + $count = $DB->count_records('block_acclaim_pending_badges'); + $this->assertEquals($expected_count, $count); + } + public function test_get_badge_id() { global $DB; - $table = 'block_acclaim'; + $table = 'block_acclaim_courses'; $DB->delete_records($table); $this->assertEmpty($DB->get_records($table)); @@ -99,7 +154,7 @@ public function test_get_badge_id() public function test_get_course() { global $DB; - $table = 'block_acclaim'; + $table = 'block_acclaim_courses'; $DB->delete_records($table); $this->assertEmpty($DB->get_records($table)); @@ -117,31 +172,44 @@ public function test_get_course() $this->assertEquals($dataobject->badgeid,$course->badgeid); } - public function test_write_block_record() - { - global $DB; - $table = 'block_acclaim'; - $DB->delete_records($table); - $this->assertEmpty($DB->get_records($table)); - $fromform = $this->mock_form(); - - $result = block_acclaim_write_badge_to_issue($fromform); - $count = $DB->count_records_select($table, "courseid = '38'"); - - $this->assertEquals($count,1); + public function test_set_course_badge_template_is_unique(){ + global $DB; - $result = block_acclaim_write_badge_to_issue($fromform); - $count = $DB->count_records_select($table, "courseid = '38'"); + $result = block_acclaim_set_course_badge_template($this->mock_form()); + $count = $DB->count_records_select( + 'block_acclaim_courses', "courseid = '38'" + ); + $this->assertEquals($count, 1); - $this->assertEquals(1,$count); + $result = block_acclaim_set_course_badge_template($this->mock_form()); + $count = $DB->count_records_select( + 'block_acclaim_courses', "courseid = '38'" + ); + $this->assertEquals(1, $count); } - public function test_create_array() - { - $badge_id = "123"; - $data = block_acclaim_create_data_array($this->mock_event('2'),$badge_id,"",'1'); - $is_set = isset($data); - $this->assertEquals(true,$is_set); + public function test_create_pending(){ + global $DB; + block_acclaim_set_course_badge_template($this->mock_form()); + + $event = $this->mock_event(); + block_acclaim_create_pending_badge($event); + $count = $DB->count_records('block_acclaim_pending_badges'); + $this->assertEquals(1, $count); + + $pending_badges = $DB->get_records_list( + 'block_acclaim_pending_badges', + 'badgetemplateid', + array('1edb816d-a9fb-445d-b024-bb52075718e5') + ); + $pending_badge = array_pop($pending_badges); + + $this->assertEquals('Richard', $pending_badge->firstname); + $this->assertEquals('Kimble', $pending_badge->lastname); + $this->assertEquals('richard.kimble@fugative.me', + $pending_badge->recipientemail + ); + $this->assertEquals(0, $pending_badge->expiration); } public function test_build_radio_buttons() @@ -152,30 +220,95 @@ public function test_build_radio_buttons() $this->assertEquals(array( 1 => "johnny"), $badge_items); } - //TODO passing in curl connection breaks group observer when curl runs, however, - // the mock in this test requires the curl connection to be passed. Either mock the class - // or update how cron uses curl. - // - //public function test_issue_badge() - //{ - //$data = 'data'; - //$url = 'url'; - //$token = 'token'; - - //$mock_curl = $this->getMockBuilder(curl::class) - //->setMethods(['post']) - //->getMock(); - - //$mock_curl->info = array("http_code" => 418); - //$mock_curl->expects($this->once()) - //->method('post') - //->with( - //'url', - //$data, - //array("CURLOPT_USERPWD" => "token:")); - //$return_code = block_acclaim_issue_badge_request($mock_curl, $data, $url, $token); - //$this->assertEquals(418, $return_code); - //} + public function test_issue_badge_success(){ + global $DB; + $time = time(); + $url = 'url'; + $token = 'token'; + + $this->assert_pending_badge_count(0); + $pending_badge = $this->create_pending_badge(); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $this->assert_pending_badge_count(1); + $mock_curl = $this->assert_curl(201, $url, $token, $time); + $return_code = block_acclaim_issue_badge( + $mock_curl, $time, $url, $token); + $this->assertEquals(201, $return_code); + $this->assert_pending_badge_count(0); + } + + public function test_issue_badge_success_with_expiration(){ + global $DB; + $time = time(); + $url = 'url'; + $token = 'token'; + + $this->assert_pending_badge_count(0); + $pending_badge = $this->create_pending_badge($time); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $this->assert_pending_badge_count(1); + $mock_curl = $this->assert_curl(201, $url, $token, $time, $expiras_at = $time); + $return_code = block_acclaim_issue_badge( + $mock_curl, $time, $url, $token); + $this->assertEquals(201, $return_code); + $this->assert_pending_badge_count(0); + } + + public function test_issue_badge_unprocessable_entity(){ + global $DB; + $time = time(); + $url = 'url'; + $token = 'token'; + + $this->assert_pending_badge_count(0); + $pending_badge = $this->create_pending_badge(); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $this->assert_pending_badge_count(1); + + $mock_curl = $this->assert_curl(422, $url, $token, $time); + $return_code = block_acclaim_issue_badge( + $mock_curl, $time, $url, $token); + $this->assertEquals(422, $return_code); + $this->assert_pending_badge_count(0); + } + + public function test_issue_badge_failure(){ + global $DB; + $time = time(); + $url = 'url'; + $token = 'token'; + + $this->assert_pending_badge_count(0); + $pending_badge = $this->create_pending_badge(); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $this->assert_pending_badge_count(1); + + $mock_curl = $this->assert_curl(401, $url, $token, $time); + $return_code = block_acclaim_issue_badge( + $mock_curl, $time, $url, $token); + $this->assertEquals(401, $return_code); + $this->assert_pending_badge_count(1); + } + + public function test_issue_badge_mutltiple(){ + global $DB; + $time = time(); + $url = 'url'; + $token = 'token'; + + $this->assert_pending_badge_count(0); + $pending_badge = $this->create_pending_badge(); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $DB->insert_record('block_acclaim_pending_badges', $pending_badge); + $this->assert_pending_badge_count(2); + + $mock_curl = $this->assert_curl(201, $url, $token, $time, 0, 2); + $return_code = block_acclaim_issue_badge( + $mock_curl, $time, $url, $token); + $this->assertEquals(201, $return_code); + + $this->assert_pending_badge_count(0); + } public function test_get_badges() { diff --git a/version.php b/version.php index de3b788..421c672 100755 --- a/version.php +++ b/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2014082507; // YYYYMMDDHH (year, month, day, 24-hr time) +$plugin->version = 2019080211; // YYYYMMDDHH (year, month, day, 24-hr time) $plugin->requires = 2014050800; // Requires this Moodle version $plugin->component = 'block_acclaim'; // Full name of the plugin (used for diagnostics) diff --git a/view.php b/view.php index 1f18904..d41b67f 100755 --- a/view.php +++ b/view.php @@ -62,7 +62,7 @@ } else if ($fromform = $acclaim->get_data()) { $courseurl = new moodle_url('/course/view.php', array('id' => $courseid)); // We need to add code to appropriately act on and store the submitted data - if (!block_acclaim_write_badge_to_issue($fromform)) { + if (!block_acclaim_set_course_badge_template($fromform)) { print_error('inserterror', 'block_acclaim'); } redirect($courseurl);