Merge branch 'MDL-87546-main' of https://github.com/HuongNV13/moodle
This commit is contained in:
@@ -51,8 +51,49 @@ class manager {
|
||||
'tool_mfa' => [
|
||||
'guidance',
|
||||
],
|
||||
'theme_*' => [
|
||||
'loginbackgroundimage',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns allowed fileareas for a component.
|
||||
*
|
||||
* Supports exact component matches and wildcard suffix matches such as `theme_*`.
|
||||
*
|
||||
* @param string $component the component to check.
|
||||
* @return array|null the allowed fileareas, or null if the component is not allowed.
|
||||
*/
|
||||
protected static function get_allowed_fileareas_for_component(string $component): ?array {
|
||||
if (array_key_exists($component, static::ALLOWED_COMPONENTS)) {
|
||||
return static::ALLOWED_COMPONENTS[$component];
|
||||
}
|
||||
|
||||
foreach (static::ALLOWED_COMPONENTS as $pattern => $fileareas) {
|
||||
if (!str_ends_with($pattern, '*')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prefix = substr($pattern, 0, -1);
|
||||
if ($prefix !== '' && str_starts_with($component, $prefix)) {
|
||||
return $fileareas;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a filearea is allowed.
|
||||
*
|
||||
* @param string $filearea the filearea to check.
|
||||
* @param array $allowedfileareas the allowed fileareas for the component, where '*' allows all fileareas.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_allowed_filearea(string $filearea, array $allowedfileareas): bool {
|
||||
return in_array('*', $allowedfileareas) || in_array($filearea, $allowedfileareas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a debug table with current factor information.
|
||||
*
|
||||
@@ -469,13 +510,15 @@ class manager {
|
||||
$component = clean_param(array_shift($args), PARAM_COMPONENT);
|
||||
$filearea = clean_param(array_shift($args), PARAM_AREA);
|
||||
|
||||
$allowedfileareas = static::get_allowed_fileareas_for_component($component);
|
||||
|
||||
// Check allowed components.
|
||||
if (!array_key_exists($component, static::ALLOWED_COMPONENTS)) {
|
||||
if ($allowedfileareas === null) {
|
||||
return self::REDIRECT;
|
||||
}
|
||||
|
||||
// Check allowed fileareas.
|
||||
if (!in_array($filearea, static::ALLOWED_COMPONENTS[$component])) {
|
||||
if (!static::is_allowed_filearea($filearea, $allowedfileareas)) {
|
||||
return self::REDIRECT;
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +277,14 @@ final class manager_test extends \advanced_testcase {
|
||||
$guideurl = new \moodle_url('/pluginfile.php/1/tool_mfa/guidance/0/capybara.png');
|
||||
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($guideurl, false));
|
||||
|
||||
// Access the allowed theme pluginfile area via wildcard component rules.
|
||||
$themeurl = new \moodle_url('/pluginfile.php/1/theme_boost/loginbackgroundimage/0/background.jpg');
|
||||
$this->assertEquals(\tool_mfa\manager::NO_REDIRECT, \tool_mfa\manager::should_require_mfa($themeurl, false));
|
||||
|
||||
// Access a different theme pluginfile area which is not explicitly allowed.
|
||||
$themeurl = new \moodle_url('/pluginfile.php/1/theme_classic/customfield/0/example.txt');
|
||||
$this->assertEquals(\tool_mfa\manager::REDIRECT, \tool_mfa\manager::should_require_mfa($themeurl, false));
|
||||
|
||||
// Access private area.
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
$privateurl = new \moodle_url("/pluginfile.php/{$user3->id}/user/private/privatefile.png");
|
||||
|
||||
@@ -14,7 +14,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| passwordpolicy | 0 |
|
||||
| sitepolicyhandler | tool_policy |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should not see "I understand and agree"
|
||||
And I set the following fields to these values:
|
||||
| Username | user1 |
|
||||
@@ -47,7 +47,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| P1 | This site policy | | full text1 | short text1 | draft |
|
||||
| P1 | This privacy policy | | full text2 | short text2 | draft |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should not see "I understand and agree"
|
||||
And I set the following fields to these values:
|
||||
| Username | user1 |
|
||||
@@ -81,7 +81,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| P1 | This site policy | | full text2 | short text2 | active |
|
||||
| P1 | This site policy | | full text3 | short text3 | draft |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
@@ -128,7 +128,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
|
||||
| This guests policy | 0 | | full text4 | short text4 | active | guest |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
@@ -187,7 +187,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Name | Revision | Content | Summary | Status |
|
||||
| This site policy | | full text2 | short text2 | active |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should see "Age and location verification"
|
||||
And I set the field "What is your age?" to "16"
|
||||
And I set the field "In which country do you live?" to "DZ"
|
||||
@@ -237,7 +237,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| This site policy | 0 | | full text2 | short text2 | active | all |
|
||||
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
|
||||
And I am on homepage
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
And I should see "This site policy"
|
||||
And I press "Next"
|
||||
And I should see "This privacy policy"
|
||||
@@ -457,7 +457,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| This guests policy | 0 | | full text4 | short text4 | active | guest |
|
||||
And I am on homepage
|
||||
And I change window size to "large"
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
Then I should see "If you continue browsing this website, you agree to our policies"
|
||||
# Confirm when navigating, the pop-up policies are displayed.
|
||||
When I am on the "My courses" page
|
||||
@@ -489,7 +489,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| This privacy policy | 1 | | full text3 | short text3 | active | loggedin |
|
||||
| This guests policy | 0 | | full text4 | short text4 | active | guest |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
@@ -521,7 +521,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
Then I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@example.com"
|
||||
And I follow "Log in"
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
# Confirm that the user can view and accept policies when attempting to create another account.
|
||||
Then I should see "This site policy" in the "region-main" "region"
|
||||
And I should see "short text2"
|
||||
@@ -617,10 +617,10 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| P1 | This site policy | | full text3 | short text3 | draft |
|
||||
And I am on homepage
|
||||
# First log in as a guest
|
||||
And I press "Access as a guest"
|
||||
And I press "Log in as guest"
|
||||
# Now sign up
|
||||
And I follow "Log in"
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should see "This site policy"
|
||||
And I should see "short text2"
|
||||
And I should see "full text2"
|
||||
@@ -668,7 +668,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Cookies policy | We eat cookies, srsly | Here goes content. | 0 |
|
||||
| Terms of Service | We teach, you learn | Here goes content. | 1 |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
# The first policy with the agreement style "on its own page" must be accepted first.
|
||||
Then I should see "Digital maturity declaration" in the "region-main" "region"
|
||||
And I should see "You declare be old enough"
|
||||
@@ -831,7 +831,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Cookies policy | We eat cookies, srsly | Here goes content. | 1 | loggedin |
|
||||
| Terms of Service | We teach, you learn | Here goes content. | 1 | guest |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
# All the policies to be displayed one by one with a button to accept each of them prior seeing the next.
|
||||
Then I should see "Digital maturity declaration" in the "region-main" "region"
|
||||
And I should see "You declare be old enough"
|
||||
|
||||
@@ -64,7 +64,7 @@ Feature: Optional policies
|
||||
| OwnPageCompulsory1 | full text4 | short text4 | 1 | 0 |
|
||||
| OwnPageOptional1 | full text5 | short text5 | 1 | 1 |
|
||||
And I am on homepage
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
# Compulsory policies displayed on own page are shown first and must be agreed.
|
||||
And I should see "OwnPageCompulsory1" in the "region-main" "region"
|
||||
And I should see "short text4" in the "region-main" "region"
|
||||
|
||||
@@ -53,6 +53,8 @@ class login implements renderable, templatable {
|
||||
public $cookieshelpicon;
|
||||
/** @var string The error message, if any. */
|
||||
public $error;
|
||||
/** @var string The error title, shown as bold heading above the error message for credential failures. */
|
||||
public $errortitle;
|
||||
/** @var string The info message, if any. */
|
||||
public $info;
|
||||
/** @var moodle_url Forgot password URL. */
|
||||
@@ -114,7 +116,7 @@ class login implements renderable, templatable {
|
||||
if (is_enabled_auth('none')) {
|
||||
$this->instructions = get_string('loginstepsnone');
|
||||
} else if ($CFG->registerauth == 'email' && empty($this->instructions)) {
|
||||
$this->instructions = get_string('loginsteps', 'core', 'signup.php');
|
||||
$this->instructions = get_string('logindonthaveaccount');
|
||||
}
|
||||
|
||||
if ($CFG->maintenance_enabled == true) {
|
||||
@@ -142,12 +144,19 @@ class login implements renderable, templatable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error message.
|
||||
* Set the error message. For the AUTH_LOGIN_FAILED case, also sets
|
||||
* an errortitle so the template can render a bold heading above the detail text.
|
||||
*
|
||||
* @param string $error The error message.
|
||||
* @param int $errorcode The error code from login/index.php.
|
||||
*/
|
||||
public function set_error($error) {
|
||||
$this->error = $error;
|
||||
public function set_error(string $error, int $errorcode = 0): void {
|
||||
if ($errorcode === AUTH_LOGIN_FAILED) {
|
||||
$this->errortitle = get_string('logininvalidlogintitle');
|
||||
$this->error = get_string('logininvalidlogindetail');
|
||||
} else {
|
||||
$this->error = $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,10 +179,10 @@ class login implements renderable, templatable {
|
||||
$data->cansignup = $this->cansignup;
|
||||
$data->cookieshelpicon = $this->cookieshelpicon->export_for_template($output);
|
||||
$data->error = $this->error;
|
||||
$data->errortitle = $this->errortitle;
|
||||
$data->info = $this->info;
|
||||
$data->forgotpasswordurl = $this->forgotpasswordurl->out(false);
|
||||
$data->hasidentityproviders = !empty($this->identityproviders);
|
||||
$data->hasinstructions = !empty($this->instructions) || $this->cansignup;
|
||||
$data->identityproviders = $identityproviders;
|
||||
list($data->instructions, $data->instructionsformat) = \core_external\util::format_text($this->instructions, FORMAT_MOODLE,
|
||||
context_system::instance()->id);
|
||||
|
||||
@@ -9,7 +9,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| registerauth | email |
|
||||
| passwordpolicy | 0 |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then I should not see "I understand and agree"
|
||||
And I set the following fields to these values:
|
||||
| Username | user1 |
|
||||
@@ -38,7 +38,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| passwordpolicy | 0 |
|
||||
| sitepolicy | https://moodle.org |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
Then the field "I understand and agree" matches value "0"
|
||||
And I set the following fields to these values:
|
||||
| Username | user1 |
|
||||
@@ -71,7 +71,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
And I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
And I set the following fields to these values:
|
||||
| Username | s2 |
|
||||
| Password | test |
|
||||
|
||||
@@ -18,7 +18,7 @@ Feature: Test the 'showlogfailures' feature works.
|
||||
And I set the field "Username" to "teacher1"
|
||||
And I set the field "Password" to "wrongpass"
|
||||
And I press "Log in"
|
||||
And I should see "Invalid login, please try again"
|
||||
And I should see "Unable to log in"
|
||||
# Now, log in with the correct credentials.
|
||||
When I set the field "Username" to "teacher1"
|
||||
And I set the field "Password" to "teacher1"
|
||||
@@ -38,7 +38,7 @@ Feature: Test the 'showlogfailures' feature works.
|
||||
And I set the field "Username" to "admin"
|
||||
And I set the field "Password" to "wrongpass"
|
||||
And I press "Log in"
|
||||
And I should see "Invalid login, please try again"
|
||||
And I should see "Unable to log in"
|
||||
# Now, log in with the correct credentials.
|
||||
When I set the field "Username" to "admin"
|
||||
And I set the field "Password" to "admin"
|
||||
|
||||
@@ -35,7 +35,7 @@ Feature: Authentication
|
||||
And I set the field "Username" to "testuser"
|
||||
And I set the field "Password" to "unexisting"
|
||||
And I press "Log in"
|
||||
Then I should see "Invalid login, please try again"
|
||||
Then I should see "Unable to log in"
|
||||
|
||||
Scenario: Log out using the Log out link
|
||||
Given I log in as "admin"
|
||||
|
||||
@@ -16,7 +16,7 @@ Feature: Test if the login form provides the correct feedback
|
||||
And I set the field "Username" to "teacher1"
|
||||
And I set the field "Password" to "incorrect"
|
||||
When I press "Log in"
|
||||
Then I should see "Invalid login, please try again"
|
||||
Then I should see "Unable to log in"
|
||||
|
||||
Scenario: Test login language selector
|
||||
Given remote langimport tests are enabled
|
||||
@@ -60,7 +60,7 @@ Feature: Test if the login form provides the correct feedback
|
||||
Given the following config values are set as admin:
|
||||
| registerauth | Email-based self-registration |
|
||||
And I am on homepage
|
||||
Then I should see "Create new account"
|
||||
Then I should see "Sign up"
|
||||
|
||||
Scenario: Set OAuth providers
|
||||
Given I log in as "admin"
|
||||
|
||||
@@ -12,7 +12,7 @@ Feature: Test the 'Digital age of consent verification' feature works.
|
||||
Scenario: User that is not considered a digital minor attempts to self-register on the site.
|
||||
# Try to access the sign up page.
|
||||
Given I am on homepage
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
Then I should see "Age and location verification"
|
||||
When I set the field "What is your age?" to "16"
|
||||
And I set the field "In which country do you live?" to "DZ"
|
||||
@@ -21,14 +21,14 @@ Feature: Test the 'Digital age of consent verification' feature works.
|
||||
And I should see "Username"
|
||||
# Try to access the sign up page again.
|
||||
When I press "Cancel"
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
Then I should see "New account"
|
||||
And I should see "Username"
|
||||
|
||||
Scenario: User that is considered a digital minor attempts to self-register on the site.
|
||||
# Try to access the sign up page.
|
||||
Given I am on homepage
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
Then I should see "Age and location verification"
|
||||
When I set the field "What is your age?" to "12"
|
||||
And I set the field "In which country do you live?" to "AT"
|
||||
@@ -37,6 +37,6 @@ Feature: Test the 'Digital age of consent verification' feature works.
|
||||
And I should see "Please ask your parent/guardian to contact:"
|
||||
# Try to access the sign up page again.
|
||||
When I click on "Back to the site home" "link"
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
Then I should see "You are too young to create an account on this site."
|
||||
And I should see "Please ask your parent/guardian to contact:"
|
||||
|
||||
@@ -28,7 +28,7 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
|
||||
| Custom instance name | Test student enrolment |
|
||||
And I log out
|
||||
When I am on "Course 1" course homepage
|
||||
And I press "Access as a guest"
|
||||
And I press "Log in as guest"
|
||||
Then I should see "Guests cannot access this course. Please log in."
|
||||
And I press "Continue"
|
||||
And I should see "Log in"
|
||||
|
||||
@@ -1233,20 +1233,40 @@ $string['loggedinasguest'] = 'You are currently using guest access';
|
||||
$string['loggedinnot'] = 'You are not logged in.';
|
||||
$string['login'] = 'Log in';
|
||||
$string['loginactivity'] = 'Login activity';
|
||||
$string['loginagain'] = 'Log in again';
|
||||
$string['loginalready'] = 'You are already logged in';
|
||||
$string['loginas'] = 'Log in as';
|
||||
$string['loginasguest'] = 'Log in as guest';
|
||||
$string['loginaspasswordexplain'] = '<p>You must enter the special "loginas password" to use this feature.<br />If you do not know it, ask your server administrator.</p>';
|
||||
$string['logindonthaveaccount'] = 'Don\'t have an account?';
|
||||
$string['loginenterpassword'] = 'Enter your password';
|
||||
$string['loginenterusernameoremail'] = 'Enter your username or email';
|
||||
$string['login_failure_logs'] = 'Login failure logs';
|
||||
$string['loginforgotpassword'] = 'Forgot password?';
|
||||
$string['loginguest'] = 'Access as a guest';
|
||||
$string['logininvalidlogindetail'] = 'Your login info may be incorrect. Please try again.';
|
||||
$string['logininvalidlogintitle'] = 'Unable to log in';
|
||||
$string['loginpaneltagline'] = 'You\'re part of a global learning community';
|
||||
$string['loginrequired'] = 'Login required';
|
||||
$string['loginseparatoror'] = 'OR';
|
||||
$string['loginsite'] = 'Log in to the site';
|
||||
$string['loginstartsignup'] = 'Sign up';
|
||||
$string['loginstatcountries'] = 'countries';
|
||||
$string['loginstatcountries_value'] = '200+';
|
||||
$string['loginstatsites'] = 'sites';
|
||||
$string['loginstatsites_value'] = '150K';
|
||||
$string['loginstatusers'] = 'users';
|
||||
$string['loginstatusers_value'] = '500M';
|
||||
$string['loginsteps'] = 'For full access to this site, you first need to create an account.';
|
||||
$string['loginstepsnone'] = '<p>Hi!</p>
|
||||
<p>For full access to courses you\'ll need to create yourself an account.</p>
|
||||
<p>All you need to do is make up a username and password and use it in the form on this page!</p>
|
||||
<p>If someone else has already chosen your username then you\'ll have to try again using a different username.</p>';
|
||||
$string['loginto'] = 'Log in to {$a}';
|
||||
$string['loginagain'] = 'Log in again';
|
||||
$string['loginrequired'] = 'Login required';
|
||||
$string['loginwelcomeback'] = 'Welcome back';
|
||||
$string['loginwelcomedescription'] = 'Your learning journey starts here. Access courses, connect with peers, and achieve your goals.';
|
||||
$string['loginwelcometitle'] = 'Welcome to Moodle';
|
||||
$string['loginwith'] = 'Log in with';
|
||||
$string['logoof'] = 'Logo of {$a}';
|
||||
$string['logout'] = 'Log out';
|
||||
$string['logoutconfirm'] = 'Do you really want to log out?';
|
||||
|
||||
@@ -364,8 +364,10 @@ if (!empty($SESSION->loginerrormsg) || !empty($SESSION->logininfomsg)) {
|
||||
// We had some messages before redirect, show them now.
|
||||
$errormsg = $SESSION->loginerrormsg ?? '';
|
||||
$infomsg = $SESSION->logininfomsg ?? '';
|
||||
$errorcode = (int) ($SESSION->loginerrorcode ?? 0);
|
||||
unset($SESSION->loginerrormsg);
|
||||
unset($SESSION->logininfomsg);
|
||||
unset($SESSION->loginerrorcode);
|
||||
|
||||
} else if ($testsession) {
|
||||
// No need to redirect here.
|
||||
@@ -376,6 +378,7 @@ if (!empty($SESSION->loginerrormsg) || !empty($SESSION->logininfomsg)) {
|
||||
// We must redirect after every password submission.
|
||||
if ($errormsg) {
|
||||
$SESSION->loginerrormsg = $errormsg;
|
||||
$SESSION->loginerrorcode = $errorcode;
|
||||
}
|
||||
|
||||
// Add redirect param to url.
|
||||
@@ -404,7 +407,7 @@ if (isloggedin() and !isguestuser()) {
|
||||
echo $OUTPUT->box_end();
|
||||
} else {
|
||||
$loginform = new \core_auth\output\login($authsequence, $frm->username);
|
||||
$loginform->set_error($errormsg);
|
||||
$loginform->set_error($errormsg, $errorcode);
|
||||
$loginform->set_info($infomsg);
|
||||
echo $OUTPUT->render($loginform);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,6 @@ Feature: Login using email address
|
||||
Examples:
|
||||
| authloginviaemail | login | message |
|
||||
| 0 | testuser | You are logged in as |
|
||||
| 0 | user@example.com | Invalid login, please try again |
|
||||
| 0 | user@example.com | Unable to log in |
|
||||
| 1 | testuser | You are logged in as |
|
||||
| 1 | user@example.com | You are logged in as |
|
||||
|
||||
@@ -29,7 +29,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum" "forum activity" page
|
||||
Then I should not see "Add discussion topic"
|
||||
And I should see "Forum discussion 1"
|
||||
@@ -48,7 +48,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| forum | Forum (single discussion) | C1 | forum | single |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum (single discussion)" "forum activity" page
|
||||
Then I should not see "Add discussion topic"
|
||||
And I should see "Forum (single discussion)"
|
||||
@@ -63,7 +63,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum" "forum activity" page
|
||||
Then I should not see "Add discussion topic"
|
||||
And I should see "Forum discussion 1"
|
||||
@@ -80,7 +80,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum" "forum activity" page
|
||||
Then I should see "Add discussion topic"
|
||||
And I click on "Add discussion topic" "link"
|
||||
@@ -108,7 +108,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| forum | Forum (single discussion) | C1 | forum | single |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum (single discussion)" "forum activity" page
|
||||
And I should see "Forum (single discussion)"
|
||||
Then I should see "Reply"
|
||||
@@ -131,7 +131,7 @@ Feature: Guest and not logged users could see the option to add new post or repl
|
||||
| teacher | forum | Forum discussion 1 | How awesome is this forum discussion? |
|
||||
And I log out
|
||||
And I am on "Course 1" course homepage
|
||||
When I press "Access as a guest"
|
||||
When I press "Log in as guest"
|
||||
And I am on the "Forum" "forum activity" page
|
||||
Then I should see "Add discussion topic"
|
||||
And I click on "Add discussion topic" "link"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
namespace theme_boost\output;
|
||||
|
||||
use context_course;
|
||||
use moodle_url;
|
||||
use html_writer;
|
||||
use get_string;
|
||||
@@ -252,4 +253,29 @@ class core_renderer extends \core_renderer {
|
||||
}
|
||||
return $firstview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Boost login form context.
|
||||
*
|
||||
* @param \core_auth\output\login $form The renderable.
|
||||
* @return string
|
||||
*/
|
||||
public function render_login(\core_auth\output\login $form) {
|
||||
global $CFG, $SITE;
|
||||
|
||||
$context = $form->export_for_template($this);
|
||||
$url = $this->get_logo_url();
|
||||
if ($url) {
|
||||
$url = $url->out(false);
|
||||
}
|
||||
$context->logourl = $url;
|
||||
$context->sitename = format_string(
|
||||
$SITE->fullname,
|
||||
true,
|
||||
['context' => context_course::instance(SITEID), 'escape' => false]
|
||||
);
|
||||
$context->hasauthinstructions = !empty($CFG->auth_instructions);
|
||||
|
||||
return $this->render_from_template('core/loginform', $context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,17 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$bodyattributes = $OUTPUT->body_attributes();
|
||||
|
||||
// Left-panel instructions.
|
||||
$leftinstructions = get_string('loginwelcomedescription');
|
||||
if (!empty($CFG->auth_instructions)) {
|
||||
$leftinstructions = $CFG->auth_instructions;
|
||||
}
|
||||
|
||||
$templatecontext = [
|
||||
'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]),
|
||||
'output' => $OUTPUT,
|
||||
'bodyattributes' => $bodyattributes
|
||||
'bodyattributes' => $bodyattributes,
|
||||
'leftinstructions' => $leftinstructions,
|
||||
];
|
||||
|
||||
echo $OUTPUT->render_from_template('theme_boost/login', $templatecontext);
|
||||
|
||||
|
||||
@@ -58,8 +58,9 @@ function theme_boost_get_extra_scss($theme) {
|
||||
// Sets the login background image.
|
||||
$loginbackgroundimageurl = $theme->setting_file_url('loginbackgroundimage', 'loginbackgroundimage');
|
||||
if (!empty($loginbackgroundimageurl)) {
|
||||
$content .= 'body.pagelayout-login #page { ';
|
||||
$content .= "background-image: url('$loginbackgroundimageurl'); background-size: cover;";
|
||||
$content .= 'body.pagelayout-login #page .login-layout-left { ';
|
||||
$content .= "background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), " .
|
||||
"url('$loginbackgroundimageurl'); background-size: cover;";
|
||||
$content .= ' }';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,84 +1,164 @@
|
||||
$loginbackground-gradient-from: $gray-100 !default;
|
||||
$loginbackground-gradient-to: $gray-300 !default;
|
||||
$logincontainer-bg: $white !default;
|
||||
$logincontainer-padding: 3rem !default;
|
||||
$logincontainer-shadow: $box-shadow !default;
|
||||
$logindivider-margin-top: 1.5rem !default;
|
||||
$logindivider-margin-bottom: 1.5rem !default;
|
||||
$logindivider-border: $border-color !default;
|
||||
$login-identity-provider-btn-border: $border-color !default;
|
||||
$login-layout-left-bg: $primary !default;
|
||||
$login-layout-panel-padding: 3rem !default;
|
||||
$login-layout-content-width: 576px !default;
|
||||
$login-layout-copy-width: 360px !default;
|
||||
$login-input-bg: $gray-100 !default;
|
||||
|
||||
.pagelayout-login #region-main {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
}
|
||||
.pagelayout-login {
|
||||
#region-main,
|
||||
#region-main-box {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pagelayout-login #page {
|
||||
background: $loginbackground-gradient-from;
|
||||
@include gradient-x($loginbackground-gradient-from, $loginbackground-gradient-to, 0%, 100%);
|
||||
div[role="main"] {
|
||||
height: 100%;
|
||||
#page {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
.login-layout-left {
|
||||
background-color: $login-layout-left-bg;
|
||||
color: color-contrast($login-layout-left-bg);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: $login-layout-panel-padding;
|
||||
}
|
||||
|
||||
.login-layout-left-content {
|
||||
max-width: $login-layout-copy-width;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-layout-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.login-layout-stat {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.login-layout-stat-value {
|
||||
font-size: $h5-font-size;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.login-layout-right {
|
||||
min-height: 100vh;
|
||||
padding: $login-layout-panel-padding;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-layout-right-content {
|
||||
width: 100%;
|
||||
max-width: $login-layout-content-width;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background-color: $logincontainer-bg;
|
||||
padding: $logincontainer-padding;
|
||||
box-shadow: $logincontainer-shadow;
|
||||
margin-bottom: 2rem;
|
||||
@include border-radius();
|
||||
.login-languagemenu {
|
||||
.login-form {
|
||||
.form-control {
|
||||
background-color: $login-input-bg;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $login-input-bg;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $login-input-bg;
|
||||
border-color: $input-focus-border-color;
|
||||
box-shadow: $input-focus-box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-sensitive-wrapper .toggle-sensitive-btn {
|
||||
--bs-btn-bg: #{$login-input-bg};
|
||||
--bs-btn-border-color: #{$input-border-color};
|
||||
--bs-btn-hover-bg: #{$login-input-bg};
|
||||
--bs-btn-hover-border-color: #{$input-border-color};
|
||||
--bs-btn-active-bg: #{$login-input-bg};
|
||||
--bs-btn-active-border-color: #{$input-border-color};
|
||||
border-inline-start: none;
|
||||
color: $gray-700;
|
||||
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
background-color: $login-input-bg;
|
||||
border-color: $input-focus-border-color;
|
||||
box-shadow: $input-focus-box-shadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-separator {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: $gray-600;
|
||||
font-size: $font-size-sm;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-top: $border-width solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.login-identityprovider-btn {
|
||||
width: 100%;
|
||||
background-color: $white;
|
||||
border-color: $gray-400;
|
||||
color: $gray-600;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $gray-100;
|
||||
border-color: $gray-500;
|
||||
}
|
||||
}
|
||||
|
||||
.login-cookiesnotice {
|
||||
.btn-link {
|
||||
font-size: $font-size-sm;
|
||||
color: $gray-600;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $gray-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-languagemenu {
|
||||
.dropdown-menu {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.login-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.login-divider {
|
||||
margin-top: $logindivider-margin-top;
|
||||
margin-bottom: $logindivider-margin-bottom;
|
||||
border-top: $border-width solid $logindivider-border;
|
||||
}
|
||||
|
||||
h1.login-heading {
|
||||
font-size: $h2-font-size;
|
||||
}
|
||||
|
||||
h2.login-heading {
|
||||
font-size: $h4-font-size;
|
||||
}
|
||||
.login-identityproviders {
|
||||
.login-identityprovider-btn {
|
||||
border: $border-width solid $login-identity-provider-btn-border;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
width: $border-width;
|
||||
background-color: $gray-300;
|
||||
height: $font-size-base * 2;
|
||||
}
|
||||
.action-menu-trigger {
|
||||
a {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.login-container {
|
||||
width: 500px !important; /* stylelint-disable-line declaration-no-important */
|
||||
@include media-breakpoint-down(lg) {
|
||||
.login-layout-right {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
.login-layout-right-content {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35550,72 +35550,135 @@ textarea[data-auto-rows] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pagelayout-login #region-main {
|
||||
.pagelayout-login #region-main,
|
||||
.pagelayout-login #region-main-box {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pagelayout-login #page {
|
||||
background: #f8f9fa;
|
||||
background-image: linear-gradient(to right, #f8f9fa 0%, #dee2e6 100%);
|
||||
}
|
||||
.pagelayout-login #page div[role=main] {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
.login-layout-left {
|
||||
background-color: #0f6cbf;
|
||||
color: #ffffff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background-color: #ffffff;
|
||||
padding: 3rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
margin-bottom: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
.login-layout-left-content {
|
||||
max-width: 360px;
|
||||
text-align: center;
|
||||
}
|
||||
.login-container .login-languagemenu {
|
||||
|
||||
.login-layout-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.login-layout-stat {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.login-layout-stat-value {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.login-layout-right {
|
||||
min-height: 100vh;
|
||||
padding: 3rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-layout-right-content {
|
||||
width: 100%;
|
||||
max-width: 576px;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.login-container .login-form .form-control {
|
||||
background-color: #f8f9fa;
|
||||
box-shadow: none;
|
||||
}
|
||||
.login-container .login-form .form-control:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.login-container .login-form .form-control:focus {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #87b6df;
|
||||
box-shadow: 0 0 0 0.25rem rgba(15, 108, 191, 0.75);
|
||||
}
|
||||
.login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn {
|
||||
--bs-btn-bg: #f8f9fa;
|
||||
--bs-btn-border-color: var(--bs-gray-500);
|
||||
--bs-btn-hover-bg: #f8f9fa;
|
||||
--bs-btn-hover-border-color: var(--bs-gray-500);
|
||||
--bs-btn-active-bg: #f8f9fa;
|
||||
--bs-btn-active-border-color: var(--bs-gray-500);
|
||||
border-inline-start: none;
|
||||
color: #495057;
|
||||
}
|
||||
.login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn:focus, .login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn:focus-visible {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #87b6df;
|
||||
box-shadow: 0 0 0 0.25rem rgba(15, 108, 191, 0.75);
|
||||
}
|
||||
.login-container .login-separator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: #6a737b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.login-container .login-separator::before, .login-container .login-separator::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container .login-identityprovider-btn {
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-color: #ced4da;
|
||||
color: #6a737b;
|
||||
}
|
||||
.login-container .login-identityprovider-btn:hover, .login-container .login-identityprovider-btn:focus {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #8f959e;
|
||||
}
|
||||
.login-container .login-cookiesnotice .btn-link {
|
||||
font-size: 0.875rem;
|
||||
color: #6a737b;
|
||||
}
|
||||
.login-container .login-cookiesnotice .btn-link:hover, .login-container .login-cookiesnotice .btn-link:focus {
|
||||
color: #495057;
|
||||
}
|
||||
.login-container .login-languagemenu .dropdown-menu {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.login-container .login-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.login-container .login-divider {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container h1.login-heading, .login-container .login-heading.h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.login-container h2.login-heading, .login-container .login-heading.h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.login-container .login-identityproviders .login-identityprovider-btn {
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container .divider {
|
||||
width: 1px;
|
||||
background-color: #dee2e6;
|
||||
height: 2rem;
|
||||
}
|
||||
.login-container .action-menu-trigger a {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.login-container {
|
||||
width: 500px !important; /* stylelint-disable-line declaration-no-important */
|
||||
@media (max-width: 991.98px) {
|
||||
.login-layout-right {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
.login-layout-right-content {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
/* modules.less */
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{{!
|
||||
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/login_layout
|
||||
|
||||
Split-screen layout for login-flow pages (login, MFA, etc.).
|
||||
|
||||
Provides a two-column structure: a decorative left panel (hidden on mobile)
|
||||
and a right panel that receives the page's main content. The left panel
|
||||
content is loaded from the core/login_panel partial, which themes can
|
||||
override to customise the welcome copy and branding.
|
||||
|
||||
Context variables required for this template:
|
||||
* output - The core renderer output object (provides main_content).
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"output": {
|
||||
"main_content": "<h1>Login form goes here</h1>"
|
||||
}
|
||||
}
|
||||
}}
|
||||
<div id="page-content" class="min-vh-100 row g-0">
|
||||
<aside class="login-layout-left col-lg-6 d-none d-lg-flex" aria-hidden="true">
|
||||
{{> core/login_panel }}
|
||||
</aside>
|
||||
<div id="region-main" class="login-layout-right col-12 col-lg-6">
|
||||
<div id="region-main-box" class="login-layout-right-content">
|
||||
<div class="login-container">
|
||||
{{{ output.main_content }}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
{{!
|
||||
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/login_panel
|
||||
|
||||
Default content for the left panel of the split-screen login layout.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"leftinstructions": "<p>Your learning journey starts here. Access courses, connect with peers, and achieve your goals.</p>"
|
||||
}
|
||||
}}
|
||||
<div class="login-layout-left-content">
|
||||
<h1 class="h2 mb-3">{{#str}}loginwelcometitle{{/str}}</h1>
|
||||
<div class="mx-auto mb-4 text-start">
|
||||
{{#leftinstructions}}
|
||||
{{{leftinstructions}}}
|
||||
{{/leftinstructions}}
|
||||
</div>
|
||||
<p class="mx-auto mb-4">{{#str}}loginpaneltagline{{/str}}</p>
|
||||
<div class="login-layout-stats" role="presentation">
|
||||
<div class="login-layout-stat">
|
||||
<strong class="login-layout-stat-value">{{#str}}loginstatusers_value{{/str}}</strong>
|
||||
<span>{{#str}}loginstatusers{{/str}}</span>
|
||||
</div>
|
||||
<div class="login-layout-stat">
|
||||
<strong class="login-layout-stat-value">{{#str}}loginstatsites_value{{/str}}</strong>
|
||||
<span>{{#str}}loginstatsites{{/str}}</span>
|
||||
</div>
|
||||
<div class="login-layout-stat">
|
||||
<strong class="login-layout-stat-value">{{#str}}loginstatcountries_value{{/str}}</strong>
|
||||
<span>{{#str}}loginstatcountries{{/str}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,253 @@
|
||||
{{!
|
||||
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/loginform
|
||||
|
||||
Moodle template for the login page.
|
||||
|
||||
Context variables required for this template:
|
||||
* autofocusform: Auto focus on form ?,
|
||||
* canloginasguest - Is guest login allowed?,
|
||||
* canloginbyemail - Is login by email allowed?,
|
||||
* cansignup - Signup allowed?,
|
||||
* showloginform - Hide the standard login form if it not necessary (only OAuth2 for example)
|
||||
* error - Any errors in the form?,
|
||||
* errortitle - Optional bold heading shown above the error message (credential failures only),
|
||||
* hasauthinstructions - Flag, set to true when auth_instructions is configured,
|
||||
* info - Info notification to display,
|
||||
* forgotpasswordurl - Forgot password url,
|
||||
* hasidentityproviders - Flag, set to true to hide identity providers,
|
||||
* identityproviders - List of identity providers,
|
||||
* instructions - Instructions,
|
||||
* loginurl - Login url,
|
||||
* signupurl - Signup url,
|
||||
* logourl - Flag, logo url,
|
||||
* sitename - Name of site.,
|
||||
* logintoken - Random token to protect login request.,
|
||||
* maintenance - Maintenance message
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"autofocusform": false,
|
||||
"canloginasguest": "1",
|
||||
"canloginbyemail": false,
|
||||
"cansignup": true,
|
||||
"showloginform": true,
|
||||
"error": "",
|
||||
"errortitle": "",
|
||||
"hasauthinstructions": false,
|
||||
"info": "",
|
||||
"forgotpasswordurl": "http://localhost/stable_master/login/forgot_password.php",
|
||||
"hasidentityproviders": false,
|
||||
"identityproviders": [],
|
||||
"instructions": "For full access to this site, you first need to create an account.",
|
||||
"loginurl": "http://localhost/stable_master/login/index.php",
|
||||
"signupurl": "http://localhost/stable_master/login/signup.php",
|
||||
"logourl": false,
|
||||
"sitename": "Beer & Chips",
|
||||
"logintoken": "randomstring",
|
||||
"maintenance": "For full access to this site, you need to login in as an admin.",
|
||||
"languagemenu": "Choose language",
|
||||
"togglepassword": true,
|
||||
"smallscreensonly": true
|
||||
}
|
||||
}}
|
||||
|
||||
<div class="loginform">
|
||||
{{#logourl}}
|
||||
<div id="loginlogo" class="d-flex justify-content-center mb-4">
|
||||
<img id="logoimage" src="{{logourl}}" class="img-fluid" alt="{{sitename}}"/>
|
||||
</div>
|
||||
{{/logourl}}
|
||||
<h1 class="h2">{{#str}}loginwelcomeback{{/str}}</h1>
|
||||
<p class="text-muted mb-4">{{#str}} loginto, core, {{sitename}} {{/str}}</p>
|
||||
{{#maintenance}}
|
||||
<div class="alert alert-danger login-maintenance">
|
||||
{{{maintenance}}}
|
||||
</div>
|
||||
{{/maintenance}}
|
||||
{{#error}}
|
||||
<div class="alert alert-danger" id="loginerrormessage" role="alert">
|
||||
{{#errortitle}}<strong class="d-block mb-1">{{errortitle}}</strong>{{/errortitle}}
|
||||
{{error}}
|
||||
</div>
|
||||
{{/error}}
|
||||
{{#info}}
|
||||
<div class="alert alert-info" id="logininfomessage" role="status">{{info}}</div>
|
||||
{{/info}}
|
||||
{{#showloginform}}
|
||||
<form class="login-form" action="{{loginurl}}" method="post" id="login">
|
||||
<input id="anchor" type="hidden" name="anchor" value="">
|
||||
<script>document.getElementById('anchor').value = location.hash;</script>
|
||||
<input type="hidden" name="logintoken" value="{{logintoken}}">
|
||||
<div class="login-form-username mb-3">
|
||||
<label for="username" class="form-label">
|
||||
{{^canloginbyemail}}
|
||||
{{#str}} username {{/str}}
|
||||
{{/canloginbyemail}}
|
||||
{{#canloginbyemail}}
|
||||
{{#str}} usernameemail {{/str}}
|
||||
{{/canloginbyemail}}
|
||||
</label>
|
||||
<input type="text" name="username" id="username" {{!
|
||||
!}}class="form-control" {{!
|
||||
!}}value="{{username}}" {{!
|
||||
!}}placeholder="{{^canloginbyemail}}{{#str}}enterusername{{/str}}{{/canloginbyemail}}{{!
|
||||
!}}{{#canloginbyemail}}{{#str}}loginenterusernameoremail{{/str}}{{/canloginbyemail}}" {{!
|
||||
!}}autocomplete="username">
|
||||
</div>
|
||||
<div class="login-form-password mb-3">
|
||||
<label for="password" class="form-label">{{#str}} password {{/str}}</label>
|
||||
<input type="password" name="password" id="password" value="" {{!
|
||||
!}}class="form-control" {{!
|
||||
!}}placeholder="{{#str}}loginenterpassword{{/str}}" {{!
|
||||
!}}autocomplete="current-password">
|
||||
</div>
|
||||
{{#recaptcha}}
|
||||
<div class="login-form-recaptcha mb-3">
|
||||
{{{recaptcha}}}
|
||||
</div>
|
||||
{{/recaptcha}}
|
||||
<div class="text-end mb-3">
|
||||
<a href="{{forgotpasswordurl}}">{{#str}}loginforgotpassword{{/str}}</a>
|
||||
</div>
|
||||
<div class="login-form-submit mb-3">
|
||||
<button class="btn btn-primary w-100" type="submit" id="loginbtn">{{#str}}login{{/str}}</button>
|
||||
</div>
|
||||
</form>
|
||||
{{/showloginform}}
|
||||
{{#hasauthinstructions}}
|
||||
<div class="d-lg-none small mb-3">
|
||||
{{{instructions}}}
|
||||
</div>
|
||||
{{/hasauthinstructions}}
|
||||
{{^hasauthinstructions}}
|
||||
{{#cansignup}}
|
||||
<div class="text-center small mb-3">
|
||||
{{#instructions}}
|
||||
<span class="text-muted me-1">{{{instructions}}}</span>
|
||||
{{/instructions}}
|
||||
<a href="{{signupurl}}">{{#str}}loginstartsignup{{/str}}</a>
|
||||
</div>
|
||||
{{/cansignup}}
|
||||
{{^cansignup}}
|
||||
{{#instructions}}
|
||||
<p class="small mb-3">{{{instructions}}}</p>
|
||||
{{/instructions}}
|
||||
{{/cansignup}}
|
||||
{{/hasauthinstructions}}
|
||||
{{#hasauthinstructions}}
|
||||
{{#cansignup}}
|
||||
<div class="text-center small mb-3">
|
||||
<a href="{{signupurl}}">{{#str}}loginstartsignup{{/str}}</a>
|
||||
</div>
|
||||
{{/cansignup}}
|
||||
{{/hasauthinstructions}}
|
||||
{{#hasidentityproviders}}
|
||||
<div class="login-separator my-5">{{#str}}loginseparatoror{{/str}}</div>
|
||||
{{/hasidentityproviders}}
|
||||
{{^hasidentityproviders}}
|
||||
{{#canloginasguest}}
|
||||
<div class="login-separator my-5">{{#str}}loginseparatoror{{/str}}</div>
|
||||
{{/canloginasguest}}
|
||||
{{/hasidentityproviders}}
|
||||
{{#hasidentityproviders}}
|
||||
<div class="login-identityproviders">
|
||||
{{#identityproviders}}
|
||||
<a class="btn login-identityprovider-btn btn-outline-secondary mb-3" href="{{url}}">
|
||||
{{#iconurl}}
|
||||
<img src="{{iconurl}}" alt="" width="24" height="24"/>
|
||||
{{/iconurl}}
|
||||
{{#str}}loginwith{{/str}} {{name}}
|
||||
</a>
|
||||
{{/identityproviders}}
|
||||
</div>
|
||||
{{/hasidentityproviders}}
|
||||
{{#canloginasguest}}
|
||||
<form action="{{loginurl}}" method="post" id="guestlogin" class="mb-4">
|
||||
<input type="hidden" name="logintoken" value="{{logintoken}}">
|
||||
<input type="hidden" name="username" value="guest" />
|
||||
<input type="hidden" name="password" value="guest" />
|
||||
<button class="btn btn-outline-secondary login-identityprovider-btn" type="submit" id="loginguestbtn"><i class="fa fa-user me-2" aria-hidden="true"></i>{{#str}}loginasguest{{/str}}</button>
|
||||
</form>
|
||||
{{/canloginasguest}}
|
||||
{{#languagemenu}}
|
||||
<div class="login-languagemenu d-flex justify-content-center my-5">
|
||||
{{>core/action_menu}}
|
||||
</div>
|
||||
{{/languagemenu}}
|
||||
<div class="login-cookiesnotice text-center">
|
||||
<button type="button" class="btn btn-link p-0" {{!
|
||||
}} data-modal="alert"{{!
|
||||
}} data-modal-title-str='["cookiesenabled", "core"]' {{!
|
||||
}} data-modal-content-str='["cookiesenabled_help_html", "core"]'{{!
|
||||
}}>{{#str}}cookiesnotice{{/str}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
{{^error}}
|
||||
{{#autofocusform}}
|
||||
require(['core_form/events'], function(FormEvent) {
|
||||
function autoFocus() {
|
||||
const userNameField = document.getElementById('username');
|
||||
const passwordField = document.getElementById('password');
|
||||
if (userNameField && userNameField.value.length == 0) {
|
||||
userNameField.focus();
|
||||
} else if (passwordField) {
|
||||
passwordField.focus();
|
||||
}
|
||||
}
|
||||
autoFocus();
|
||||
window.addEventListener(FormEvent.eventTypes.fieldStructureChanged, autoFocus);
|
||||
});
|
||||
{{/autofocusform}}
|
||||
{{/error}}
|
||||
require(['core/pending'], function(Pending) {
|
||||
const errorMessageDiv = document.getElementById('loginerrormessage');
|
||||
const infoMessageDiv = document.getElementById('logininfomessage');
|
||||
const errorMessage = errorMessageDiv?.textContent.trim();
|
||||
const infoMessage = infoMessageDiv?.textContent.trim();
|
||||
if (errorMessage || infoMessage) {
|
||||
const pendingJS = new Pending('login-move-focus');
|
||||
const usernameField = document.getElementById('username');
|
||||
setTimeout(function() {
|
||||
if (errorMessage && usernameField) {
|
||||
usernameField.focus();
|
||||
}
|
||||
if (errorMessage) {
|
||||
errorMessageDiv.innerHTML += " ";
|
||||
}
|
||||
if (infoMessage) {
|
||||
infoMessageDiv.innerHTML += " ";
|
||||
}
|
||||
pendingJS.resolve();
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
{{#togglepassword}}
|
||||
require(['core/togglesensitive'], function(ToggleSensitive) {
|
||||
ToggleSensitive.init("password", {{smallscreensonly}});
|
||||
});
|
||||
{{/togglepassword}}
|
||||
require(['core_form/submit'], function(Submit) {
|
||||
Submit.init("loginbtn");
|
||||
{{#canloginasguest}}
|
||||
Submit.init("loginguestbtn");
|
||||
{{/canloginasguest}}
|
||||
});
|
||||
{{/js}}
|
||||
@@ -38,18 +38,8 @@
|
||||
|
||||
{{{ output.standard_top_of_body_html }}}
|
||||
|
||||
<div id="page" class="container-fluid pt-5 mt-0">
|
||||
<div id="page-content" class="row">
|
||||
<div id="region-main-box" class="col-12">
|
||||
<div id="region-main" class="col-12 h-100">
|
||||
<div class="login-wrapper">
|
||||
<div class="login-container">
|
||||
{{{ output.main_content }}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="page" class="mt-0">
|
||||
{{> core/login_layout }}
|
||||
</div>
|
||||
{{> theme_boost/footer }}
|
||||
</div>
|
||||
|
||||
@@ -109,8 +109,9 @@ function theme_classic_get_extra_scss($theme) {
|
||||
// Sets the login background image.
|
||||
$loginbackgroundimageurl = $theme->setting_file_url('loginbackgroundimage', 'loginbackgroundimage');
|
||||
if (!empty($loginbackgroundimageurl)) {
|
||||
$content .= 'body.pagelayout-login #page { ';
|
||||
$content .= "background-image: url('$loginbackgroundimageurl'); background-size: cover;";
|
||||
$content .= 'body.pagelayout-login #page .login-layout-left { ';
|
||||
$content .= "background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), " .
|
||||
"url('$loginbackgroundimageurl'); background-size: cover;";
|
||||
$content .= ' }';
|
||||
}
|
||||
|
||||
|
||||
@@ -35550,72 +35550,135 @@ textarea[data-auto-rows] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pagelayout-login #region-main {
|
||||
.pagelayout-login #region-main,
|
||||
.pagelayout-login #region-main-box {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pagelayout-login #page {
|
||||
background: #f8f9fa;
|
||||
background-image: linear-gradient(to right, #f8f9fa 0%, #dee2e6 100%);
|
||||
}
|
||||
.pagelayout-login #page div[role=main] {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
.login-layout-left {
|
||||
background-color: #0f6cbf;
|
||||
color: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background-color: #fff;
|
||||
padding: 3rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
margin-bottom: 2rem;
|
||||
border-radius: 0.375rem;
|
||||
.login-layout-left-content {
|
||||
max-width: 360px;
|
||||
text-align: center;
|
||||
}
|
||||
.login-container .login-languagemenu {
|
||||
|
||||
.login-layout-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.login-layout-stat {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.login-layout-stat-value {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.login-layout-right {
|
||||
min-height: 100vh;
|
||||
padding: 3rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-layout-right-content {
|
||||
width: 100%;
|
||||
max-width: 576px;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.login-container .login-form .form-control {
|
||||
background-color: #f8f9fa;
|
||||
box-shadow: none;
|
||||
}
|
||||
.login-container .login-form .form-control:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.login-container .login-form .form-control:focus {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #87b6df;
|
||||
box-shadow: 0 0 0 0.25rem rgba(15, 108, 191, 0.75);
|
||||
}
|
||||
.login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn {
|
||||
--bs-btn-bg: #f8f9fa;
|
||||
--bs-btn-border-color: #8f959e;
|
||||
--bs-btn-hover-bg: #f8f9fa;
|
||||
--bs-btn-hover-border-color: #8f959e;
|
||||
--bs-btn-active-bg: #f8f9fa;
|
||||
--bs-btn-active-border-color: #8f959e;
|
||||
border-inline-start: none;
|
||||
color: #495057;
|
||||
}
|
||||
.login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn:focus, .login-container .login-form .toggle-sensitive-wrapper .toggle-sensitive-btn:focus-visible {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #87b6df;
|
||||
box-shadow: 0 0 0 0.25rem rgba(15, 108, 191, 0.75);
|
||||
}
|
||||
.login-container .login-separator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
color: #6a737b;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.login-container .login-separator::before, .login-container .login-separator::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container .login-identityprovider-btn {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-color: #ced4da;
|
||||
color: #6a737b;
|
||||
}
|
||||
.login-container .login-identityprovider-btn:hover, .login-container .login-identityprovider-btn:focus {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #8f959e;
|
||||
}
|
||||
.login-container .login-cookiesnotice .btn-link {
|
||||
font-size: 0.875rem;
|
||||
color: #6a737b;
|
||||
}
|
||||
.login-container .login-cookiesnotice .btn-link:hover, .login-container .login-cookiesnotice .btn-link:focus {
|
||||
color: #495057;
|
||||
}
|
||||
.login-container .login-languagemenu .dropdown-menu {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.login-container .login-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.login-container .login-divider {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container h1.login-heading, .login-container .login-heading.h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.login-container h2.login-heading, .login-container .login-heading.h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.login-container .login-identityproviders .login-identityprovider-btn {
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
.login-container .divider {
|
||||
width: 1px;
|
||||
background-color: #dee2e6;
|
||||
height: 2rem;
|
||||
}
|
||||
.login-container .action-menu-trigger a {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.login-container {
|
||||
width: 500px !important; /* stylelint-disable-line declaration-no-important */
|
||||
@media (max-width: 991.98px) {
|
||||
.login-layout-right {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
.login-layout-right-content {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
/* modules.less */
|
||||
|
||||
@@ -32,7 +32,7 @@ Feature: Custom profile fields should be visible and editable by those with the
|
||||
@javascript
|
||||
Scenario: Visible custom profile fields can be part of the sign up form for anonymous users.
|
||||
Given I am on homepage
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
And I expand all fieldsets
|
||||
Then I should not see "notvisible_field"
|
||||
And I should see "uservisible_field"
|
||||
@@ -44,7 +44,7 @@ Feature: Custom profile fields should be visible and editable by those with the
|
||||
Given I log in as "guest"
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I click on "Create new account" "link"
|
||||
When I click on "Sign up" "link"
|
||||
And I expand all fieldsets
|
||||
Then I should not see "notvisible_field"
|
||||
And I should see "uservisible_field"
|
||||
|
||||
@@ -10,7 +10,7 @@ Feature: Both first name and last name are always available for every user
|
||||
| registerauth | email |
|
||||
| passwordpolicy | 0 |
|
||||
And I am on homepage
|
||||
And I click on "Create new account" "link"
|
||||
And I click on "Sign up" "link"
|
||||
When I set the following fields to these values:
|
||||
| Username | mrwhitespace |
|
||||
| Password | Gue$$m3ifY0uC&n |
|
||||
|
||||
Reference in New Issue
Block a user