Commit 9bb110a8 authored by Cecilia Vela Gurovic's avatar Cecilia Vela Gurovic Committed by Gerrit Code Review

Merge changes Ibe59bdc3,I4e75b905

* changes:
  Bug 1787507: Moving mobile apps / badgr apps to third menu
  Bug 1787507: Getting openbadges displayer working with Badgr
parents df539084 83365056
<?php
/**
*
* @package mahara
* @subpackage core
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
define('INTERNAL', 1);
define('MENUITEM', 'settings/apps');
define('SECTION_PLUGINTYPE', 'core');
define('SECTION_PLUGINNAME', 'account');
define('SECTION_PAGE', 'apps');
define('APPS', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
define('TITLE', get_string('myapplications'));
$hasapps = apps_get_menu_tabs();
$smarty = smarty();
$smarty->assign('hasapps', !empty($hasapps));
$smarty->display('account/apps.tpl');
......@@ -96,6 +96,20 @@ abstract class PluginBlocktype extends Plugin implements IPluginBlocktype {
}
}
/**
* This function returns an array of menu items
* to be displayed in the top right navigation menu
*
* See the function find_menu_children() in lib/web.php
* for a description of the expected array structure.
*
* @return array
*/
public static function right_nav_menu_items() {
return array();
}
/**
* If the theme wants to display CSS icons for Mahara blocks, then it will
* call this method to find out the name of the CSS icon to use. If this
......
......@@ -29,11 +29,18 @@ if (!isset($emails) || !in_array($email, $emails)) {
$uid = PluginBlocktypeOpenbadgedisplayer::get_backpack_id($host, $email);
$hosttitle = get_string('title_' . $host, 'blocktype.openbadgedisplayer');
$badgegroupnames = isset($uid) ? PluginBlocktypeOpenbadgedisplayer::get_badgegroupnames($host, $uid) : null;
$nobadgegroup = get_string('nobadgegroupsin1', 'blocktype.openbadgedisplayer', $hosttitle, $email);
$nobackpack = get_string('nobackpackidin1', 'blocktype.openbadgedisplayer', $email, $hosttitle);
if ($host == 'badgr' && is_null($uid)) {
$nobackpack = get_string('nobadgruid', 'blocktype.openbadgedisplayer');
}
json_reply(false, array(
'host' => $host,
'hosttitle' => $hosttitle,
'uid' => $uid,
'badgegroups' => isset($uid) ? PluginBlocktypeOpenbadgedisplayer::get_badgegroupnames($host, $uid) : null,
'nobackpackmsg' => get_string('nobackpackidin1', 'blocktype.openbadgedisplayer', $email, $hosttitle),
'nobadgegroupsmsg' => get_string('nobadgegroupsin1', 'blocktype.openbadgedisplayer', $hosttitle, $email)
'badgegroups' => $badgegroupnames,
'nobackpackmsg' => $nobackpack,
'nobadgegroupsmsg' => $nobadgegroup,
));
<?php
/**
*
* @package mahara
* @subpackage blocktype-openbadgesdisplayer-badgr-token
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
/**
* This page lets users manage a badgr token with their account
*
*/
define('INTERNAL', 1);
define('MENUITEM', 'settings/badgr');
define('SECTION_PLUGINTYPE', 'core');
define('SECTION_PLUGINNAME', 'account');
define('SECTION_PAGE', 'openbadgedisplayer');
define('APPS', 1);
require('./../../init.php');
safe_require('blocktype', 'openbadgedisplayer');
define('TITLE', get_string('badgrtokentitle', 'blocktype.openbadgedisplayer'));
// Users shouldn't be able to access this page if openbadgedisplayer blocktype is not active.
if (!is_plugin_active('openbadgedisplayer','blocktype')) {
throw new AccessDeniedException(get_string('featuredisabled', 'blocktype.openbadgedisplayer'));
}
$sources = PluginBlocktypeOpenbadgedisplayer::get_backpack_source();
if (empty($sources['badgr'])) {
throw new AccessDeniedException(get_string('badgrsourcemissing', 'blocktype.openbadgedisplayer'));
}
$token = get_field('usr_account_preference', 'value', 'field', 'badgr_token', 'usr', $USER->get('id'));
$elements = array();
if ($token) {
$elements['tokenhtml'] = array(
'type' => 'html',
'value' => get_string('badgrtoken', 'blocktype.openbadgedisplayer', $token),
);
$elements['token'] = array(
'type' => 'hidden',
'value' => $token,
);
// delete button
$elements['submit'] = array(
'type' => 'button',
'usebuttontag' => true,
'class' => 'btn-default btn-sm',
'value' => '<span class="icon icon-trash icon-lg text-danger left" role="presentation" aria-hidden="true"></span>' . get_string('delete'),
'elementtitle' => get_string('deletespecific', 'mahara', $token),
);
}
else {
$elements['badgrusername'] = array(
'title' => get_string('badgrusername', 'blocktype.openbadgedisplayer'),
'type' => 'text',
'defaultvalue' => $USER->get('email'),
'rules' => array('required' => true),
);
$elements['badgrpassword'] = array(
'title' => get_string('badgrpassword', 'blocktype.openbadgedisplayer'),
'type' => 'password',
'rules' => array('required' => true),
);
$elements['submit'] = array(
'type' => 'submit',
'class' => 'btn-primary',
'value' => get_string('save'),
);
}
$form = array(
'renderer' => 'div',
'id' => 'maintable',
'name' => 'maincontainer',
'dieaftersubmit' => false,
'successcallback' => 'badgr_token_submit',
'elements' => $elements,
);
/**
* handle the callback for actions on the user token panel
* - generate new token
* - delete token
*
* @param Pieform $form
* @param array $values
*/
function badgr_token_submit(Pieform $form, $values) {
global $USER, $SESSION, $sources;
if (!empty($values['token'])) {
// We are in delete mode
delete_records('usr_account_preference', 'usr', $USER->get('id'), 'field', 'badgr_token', 'value', $values['token']);
$SESSION->add_ok_msg(get_string('badgrtokendeleted', 'blocktype.openbadgedisplayer'));
}
else {
// We are in add mode
$res = mahara_http_request(
array(
CURLOPT_URL => $sources['badgr'] . 'api-auth/token',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => 'username=' . $values['badgrusername'] . '&password=' . $values['badgrpassword'],
)
);
$json = json_decode($res->data);
if (isset($json->token)) {
set_account_preference($USER->get('id'), 'badgr_token', $json->token);
$SESSION->add_ok_msg(get_string('badgrtokenadded', 'blocktype.openbadgedisplayer'));
}
else {
$SESSION->add_error_msg(get_string('badgrtokennotfound', 'blocktype.openbadgedisplayer'));
}
}
redirect('/blocktype/openbadgedisplayer/badgrtoken.php');
}
// render the page
$form = pieform($form);
$smarty = smarty();
setpageicon($smarty, 'icon-globe');
$smarty->assign('form', $form);
$smarty->display('form.tpl');
......@@ -8,8 +8,8 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" SEQUENCE="true" NOTNULL="true" />
<FIELD NAME="host" TYPE="char" LENGTH="255" NOTNULL="true" />
<FIELD NAME="uid" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="badgegroupid" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="uid" TYPE="char" LENGTH="100" NOTNULL="true" />
<FIELD NAME="badgegroupid" TYPE="char" LENGTH="100" NOTNULL="true" />
<FIELD NAME="name" TYPE="text" NOTNULL="false" />
<FIELD NAME="html" TYPE="text" NOTNULL="false" />
<FIELD NAME="lastupdate" TYPE="datetime" NOTNULL="false" />
......
......@@ -50,5 +50,16 @@ function xmldb_blocktype_openbadgedisplayer_upgrade($oldversion = 0) {
create_table($table);
}
if ($oldversion < 2018081700) {
// Alter table blocktype_openbadgedisplayer_data make uid and badgegroupid char string
$table = new XMLDBTable('blocktype_openbadgedisplayer_data');
$field = new XMLDBField('uid');
$field->setAttributes(XMLDB_TYPE_CHAR, 100, null, XMLDB_NOTNULL);
change_field_type($table, $field, true, true);
$field = new XMLDBField('badgegroupid');
$field->setAttributes(XMLDB_TYPE_CHAR, 100, null, XMLDB_NOTNULL);
change_field_type($table, $field, true, true);
}
return true;
}
\ No newline at end of file
......@@ -68,9 +68,23 @@ $string['title_backpack'] = 'Mozilla Backpack';
$string['title_passport'] = 'Open Badge Passport';
$string['title_badgr'] = 'Badgr Backpack';
$string['fetchingbadges'] = 'Fetching entries. This may take a while.';
$string['missingbadgesources'] = 'Missing sources setting. Please add to your config.php file, e.g.<br><br>$cfg->openbadgedisplayer_source = \'{"backpack":"https://backpack.openbadges.org/"}\'';
$string['selectall'] = 'Select all';
$string['selectnone'] = 'Select none';
// Badgr token page
$string['featuredisabled'] = 'The openbadgedisplayer blocktype is not active';
$string['badgrsourcemissing'] = 'Badgr is not in the sources configuration';
$string['badgrusername'] = "Badgr Username";
$string['badgrpassword'] = "Badgr Password";
$string['badgrtokentitle'] = "Badgr";
$string['badgrtoken'] = "Badgr token: %s";
$string['badgrtokenadded'] = "Badgr token added to account";
$string['badgrtokendeleted'] = "Badgr token deleted";
$string['badgrtokennotfound'] = "Badgr token not found with supplied credentials";
$string['nobadgruid'] = 'Badgr requires a token for use. Please go to "Settings → Badgr token access" to fetch your token';
......@@ -78,6 +78,18 @@ class PluginBlocktypeOpenbadgedisplayer extends SystemBlocktype {
return array('openbadgedisplayer' => array('media'));
}
public static function app_tabs() {
return array(
'badgr' => array(
'path' => 'settings/badgr',
'url' => 'blocktype/openbadgedisplayer/badgrtoken.php',
'title' => get_string('badgrtokentitle', 'blocktype.openbadgedisplayer'),
'weight' => 20,
'iconclass' => 'flag'
),
);
}
public static function render_instance(BlockInstance $instance, $editing=false, $versioning=false) {
$configdata = $instance->get('configdata');
if (empty($configdata) || !isset($configdata['badgegroup']) || !get_config('openbadgedisplayer_source')) {
......@@ -165,18 +177,87 @@ class PluginBlocktypeOpenbadgedisplayer extends SystemBlocktype {
$html = '';
$existing = array();
$backpack_url = self::get_backpack_url($host);
$url = $backpack_url . 'displayer/' . $uid . '/group/' . $badgegroupid . '.json';
$res = mahara_http_request(array(CURLOPT_URL => $url));
if ($host == 'badgr') {
$url = $backpack_url . 'v2/backpack/collections/' . $badgegroupid;
$res = mahara_http_request(
array(
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => array('Authorization: Token ' . $uid),
)
);
}
else {
$url = $backpack_url . 'displayer/' . $uid . '/group/' . $badgegroupid . '.json';
$res = mahara_http_request(array(CURLOPT_URL => $url));
}
if ($res->info['http_code'] != 200) {
return '';
}
$json = json_decode($res->data);
if (isset($json->badges) && is_array($json->badges)) {
if (isset($json->status) && $json->status->success) {
foreach ($json->result as $collection) {
foreach ($collection->assertions as $assertion) {
// Currently I can't see a way to fetch the badge/assertion/issuer info
// as one json blob/one curl request
$url = $backpack_url . 'v2/backpack/assertions/' . $assertion;
$res2 = mahara_http_request(
array(
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => array('Authorization: Token ' . $uid),
)
);
$badge = json_decode($res2->data);
$res3 = mahara_http_request(
array(
CURLOPT_URL => $badge->result[0]->badgeclassOpenBadgeId,
CURLOPT_HTTPHEADER => array('accept: application/json'),
)
);
$badgeinfo = json_decode($res3->data);
$res4 = mahara_http_request(
array(
CURLOPT_URL => $badgeinfo->issuer,
CURLOPT_HTTPHEADER => array('accept: application/json'),
)
);
if (!empty($badgeinfo->id)) {
$criteria = $badgeinfo->id;
}
else if (is_array($badgeinfo->criteria)) {
$criteria = $badgeinfo->criteria->id;
}
else if (is_string($badgeinfo->criteria)) {
$criteria = $badgeinfo->criteria;
}
else {
$criteria = '';
}
$issuer = json_decode($res4->data);
$data_assertion = $badge->result[0];
$data_assertion->issued_on = strtotime($data_assertion->issuedOn);
$data_assertion->expires = strtotime($data_assertion->expires);
$data_assertion->badge = new stdClass();
$data_assertion->badge->name = hsc($badgeinfo->name);
$data_assertion->badge->description = hsc($badgeinfo->description);
$data_assertion->badge->criteria = $criteria;
$data_assertion->badge->_location = $data_assertion->openBadgeId;
$data_assertion->badge->image = $data_assertion->image;
$data_assertion->badge->issuer = new stdClass();
$data_assertion->badge->issuer->origin = $issuer->url;
$data_assertion->badge->issuer->name = $issuer->name;
$data_assertion->badge->issuer->org = $issuer->email;
$html .= '<img tabindex="0" id="' . (preg_replace('/\:/', '_', $group)) . '" '
. 'src="' . $badge->result[0]->image . '" '
. 'title="' . $badge->result[0]->entityId . '" '
. 'data-assertion="' . htmlentities(json_encode($data_assertion)) . '" />';
}
}
}
else if (isset($json->badges) && is_array($json->badges)) {
foreach ($json->badges as $badge) {
$b = $badge->assertion->badge;
......@@ -385,17 +466,36 @@ class PluginBlocktypeOpenbadgedisplayer extends SystemBlocktype {
}
if ($backpack_url !== false) {
$res = mahara_http_request(
array(
CURLOPT_URL => $backpack_url . 'displayer/convert/email',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => 'email=' . urlencode($email)
)
);
$res = json_decode($res->data);
if (isset($res->userId)) {
$backpackids[$host][$email] = $res->userId;
return $res->userId;
if ($backpack_url == 'https://api.badgr.io/') {
$userid = get_field('artefact_internal_profile_email', 'owner', 'email', $email);
$token = get_field('usr_account_preference', 'value', 'field', 'badgr_token', 'usr', $userid);
if ($token) {
$res = mahara_http_request(
array(
CURLOPT_URL => $backpack_url . 'v2/users/self',
CURLOPT_HTTPHEADER => array('Authorization: Token ' . $token),
)
);
$res = json_decode($res->data);
if (isset($res->status) && $res->status->success) {
$backpackids[$host][$email] = $token;
return $token;
}
}
}
else {
$res = mahara_http_request(
array(
CURLOPT_URL => $backpack_url . 'displayer/convert/email',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => 'email=' . urlencode($email)
)
);
$res = json_decode($res->data);
if (isset($res->userId)) {
$backpackids[$host][$email] = $res->userId;
return $res->userId;
}
}
}
return null;
......@@ -466,7 +566,14 @@ class PluginBlocktypeOpenbadgedisplayer extends SystemBlocktype {
$badgegroupnames[$host][$uid] = array();
$backpack_url = self::get_backpack_url($host);
$res = mahara_http_request(array(CURLOPT_URL => $backpack_url . "displayer/{$uid}/groups.json"));
if ($backpack_url == 'https://api.badgr.io/') {
$res = mahara_http_request(array(CURLOPT_URL => $backpack_url . "v2/backpack/collections",
CURLOPT_HTTPHEADER => array('Authorization: Token ' . $uid,
'accept: application/json')));
}
else {
$res = mahara_http_request(array(CURLOPT_URL => $backpack_url . "displayer/{$uid}/groups.json"));
}
$res = json_decode($res->data);
if (!empty($res->groups)) {
......@@ -498,6 +605,32 @@ class PluginBlocktypeOpenbadgedisplayer extends SystemBlocktype {
);
}
}
else if (isset($res->status) && isset($res->status->success) && $res->status->success) {
foreach ($res->result as $g) {
if (!$g->published) {
continue;
}
$name = hsc($g->name);
$name .= ' (' . get_string('nbadges', 'blocktype.openbadgedisplayer', count($g->assertions)) . ')';
$badgegroupnames[$host][$uid][$g->entityId] = $name;
// Caching badge info into database for better performance
ensure_record_exists('blocktype_openbadgedisplayer_data',
(object) array(
'host' => $host,
'uid' => $uid,
'badgegroupid' => $g->entityId,
),
(object) array(
'host' => $host,
'uid' => $uid,
'badgegroupid' => $g->entityId,
'name' => $name,
'lastupdate' => db_format_timestamp(time())
)
);
}
}
return $badgegroupnames[$host][$uid];
}
......
......@@ -30,5 +30,5 @@
defined('INTERNAL') || die();
$config = new stdClass();
$config->version = 2016030200;
$config->release = '1.0.5';
$config->version = 2018081700;
$config->release = '1.1.0';
......@@ -884,6 +884,11 @@ $string['profileimagetexttemplate'] = "User's profile picture will go here";
$string['profileimagetextanonymous'] = "anonymous profile picture";
$string['primaryemailinvalid'] = 'Your primary email address is invalid.';
$string['addemail'] = 'Add email address';
$string['myapps'] = 'Apps';
$string['myapplications'] = 'My applications';
$string['acccountappsdescription'] = 'Here you can administer any applications that require token access.';
$string['acccountaddappsdescription'] = 'Currently there are no plugins active that allow token administration.';
$string['acccountchooseappsdescription'] = 'To administer your application tokens please select the application from the "My applications" bar.';
// Search
$string['search'] = 'Search';
......
......@@ -709,7 +709,7 @@ $cfg->passwordsaltalt1 = 'old salt value';
* @global array $cfg->openbadgedisplayer_source The open badge sources
* The default sources are Mozilla Backpack and openbadgepassport.com
*/
$cfg->openbadgedisplayer_source = '{"backpack":"https://backpack.openbadges.org/","passport":"https://openbadgepassport.com/"}';
$cfg->openbadgedisplayer_source = '{"backpack":"https://backpack.openbadges.org/","passport":"https://openbadgepassport.com/","badgr":"https://api.badgr.io/"}';
/**
* @global string $cfg->memcacheservers
......
......@@ -94,6 +94,8 @@ function check_upgrades($name=null) {
$core->fromrelease = $corerelease;
}
else if ($config->version < $coreversion) {
// Core can't be upgraded. Remove it from the list!
unset($toupgrade['core']);
if (get_config('productionmode')) {
throw new ConfigSanityException("Database version of Mahara $corerelease ($coreversion) is newer "
. "than files version $config->release ($config->version). "
......
......@@ -794,6 +794,12 @@ EOF;
}
}
if (defined('APPS')) {
if (!defined('NOAPPSMENU')) {
$smarty->assign('SUBPAGENAV', apps_get_menu_tabs());
}
}
// ---------- sideblock stuff ----------
$sidebars = !isset($extraconfig['sidebars']) || $extraconfig['sidebars'] !== false;
if ($sidebars && !defined('INSTALLER') && (!defined('MENUITEM') || substr(MENUITEM, 0, 5) != 'admin')) {
......@@ -3157,6 +3163,12 @@ function right_nav() {
'title' => get_string('legal', 'admin'),
'weight' => 30
),
'settings/apps' => array(
'path' => 'settings/apps',
'url' => 'account/apps.php',
'title' => get_string('myapps'),
'weight' => 50
),
'settings/notifications' => array(
'path' => 'settings/notifications',
'url' => 'account/activity/preferences/index.php',
......@@ -3167,7 +3179,7 @@ function right_nav() {
);
// enable plugins to augment the menu structure
foreach (array('artefact', 'interaction', 'module') as $plugintype) {
foreach (array('artefact', 'blocktype', 'interaction', 'module') as $plugintype) {
if ($plugins = plugins_installed($plugintype)) {
foreach ($plugins as &$plugin) {
if (safe_require_plugin($plugintype, $plugin->name)) {
......@@ -3280,6 +3292,33 @@ function footer_menu($all=false) {
return $menu;
}
function apps_get_menu_tabs() {
$menu = array();
foreach (plugin_types_installed() as $plugin_type_installed) {
foreach (plugins_installed($plugin_type_installed) as $plugin) {
safe_require($plugin_type_installed, $plugin->name);
if (method_exists(generate_class_name($plugin_type_installed, $plugin->name), 'app_tabs')) {
$plugin_menu = call_static_method(
generate_class_name($plugin_type_installed, $plugin->name),
'app_tabs'
);
$menu = array_merge($menu, $plugin_menu);
}
}
}
if (defined('MENUITEM')) {
$key = substr(MENUITEM, strlen('settings/'));
if ($key && isset($menu[$key])) {
$menu[$key]['selected'] = true;
}
}
// Sort the menu items by weight
uasort($menu, "sort_menu_by_weight");
return $menu;
}
/**
* Given a menu structure and a path, returns a data structure representing all
......
......@@ -20,11 +20,12 @@ define('MENUITEM', 'settings/webservice');
define('SECTION_PLUGINTYPE', 'core');
define('SECTION_PLUGINNAME', 'account');
define('SECTION_PAGE', 'webservice');
define('APPS', 1);
require('./../../init.php');
require_once($CFG->docroot . 'webservice/lib.php');
safe_require('module', 'mobileapi');
define('TITLE', get_string('mytokenspagetitle', 'module.mobileapi'));
define('TITLE', get_string('mytokenspagetitle1', 'module.mobileapi'));
// Users shouldn't be able to access this page if webservices are not enabled.
if (!PluginModuleMobileapi::is_service_ready()) {
......
......@@ -28,8 +28,8 @@ $string['servicenotallowed'] = 'The credentials you have provided are not author
$string['webserviceproviderenabled'] = 'Incoming web service requests allowed';
// User management of webservice access tokens
$string['mytokensmenutitle'] = 'Apps';
$string['mytokenspagetitle'] = 'Applications';
$string['mytokensmenutitle1'] = 'Mahara mobile';
$string['mytokenspagetitle1'] = 'Mahara mobile tokens';
$string['mytokenspagedesc'] = 'These applications can access your account.';
$string['nopersonaltokens'] = 'You have not granted access to any applications.';
$string['clientinfo'] = 'App';
......
......@@ -143,14 +143,14 @@ class PluginModuleMobileapi extends PluginModule {
return true;
}
public static function right_nav_menu_items() {
public static function app_tabs() {
if (PluginModuleMobileapi::is_service_ready()) {
return array(
'settings/webservice' => array(
'webservice' => array(
'path' => 'settings/webservice',
'url' => 'module/mobileapi/apps.php',
'title' => get_string('mytokensmenutitle', 'module.mobileapi'),
'weight' => 50,
'title' => get_string('mytokensmenutitle1', 'module.mobileapi'),