// indirectly calls the protected init() method is good here.
core_component::get_core_subsystems();
+if (is_major_upgrade_required() && isloggedin()) {
+ // A major upgrade is required.
+ // Terminate the session and redirect back here before anything DB-related happens.
+ redirect_if_major_upgrade_required();
+}
+
require_once($CFG->libdir.'/adminlib.php'); // various admin-only functions
require_once($CFG->libdir.'/upgradelib.php'); // general upgrade/install related functions
// Look at all the templates dirs for subsystems.
$subsystems = core_component::get_core_subsystems();
foreach ($subsystems as $subsystem => $dir) {
+ if (empty($dir)) {
+ continue;
+ }
$dir .= '/templates';
if (is_dir($dir)) {
$dirs = mustache_template_finder::get_template_directories_for_component('core_' . $subsystem, $themename);
$data->error = $this->error;
$data->forgotpasswordurl = $this->forgotpasswordurl->out(false);
$data->hasidentityproviders = !empty($this->identityproviders);
- $data->hasinstructions = !empty($this->instructions);
+ $data->hasinstructions = !empty($this->instructions) || $this->cansignup;
$data->identityproviders = $identityproviders;
list($data->instructions, $data->instructionsformat) = external_format_text($this->instructions, FORMAT_MOODLE,
context_system::instance()->id);
$userid = $USER->id;
}
+ if (linked_login::count_records(['username' => $userinfo['username']]) > 0) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
+ }
+
if (\core\session\manager::is_loggedinas()) {
throw new moodle_exception('notwhileloggedinas', 'auth_oauth2');
}
$record->issuerid = $issuer->get('id');
$record->username = $userinfo['username'];
$record->userid = $userid;
- $existing = linked_login::get_record((array)$record);
- if ($existing) {
- return false;
+ if (linked_login::count_records(['username' => $userinfo['username']]) > 0) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
}
$record->email = $userinfo['email'];
$record->confirmtoken = random_string(32);
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
+ if (linked_login::count_records(['username' => $userinfo['username']]) > 0) {
+ throw new moodle_exception('alreadylinked', 'auth_oauth2');
+ }
+
$user = new stdClass();
$user->username = $userinfo['username'];
$user->email = $userinfo['email'];
$login->delete();
}
+
+ /**
+ * Delete linked logins for a user.
+ *
+ * @param \core\event\user_deleted $event
+ * @return boolean
+ */
+ public static function user_deleted(\core\event\user_deleted $event) {
+ global $DB;
+
+ $userid = $event->objectid;
+
+ return $DB->delete_records(linked_login::TABLE, ['userid' => $userid]);
+ }
}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file definies observers needed by the plugin.
+ *
+ * @package auth_oauth2
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// List of observers.
+$observers = [
+ [
+ 'eventname' => '\core\event\user_deleted',
+ 'callback' => '\auth_oauth2\api::user_deleted',
+ ],
+];
$string['loginerror_nouserinfo'] = 'No user information was returned. The OAuth 2 service may be configured incorrectly.';
$string['loginerror_invaliddomain'] = 'The email address is not allowed at this site.';
$string['loginerror_authenticationfailed'] = 'The authentication process failed.';
-$string['loginerror_cannotcreateaccounts'] = 'The account does not exist and this site does not allow self-registration.';
+$string['loginerror_cannotcreateaccounts'] = 'An account with your email address could not be found.';
$string['notloggedindebug'] = 'The login attempt failed. Reason: {$a}';
$string['notwhileloggedinas'] = 'Linked logins cannot be managed while logged in as another user.';
$string['oauth2:managelinkedlogins'] = 'Manage own linked login accounts';
$string['plugindescription'] = 'This authentication plugin displays a list of the configured identity providers on the login page. Selecting an identity provider allows users to login with their credentials from an OAuth 2 provider.';
$string['pluginname'] = 'OAuth 2';
+$string['alreadylinked'] = 'This external account is already linked to an account on this site';
$userinfo = $client->get_userinfo();
if (!empty($userinfo)) {
- \auth_oauth2\api::link_login($userinfo, $issuer);
- redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
+ try {
+ \auth_oauth2\api::link_login($userinfo, $issuer);
+ redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
+ } catch (Exception $e) {
+ redirect($PAGE->url, $e->getMessage(), null, \core\output\notification::NOTIFY_ERROR);
+ }
} else {
redirect($PAGE->url, get_string('notloggedin', 'auth_oauth2'), null, \core\output\notification::NOTIFY_ERROR);
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2017051500; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2017051501; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017050500; // Requires this Moodle version.
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
protected function get_other_values(renderer_base $output) {
$event = $this->related['event'];
+ if (!$event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ return ['showitemcount' => false];
+ }
$modulename = $event->get_course_module()->get('modname');
$component = 'mod_' . $modulename;
$showitemcountcallback = 'core_calendar_event_action_shows_item_count';
$values = [];
$event = $this->event;
$context = $this->related['context'];
- $modulename = $event->get_course_module()->get('modname');
- $moduleid = $event->get_course_module()->get('id');
+ if ($moduleproxy = $event->get_course_module()) {
+ $modulename = $moduleproxy->get('modname');
+ $moduleid = $moduleproxy->get('id');
+ $url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
+ } else {
+ // TODO MDL-58866 We do not have any way to find urls for events outside of course modules.
+ global $CFG;
+ require_once($CFG->dirroot.'/course/lib.php');
+ $url = \course_get_url($this->related['course'] ?: SITEID);
+ }
$timesort = $event->get_times()->get_sort_time()->getTimestamp();
- $url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
$iconexporter = new event_icon_exporter($event, ['context' => $context]);
$values['url'] = $url->out(false);
$userid = $user ? $user->get('id') : null;
$isactivityevent = !empty($coursemodule);
$isglobalevent = ($course && $courseid == SITEID);
- $iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && $group && empty($groupid));
+ $iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && empty($groupid));
$isgroupevent = ($group && !empty($groupid));
$isuserevent = ($user && !empty($userid));
$getcallback('action'),
$getcallback('visibility'),
function ($dbrow) {
- // At present we only handle callbacks in course modules.
+ // At present we only have a bail-out check for events in course modules.
if (empty($dbrow->modulename)) {
return false;
}
// Callbacks will get supplied a "legacy" version
// of the event class.
$mapper = self::$eventmapper;
- $action = component_callback(
- 'mod_' . $event->get_course_module()->get('modname'),
- 'core_calendar_provide_event_action',
- [
- $mapper->from_event_to_legacy_event($event),
- self::$actionfactory
- ]
- );
+ $action = null;
+ if ($event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ // Any other event will not be displayed on the dashboard.
+ $action = component_callback(
+ 'mod_' . $event->get_course_module()->get('modname'),
+ 'core_calendar_provide_event_action',
+ [
+ $mapper->from_event_to_legacy_event($event),
+ self::$actionfactory
+ ]
+ );
+ }
// If we get an action back, return an action event, otherwise
// continue piping through the original event.
// This is enforced by the event_factory.
'visibility' => function (event_interface $event) {
$mapper = self::$eventmapper;
- $eventvisible = component_callback(
- 'mod_' . $event->get_course_module()->get('modname'),
- 'core_calendar_is_event_visible',
- [
- $mapper->from_event_to_legacy_event($event)
- ]
- );
+ $eventvisible = null;
+ if ($event->get_course_module()) {
+ // TODO MDL-58866 Only activity modules currently support this callback.
+ $eventvisible = component_callback(
+ 'mod_' . $event->get_course_module()->get('modname'),
+ 'core_calendar_is_event_visible',
+ [
+ $mapper->from_event_to_legacy_event($event)
+ ]
+ );
+ }
// Do not display the event if there is nothing to action.
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
$string['choosefilefromactivitybackup_help'] = 'Activity backups made using default settings are stored here.';
$string['choosefilefromautomatedbackup'] = 'Automated backups';
$string['choosefilefromautomatedbackup_help'] = 'Contains automatically generated backups.';
-$string['config_keep_groups_and_groupings'] = 'By default keep current roles and enrolments';
-$string['config_keep_roles_and_enrolments'] = 'By default keep current groups and groupings';
+$string['config_keep_groups_and_groupings'] = 'By default keep current groups and groupings.';
+$string['config_keep_roles_and_enrolments'] = 'By default keep current roles and enrolments.';
$string['config_overwrite_conf'] = 'Allows user to overwrite the current course configuration';
$string['config_overwrite_course_fullname'] = 'By default overwrite course full name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course full name (moodle/course:changefullname)';
$string['config_overwrite_course_shortname'] = 'By default overwrite course short name with the one from the backup file. This requires "Overwrite course configuration" to be checked and current user to have the capability to change course short name (moodle/course:changeshortname)';
$string['hiddenrules'] = 'Some settings specific to <b>{$a}</b> have been hidden. To view unselect other activities';
$string['editcoursecompletionsettings'] = 'Edit course completion settings';
$string['enablecompletion'] = 'Enable completion tracking';
-$string['enablecompletion_help'] = 'If enabled, activity completion conditions may be set in the activity settings and/or course completion conditions may be set. It is recommended to have this enabled in order for the course progress dashboard to display meaningful data.';
+$string['enablecompletion_help'] = 'If enabled, activity completion conditions may be set in the activity settings and/or course completion conditions may be set. It is recommended to have this enabled so that meaningful data is displayed in the course overview on the Dashboard.';
$string['enrolmentduration'] = 'Enrolment duration';
$string['enrolmentdurationlength'] = 'User must remain enrolled for';
$string['err_noactivities'] = 'Completion information is not enabled for any activity, so none can be displayed. You can enable completion information by editing the settings for an activity.';
'core:i/grade_correct' => 'fa-check text-success',
'core:i/grade_incorrect' => 'fa-remove text-danger',
'core:i/grade_partiallycorrect' => 'fa-check-square',
- 'core:i/grades' => 'fa-graduation-cap',
+ 'core:i/grades' => 'fa-table',
'core:i/groupevent' => 'fa-group',
'core:i/groupn' => 'fa-user',
'core:i/group' => 'fa-users',
'core:t/enrolusers' => 'fa-user-plus',
'core:t/expanded' => 'fa-caret-down',
'core:t/go' => 'fa-play',
- 'core:t/grades' => 'fa-graduation-cap',
+ 'core:t/grades' => 'fa-table',
'core:t/groupn' => 'fa-user',
'core:t/groups' => 'fa-user-circle',
'core:t/groupv' => 'fa-user-circle-o',
}
/**
- * Check whether a major upgrade is needed. That is defined as an upgrade that
- * changes something really fundamental in the database, so nothing can possibly
- * work until the database has been updated, and that is defined by the hard-coded
- * version number in this function.
+ * Check whether a major upgrade is needed.
+ *
+ * That is defined as an upgrade that changes something really fundamental
+ * in the database, so nothing can possibly work until the database has
+ * been updated, and that is defined by the hard-coded version number in
+ * this function.
+ *
+ * @return bool
*/
-function redirect_if_major_upgrade_required() {
+function is_major_upgrade_required() {
global $CFG;
$lastmajordbchanges = 2017040403.00;
- if (empty($CFG->version) or (float)$CFG->version < $lastmajordbchanges or
- during_initial_install() or !empty($CFG->adminsetuppending)) {
+
+ $required = empty($CFG->version);
+ $required = $required || (float)$CFG->version < $lastmajordbchanges;
+ $required = $required || during_initial_install();
+ $required = $required || !empty($CFG->adminsetuppending);
+
+ return $required;
+}
+
+/**
+ * Redirect to the Notifications page if a major upgrade is required, and
+ * terminate the current user session.
+ */
+function redirect_if_major_upgrade_required() {
+ global $CFG;
+ if (is_major_upgrade_required()) {
try {
@\core\session\manager::terminate_current();
} catch (Exception $e) {
Example context (json):
{
- "col1content": "<div class='alert alert-error'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
- "col2content": "<div class='alert alert-success'>Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
- "col3content": "<div class='alert alert-info'>Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
+ "col1content": "<div class='alert alert-error'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
+ "col2content": "<div class='alert alert-success'>2. Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
+ "col3content": "<div class='alert alert-info'>3. Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
}
}}
Example context (json):
{
- "col1content": "<div class='alert alert-info'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
- "col2content": "<div class='alert alert-success'>Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
}
}}
Example context (json):
{
- "col1content": "<div class='alert alert-info'>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
- "col2content": "<div class='alert alert-success'>Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
}
}}
<div class="row-fluid rtl-compatible">
echo $OUTPUT->header();
-echo $OUTPUT->render($mform_signup);
+if ($mform_signup instanceof renderable) {
+ // Try and use the renderer from the auth plugin if it exists.
+ try {
+ $renderer = $PAGE->get_renderer('auth_' . $authplugin->authtype);
+ } catch (coding_exception $ce) {
+ // Fall back on the general renderer.
+ $renderer = $OUTPUT;
+ }
+ echo $renderer->render($mform_signup);
+} else {
+ // Fall back for auth plugins not using renderables.
+ $mform_signup->display();
+}
echo $OUTPUT->footer();
JOIN {groups_members} gm ON gm.groupid = g.id
WHERE go.assignid = :assignmentid6
)
- ) AS merged
+ ) merged
GROUP BY merged.userid
) priority ON priority.userid = u.id
JOIN (
(SELECT 9999999 AS priority,
u.id AS userid,
-
a.allowsubmissionsfromdate,
a.duedate,
a.cutoffdate
UNION
(SELECT 0 AS priority,
uo.userid,
-
uo.allowsubmissionsfromdate,
uo.duedate,
uo.cutoffdate
UNION
(SELECT go.sortorder AS priority,
gm.userid,
-
go.allowsubmissionsfromdate,
go.duedate,
go.cutoffdate
WHERE go.assignid = :assignmentid9
)
- ) AS effective ON effective.priority = priority.priority AND effective.userid = priority.userid ';
+ ) effective ON effective.priority = priority.priority AND effective.userid = priority.userid ';
}
if (!empty($this->assignment->get_instance()->blindmarking)) {
/**
* Echo an exception message encapsulated in XML.
*
- * @param \Exception $exception The exception that was thrown
+ * @param \Exception|\Throwable $exception The exception that was thrown
*/
- public function handle(\Exception $exception) {
+ public function handle($exception) {
$message = $exception->getMessage();
// Add the exception backtrace for developers.
$string['contents'] = 'Contents';
$string['coursepacket'] = 'Course package';
$string['coursestruct'] = 'Course structure';
-$string['crontask'] = 'Background processing for Scorm';
+$string['crontask'] = 'Background processing for SCORM';
$string['currentwindow'] = 'Current window';
$string['datadir'] = 'Filesystem error: Can\'t create course data directory';
$string['defaultdisplaysettings'] = 'Default display settings';
$string['internal'] = 'Internal (files stored in Moodle)';
$string['issuer_help'] = 'Select the OAuth 2 service that is configured to talk to the OneDrive API. If the service does not exist yet, you will need to create it.';
$string['issuer'] = 'OAuth 2 service';
-$string['mysitenotfound'] = 'You have never logged into OneDrive before. You must login to OneDrive at least once it before it can be used with Moodle.';
+$string['mysitenotfound'] = 'You have never logged into OneDrive before. You must log in to OneDrive at least once before it can be used with Moodle.';
$string['oauth2serviceslink'] = '<a href="{$a}" title="Link to OAuth 2 services configuration">OAuth 2 services configuration</a>';
$string['owner'] = 'Owned by: {$a}';
$string['pluginname'] = 'Microsoft OneDrive';
--- /dev/null
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+ @template core/columns-1to1to1
+
+ Moodle columns-1to1to1 template.
+
+ The purpose of this template is to render a template with 3 columns.
+ On mobile the columns stack underneath each other.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+ * col3content Column 3 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-error'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam pellentesque id urna sit amet tempor.</div>",
+ "col2content": "<div class='alert alert-success'>2. Donec lacus nisl, molestie eget sodales non, sodales et nibh. Praesent dignissim placerat sodales.</div>",
+ "col3content": "<div class='alert alert-info'>3. Praesent sit amet ante odio. In mollis nisl at mi bibendum venenatis.</div>"
+ }
+
+}}
+<div class="row">
+ <div class="col-md-4">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-4">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+ <div class="col-md-4">{{$ column3 }}{{{ col3content }}}{{/ column3 }}</div>
+</div>
--- /dev/null
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+ @template core/columns-1to2
+
+ Moodle columns-1to2 template.
+
+ The purpose of this template is to render 2 columns where the second column has twice the width of the first one.
+ On mobile the second column collapses underneath the first.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ }
+
+}}
+<div class="row">
+ <div class="col-md-4">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-8">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+</div>
--- /dev/null
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+ @template core/columns-2to1
+
+ Moodle columns-2to1 template.
+
+ The purpose of this template is to render 2 columns where the first column has twice the width of the second one.
+ On mobile the second column collapses underneath the first.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+ * col1content Column 1 contents.
+ * col2content Column 2 contents.
+
+ Example context (json):
+ {
+ "col1content": "<div class='alert alert-info'>1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In porttitor vulputate turpis, quis tempor arcu.</div>",
+ "col2content": "<div class='alert alert-success'>2. Vivamus ac orci in velit fringilla aliquam a a nisl. Cras luctus quam laoreet magna pulvinar aliquet.</div>"
+ }
+}}
+<div class="row">
+ <div class="col-md-8">{{$ column1 }}{{{ col1content }}}{{/ column1 }}</div>
+ <div class="col-md-4">{{$ column2 }}{{{ col2content }}}{{/ column2 }}</div>
+</div>
/**
* Generate the XML-RPC fault response.
*
- * @param Exception $ex The exception.
+ * @param Exception|Throwable $ex The exception.
* @param int $faultcode The faultCode to be included in the fault response
* @return string The XML-RPC fault response xml containing the faultCode and faultString.
*/
- protected function generate_error(Exception $ex, $faultcode = 404) {
+ protected function generate_error($ex, $faultcode = 404) {
$error = $ex->getMessage();
if (!empty($ex->errorcode)) {