diff --git a/public/admin/tool/mfa/classes/manager.php b/public/admin/tool/mfa/classes/manager.php index 74e9ca7216a..ab557b6f796 100644 --- a/public/admin/tool/mfa/classes/manager.php +++ b/public/admin/tool/mfa/classes/manager.php @@ -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; } diff --git a/public/admin/tool/mfa/tests/manager_test.php b/public/admin/tool/mfa/tests/manager_test.php index 16e97565198..35f856e3f9a 100644 --- a/public/admin/tool/mfa/tests/manager_test.php +++ b/public/admin/tool/mfa/tests/manager_test.php @@ -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"); diff --git a/public/admin/tool/policy/tests/behat/consent.feature b/public/admin/tool/policy/tests/behat/consent.feature index 25f3f261d8e..27d3b13d3b2 100644 --- a/public/admin/tool/policy/tests/behat/consent.feature +++ b/public/admin/tool/policy/tests/behat/consent.feature @@ -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" diff --git a/public/admin/tool/policy/tests/behat/optional.feature b/public/admin/tool/policy/tests/behat/optional.feature index b56906d1754..9a0617947f3 100644 --- a/public/admin/tool/policy/tests/behat/optional.feature +++ b/public/admin/tool/policy/tests/behat/optional.feature @@ -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" diff --git a/public/auth/classes/output/login.php b/public/auth/classes/output/login.php index 5b6db8b7c27..fb245885ccc 100644 --- a/public/auth/classes/output/login.php +++ b/public/auth/classes/output/login.php @@ -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); diff --git a/public/auth/email/tests/behat/signup.feature b/public/auth/email/tests/behat/signup.feature index fd72a681439..cdc07c12a17 100644 --- a/public/auth/email/tests/behat/signup.feature +++ b/public/auth/email/tests/behat/signup.feature @@ -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 | diff --git a/public/auth/tests/behat/displayloginfailures.feature b/public/auth/tests/behat/displayloginfailures.feature index bca58bf045d..0fa5a788156 100644 --- a/public/auth/tests/behat/displayloginfailures.feature +++ b/public/auth/tests/behat/displayloginfailures.feature @@ -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" diff --git a/public/auth/tests/behat/login.feature b/public/auth/tests/behat/login.feature index 158437ba632..b9479a40f49 100644 --- a/public/auth/tests/behat/login.feature +++ b/public/auth/tests/behat/login.feature @@ -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" diff --git a/public/auth/tests/behat/loginform.feature b/public/auth/tests/behat/loginform.feature index 4379e3e4f6f..fcc529f0de1 100644 --- a/public/auth/tests/behat/loginform.feature +++ b/public/auth/tests/behat/loginform.feature @@ -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" diff --git a/public/auth/tests/behat/verifyageofconsent.feature b/public/auth/tests/behat/verifyageofconsent.feature index ded4752b5be..501c8b6d165 100644 --- a/public/auth/tests/behat/verifyageofconsent.feature +++ b/public/auth/tests/behat/verifyageofconsent.feature @@ -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:" diff --git a/public/enrol/self/tests/behat/self_enrolment.feature b/public/enrol/self/tests/behat/self_enrolment.feature index 09ee584699a..e7d78ada192 100644 --- a/public/enrol/self/tests/behat/self_enrolment.feature +++ b/public/enrol/self/tests/behat/self_enrolment.feature @@ -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" diff --git a/public/lang/en/moodle.php b/public/lang/en/moodle.php index 0a1e027ebc4..c001dfb15e4 100644 --- a/public/lang/en/moodle.php +++ b/public/lang/en/moodle.php @@ -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'] = '
You must enter the special "loginas password" to use this feature.
If you do not know it, ask your server administrator.
Hi!
For full access to courses you\'ll need to create yourself an account.
All you need to do is make up a username and password and use it in the form on this page!
If someone else has already chosen your username then you\'ll have to try again using a different username.
'; $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?'; diff --git a/public/login/index.php b/public/login/index.php index 3cf67bf82bc..bdbfb9b4d35 100644 --- a/public/login/index.php +++ b/public/login/index.php @@ -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); } diff --git a/public/login/tests/behat/email_login.feature b/public/login/tests/behat/email_login.feature index 2e55b34fb0a..1bfabce5b90 100644 --- a/public/login/tests/behat/email_login.feature +++ b/public/login/tests/behat/email_login.feature @@ -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 | diff --git a/public/mod/forum/tests/behat/guest_users.feature b/public/mod/forum/tests/behat/guest_users.feature index f19be705fba..22c361663d1 100644 --- a/public/mod/forum/tests/behat/guest_users.feature +++ b/public/mod/forum/tests/behat/guest_users.feature @@ -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" diff --git a/public/theme/boost/classes/output/core_renderer.php b/public/theme/boost/classes/output/core_renderer.php index 62ef886d9fb..d26e093ac0b 100644 --- a/public/theme/boost/classes/output/core_renderer.php +++ b/public/theme/boost/classes/output/core_renderer.php @@ -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); + } } diff --git a/public/theme/boost/layout/login.php b/public/theme/boost/layout/login.php index e86169dfc06..1a2a0c24d7c 100644 --- a/public/theme/boost/layout/login.php +++ b/public/theme/boost/layout/login.php @@ -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); - diff --git a/public/theme/boost/lib.php b/public/theme/boost/lib.php index 3207cfe683c..df664c7d167 100644 --- a/public/theme/boost/lib.php +++ b/public/theme/boost/lib.php @@ -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 .= ' }'; } diff --git a/public/theme/boost/scss/moodle/login.scss b/public/theme/boost/scss/moodle/login.scss index 3d10cf0b8cf..364b1524442 100644 --- a/public/theme/boost/scss/moodle/login.scss +++ b/public/theme/boost/scss/moodle/login.scss @@ -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; } } diff --git a/public/theme/boost/style/moodle.css b/public/theme/boost/style/moodle.css index 9bbdb9edb9b..ceb9e1ed194 100644 --- a/public/theme/boost/style/moodle.css +++ b/public/theme/boost/style/moodle.css @@ -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 */ diff --git a/public/theme/boost/templates/core/login_layout.mustache b/public/theme/boost/templates/core/login_layout.mustache new file mode 100644 index 00000000000..92c1098ab6c --- /dev/null +++ b/public/theme/boost/templates/core/login_layout.mustache @@ -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, seeYour learning journey starts here. Access courses, connect with peers, and achieve your goals.
" + } +}} +{{#str}}loginpaneltagline{{/str}}
+{{#str}} loginto, core, {{sitename}} {{/str}}
+ {{#maintenance}} +{{{instructions}}}
+ {{/instructions}} + {{/cansignup}} + {{/hasauthinstructions}} + {{#hasauthinstructions}} + {{#cansignup}} + + {{/cansignup}} + {{/hasauthinstructions}} + {{#hasidentityproviders}} +