Merge pull request #1331 from ogayot/ubuntu-pro-visual-no-ubuntu-one

ubuntu-pro: update screens to match current design mockups
This commit is contained in:
Olivier Gayot 2022-07-05 16:12:15 +02:00 committed by GitHub
commit 0fa8c54296
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 599 additions and 82 deletions

View File

@ -18,6 +18,8 @@ import asyncio
import logging
from typing import Callable, List, Optional
from urwid import Widget
from subiquitycore.async_helpers import schedule_task
from subiquity.client.controller import SubiquityTuiController
@ -26,7 +28,12 @@ from subiquity.common.types import (
UbuntuProCheckTokenStatus as TokenStatus,
UbuntuProService,
)
from subiquity.ui.views.ubuntu_pro import UbuntuProView
from subiquity.ui.views.ubuntu_pro import (
UbuntuProView,
UpgradeYesNoForm,
UpgradeModeForm,
TokenAddedWidget,
)
from subiquitycore.lsb_release import lsb_release
from subiquitycore.tuicontroller import Skip
@ -58,9 +65,66 @@ class UbuntuProController(SubiquityTuiController):
ubuntu_pro_info = await self.endpoint.GET()
return UbuntuProView(self, ubuntu_pro_info.token)
def run_answers(self) -> None:
if "token" in self.answers:
self.done(self.answers["token"])
async def run_answers(self) -> None:
""" Interact with the UI to go through the pre-attach process if
requested. """
if "token" not in self.answers:
return
from subiquitycore.testing.view_helpers import (
click,
enter_data,
find_button_matching,
find_with_pred,
keypress,
)
view = self.app.ui.body
def run_yes_no_screen(skip: bool) -> None:
if skip:
radio = view.upgrade_yes_no_form.skip.widget
else:
radio = view.upgrade_yes_no_form.upgrade.widget
keypress(radio, key="enter")
click(find_button_matching(view, UpgradeYesNoForm.ok_label))
def run_token_screen(token: str) -> None:
keypress(view.upgrade_mode_form.with_contract_token.widget,
key="enter")
data = {"with_contract_token_subform": {"token": token}}
# TODO: add this point, it would be good to trigger the validation
# code for the token field.
enter_data(view.upgrade_mode_form, data)
click(find_button_matching(view, UpgradeModeForm.ok_label))
async def run_token_added_overlay() -> None:
def is_token_added_overlay(widget: Widget) -> bool:
try:
if widget._text == f" {TokenAddedWidget.title} ":
return True
except AttributeError:
return False
# Wait until the "Token added successfully" overlay is shown.
while not find_with_pred(view, is_token_added_overlay):
await asyncio.sleep(.2)
click(find_button_matching(view, TokenAddedWidget.done_label))
def run_services_screen() -> None:
click(find_button_matching(view._w,
UbuntuProView.services_done_label))
if not self.answers["token"]:
run_yes_no_screen(skip=True)
return
run_yes_no_screen(skip=False)
run_token_screen(self.answers["token"])
await run_token_added_overlay()
run_services_screen()
def check_token(self, token: str,
on_success: Callable[[List[UbuntuProService]], None],
@ -70,6 +134,7 @@ class UbuntuProController(SubiquityTuiController):
async def inner() -> None:
answer = await self.endpoint.check_token.GET(token)
if answer.status == TokenStatus.VALID_TOKEN:
await self.endpoint.POST(UbuntuProInfo(token=token))
on_success(answer.subscription.services)
else:
on_failure(answer.status)
@ -85,6 +150,12 @@ class UbuntuProController(SubiquityTuiController):
self.app.prev_screen()
def done(self, token: str) -> None:
""" Submit the token and move on to the next screen. """
self.app.next_screen(
self.endpoint.POST(UbuntuProInfo(token=token))
)
def next_screen(self) -> None:
""" Move on to the next screen. Assume the token should not be
submitted (or has already been submitted). """
self.app.next_screen()

View File

@ -79,12 +79,16 @@ class MockedUAInterfaceStrategy(UAInterfaceStrategy):
UA token. No actual query is done to the UA servers in this
implementation. Instead, we create a response based on the following
rules:
* Empty tokens are considered invalid.
* Tokens starting with "x" will be considered expired.
* Tokens starting with "i" will be considered invalid.
* Tokens starting with "f" will generate an internal error.
"""
await asyncio.sleep(1 / self.scale_factor)
if not token:
raise InvalidTokenError(token)
if token[0] == "x":
path = "examples/uaclient-status-expired.json"
elif token[0] == "i":
@ -118,6 +122,11 @@ class UAClientUAInterfaceStrategy(UAInterfaceStrategy):
UA token. The information will be queried using the UA client
executable passed to the initializer.
"""
if not token:
# u-a-c does not produce the expected output when the contract
# token is empty ; so let's not call it at all.
raise InvalidTokenError(token)
command = tuple(self.executable) + (
"status",
"--format", "json",

View File

@ -16,9 +16,10 @@
import logging
import re
from typing import List
from typing import Callable, List
from urwid import (
Columns,
connect_signal,
LineBox,
Text,
@ -34,15 +35,21 @@ from subiquitycore.ui.buttons import (
back_btn,
cancel_btn,
done_btn,
menu_btn,
ok_btn,
)
from subiquitycore.ui.container import (
ListBox,
Pile,
WidgetWrap,
)
from subiquitycore.ui.form import (
Form,
SubForm,
SubFormField,
NO_HELP,
simple_field,
RadioButtonField,
WantsToKnowFormField,
)
from subiquitycore.ui.spinner import (
@ -53,7 +60,7 @@ from subiquitycore.ui.stretchy import (
)
from subiquitycore.ui.utils import (
button_pile,
SomethingFailed,
screen,
)
from subiquitycore.ui.interactive import StringEditor
@ -61,18 +68,13 @@ from subiquitycore.ui.interactive import StringEditor
log = logging.getLogger('subiquity.ui.views.ubuntu_pro')
ua_help = _("If you want to enroll this system using your Ubuntu Pro "
"subscription, enter your Ubuntu Pro token here. "
"Otherwise, leave this blank.")
class UATokenEditor(StringEditor, WantsToKnowFormField):
class ContractTokenEditor(StringEditor, WantsToKnowFormField):
""" Represent a text-box editor for the Ubuntu Pro Token. """
def __init__(self):
""" Initialize the text-field editor for UA token. """
self.valid_char_pat = r"[a-zA-Z0-9]"
self.error_invalid_char = _("The only characters permitted in this "
"field are alphanumeric characters.")
self.valid_char_pat = r"[1-9A-HJ-NP-Za-km-z]"
self.error_invalid_char = _("'{}' is not a valid character.")
super().__init__()
def valid_char(self, ch: str) -> bool:
@ -81,23 +83,95 @@ class UATokenEditor(StringEditor, WantsToKnowFormField):
"""
if len(ch) == 1 and not re.match(self.valid_char_pat, ch):
self.bff.in_error = True
self.bff.show_extra(("info_error", self.error_invalid_char))
self.bff.show_extra(
("info_error", self.error_invalid_char.format(ch)))
return False
return super().valid_char(ch)
class UbuntuProForm(Form):
class ContractTokenForm(SubForm):
""" Represents a sub-form requesting Ubuntu Pro token.
+---------------------------------------------------------+
| Token: C123456789ABCDEF |
| This is your Ubuntu Pro token |
+---------------------------------------------------------+
"""
Represents a form requesting Ubuntu Pro information
ContractTokenField = simple_field(ContractTokenEditor)
token = ContractTokenField(
_("Token:"),
help=_("This is your Ubuntu Pro token"))
def validate_token(self):
""" Return an error if the token input does not match the expected
format. """
if not 24 <= len(self.token.value) <= 30:
return _("Invalid token: should be between 24 and 30 characters")
class UpgradeModeForm(Form):
""" Represents a form requesting the Ubuntu Pro credentials.
+---------------------------------------------------------+
| (X) Add token manually |
| Token: C123456789ABCDEF |
| This is your Ubuntu Pro token |
| |
| [ Continue ] |
| [ Back ] |
+---------------------------------------------------------+
"""
cancel_label = _("Back")
ok_label = _("Continue")
group: List[RadioButtonField] = []
UATokenField = simple_field(UATokenEditor)
with_contract_token = RadioButtonField(
group, _("Add token manually"),
help=NO_HELP)
with_contract_token_subform = SubFormField(
ContractTokenForm, "", help=NO_HELP)
token = UATokenField(_("Ubuntu Pro token:"), help=ua_help)
def __init__(self, initial) -> None:
""" Initializer that configures the callback to run when the radio is
checked/unchecked. Since there is a single radio for now, it can
only be unchecked programmatically. """
super().__init__(initial)
connect_signal(self.with_contract_token.widget,
'change', self._toggle_contract_token_input)
def _toggle_contract_token_input(self, sender, new_value):
""" Enable/disable the sub-form that requests the contract token. """
self.with_contract_token_subform.enabled = new_value
class CheckingUAToken(WidgetWrap):
class UpgradeYesNoForm(Form):
""" Represents a form asking if we want to upgrade to Ubuntu Pro.
+---------------------------------------------------------+
| (X) Upgrade to Ubuntu Pro |
| |
| ( ) Do this later |
| |
| You can always enable Ubuntu Pro later via the |
| 'pro attach' command. |
| |
| [ Continue ] |
| [ Back ] |
+---------------------------------------------------------+
"""
cancel_label = _("Back")
ok_label = _("Continue")
group: List[RadioButtonField] = []
upgrade = RadioButtonField(
group, _("Upgrade to Ubuntu Pro"),
help=NO_HELP)
skip = RadioButtonField(
group, _("Do this later"),
help="\n" + _("You can always enable Ubuntu Pro later via the"
" 'pro attach' command."))
class CheckingContractToken(WidgetWrap):
""" Widget displaying a loading animation while checking ubuntu pro
subscription. """
def __init__(self, parent: BaseView):
@ -125,76 +199,261 @@ class CheckingUAToken(WidgetWrap):
class UbuntuProView(BaseView):
""" Represent the view of the Ubuntu Pro configuration. """
title = _("Enable Ubuntu Pro")
excerpt = _("Enter your Ubuntu Pro token if you want to enroll "
"this system.")
title = _("Upgrade to Ubuntu Pro")
services_done_label = _("Continue")
def __init__(self, controller, token: str):
""" Initialize the view with the default value for the token. """
self.controller = controller
self.form = UbuntuProForm(initial={"token": token})
self.upgrade_yes_no_form = UpgradeYesNoForm(initial={
"skip": not token,
"upgrade": bool(token),
})
self.upgrade_mode_form = UpgradeModeForm(initial={
"with_contract_token_subform": {"token": token},
})
def on_cancel(_: UbuntuProForm):
def on_upgrade_yes_no_cancel(unused: UpgradeYesNoForm):
""" Function to call when hitting Done from the upgrade/skip
screen. """
self.cancel()
connect_signal(self.form, 'submit', self.done)
connect_signal(self.form, 'cancel', on_cancel)
def on_upgrade_mode_cancel(unused: UpgradeModeForm):
""" Function to call when hitting Back from the contract token
form. """
self._w = self.upgrade_yes_no_screen()
super().__init__(self.form.as_screen(excerpt=_(self.excerpt)))
connect_signal(self.upgrade_yes_no_form,
'submit', self.upgrade_yes_no_done)
connect_signal(self.upgrade_yes_no_form,
'cancel', on_upgrade_yes_no_cancel)
connect_signal(self.upgrade_mode_form,
'submit', self.upgrade_mode_done)
connect_signal(self.upgrade_mode_form,
'cancel', on_upgrade_mode_cancel)
def done(self, form: UbuntuProForm) -> None:
""" If no token was supplied, move on to the next screen.
If a token was provided, open the loading dialog and
asynchronously check if the token is valid. """
token: str = form.token.value
if token:
def on_success(services: List[UbuntuProService]) -> None:
super().__init__(self.upgrade_yes_no_screen())
def upgrade_mode_screen(self) -> Widget:
""" Return a screen that asks the user for his information (e.g.,
contract token).
+---------------------------------------------------------+
| To upgrade to Ubuntu Pro, you can enter your token |
| manually. |
| |
| [ How to Register -> ] |
| |
| (X) Add token manually |
| Token: C123456789ABCDEF |
| This is your Ubuntu Pro token |
| |
| [ Continue ] |
| [ Back ] |
+---------------------------------------------------------+
"""
excerpt = _("To upgrade to Ubuntu Pro, you can enter your token"
" manually.")
how_to_register_btn = menu_btn(
_("How to Register"),
on_press=lambda unused: self.show_how_to_register()
)
bp = button_pile([how_to_register_btn])
bp.align = "left"
rows = [
bp,
Text(""),
] + self.upgrade_mode_form.as_rows()
return screen(
ListBox(rows),
self.upgrade_mode_form.buttons,
excerpt=excerpt,
focus_buttons=True)
def upgrade_yes_no_screen(self) -> Widget:
""" Return a screen that asks the user to skip or upgrade.
+---------------------------------------------------------+
| Upgrade this machine to Ubuntu Pro or skip this step. |
| |
| [ About Ubuntu Pro -> ] |
| |
| ( ) Upgrade to Ubuntu Pro |
| |
| (X) Do this later |
| You can always enable Ubuntu Pro later via the |
| 'pro attach' command. |
| |
| [ Continue ] |
| [ Back ] |
+---------------------------------------------------------+
"""
excerpt = _("Upgrade this machine to Ubuntu Pro or skip this step.")
about_pro_btn = menu_btn(
_("About Ubuntu Pro"),
on_press=lambda unused: self.show_about_ubuntu_pro())
bp = button_pile([about_pro_btn])
bp.align = "left"
rows = [
bp,
Text(""),
] + self.upgrade_yes_no_form.as_rows()
return screen(
ListBox(rows),
self.upgrade_yes_no_form.buttons,
excerpt=excerpt,
focus_buttons=True)
def services_screen(self, services) -> Widget:
"""
+---------------------------------------------------------+
| List of your enabled services: |
| |
| * ... |
| * ... |
| |
| Other available services: |
| |
| * ... |
| * ... |
| |
| If you want to change the default enablements for your |
| token, you can do so via the ubuntu.com/pro web |
| interface. Alternatively, you can change enabled |
| services using the `pro' command-line tool once the |
| installation is finished. |
| |
| [ Continue ] |
| [ Back ] |
+---------------------------------------------------------+
"""
auto_enabled = [svc for svc in services if svc.auto_enabled]
can_be_enabled = [svc for svc in services if not svc.auto_enabled]
svc_rows: List[Widget] = []
if auto_enabled:
svc_rows.append(Text(_("List of your enabled services:")))
svc_rows.append(Text(""))
svc_rows.extend(
[Text(f" * {svc.description}") for svc in auto_enabled])
if can_be_enabled:
if auto_enabled:
# available here means activable
svc_rows.append(Text(""))
svc_rows.append(Text(_("Other available services:")))
else:
svc_rows.append(Text(_("Available services:")))
svc_rows.append(Text(""))
svc_rows.extend(
[Text(f" * {svc.description}") for svc in can_be_enabled])
def on_continue() -> None:
self.controller.next_screen()
def on_back() -> None:
self._w = self.upgrade_yes_no_screen()
back_button = back_btn(
label=_("Back"),
on_press=lambda unused: on_back())
continue_button = done_btn(
label=self.__class__.services_done_label,
on_press=lambda unused: on_continue())
widgets: List[Widget] = [
Text(""),
Pile(svc_rows),
Text(""),
Text(_("If you want to change the default enablements for your"
" token, you can do so via the ubuntu.com/pro web"
" interface. Alternatively you can change enabled services"
" using the `pro` command-line tool once the installation"
" is finished.")),
Text(""),
]
return screen(
ListBox(widgets),
buttons=[continue_button, back_button],
excerpt=None,
focus_buttons=True)
def upgrade_mode_done(self, form: UpgradeModeForm) -> None:
""" Open the loading dialog and asynchronously check if the token is
valid. """
def on_success(services: List[UbuntuProService]) -> None:
def show_services() -> None:
self.remove_overlay()
self.show_activable_services(services)
def on_failure(status: UbuntuProCheckTokenStatus) -> None:
self.remove_overlay()
if status == UbuntuProCheckTokenStatus.INVALID_TOKEN:
self.show_invalid_token()
elif status == UbuntuProCheckTokenStatus.EXPIRED_TOKEN:
self.show_expired_token()
elif status == UbuntuProCheckTokenStatus.UNKNOWN_ERROR:
self.show_unknown_error()
self.remove_overlay()
widget = TokenAddedWidget(
parent=self,
on_continue=show_services)
self.show_stretchy_overlay(widget)
checking_token_overlay = CheckingUAToken(self)
self.show_overlay(checking_token_overlay,
width=checking_token_overlay.width,
min_width=None)
def on_failure(status: UbuntuProCheckTokenStatus) -> None:
self.remove_overlay()
token_field = form.with_contract_token_subform.widget.form.token
if status == UbuntuProCheckTokenStatus.INVALID_TOKEN:
self.show_invalid_token()
token_field.in_error = True
token_field.show_extra(("info_error", "Invalid token"))
form.validated()
elif status == UbuntuProCheckTokenStatus.EXPIRED_TOKEN:
self.show_expired_token()
token_field.in_error = True
token_field.show_extra(("info_error", "Expired token"))
form.validated()
elif status == UbuntuProCheckTokenStatus.UNKNOWN_ERROR:
self.show_unknown_error()
self.controller.check_token(token,
on_success=on_success,
on_failure=on_failure)
token: str = form.with_contract_token_subform.value["token"]
checking_token_overlay = CheckingContractToken(self)
self.show_overlay(checking_token_overlay,
width=checking_token_overlay.width,
min_width=None)
self.controller.check_token(token,
on_success=on_success,
on_failure=on_failure)
def upgrade_yes_no_done(self, form: UpgradeYesNoForm) -> None:
""" If skip is selected, move on to the next screen.
Otherwise, show the form requesting the contract token. """
if form.skip.value:
self.controller.done("")
else:
self.controller.done(token)
self._w = self.upgrade_mode_screen()
def cancel(self) -> None:
""" Called when the user presses the Back button. """
self.controller.cancel()
def show_about_ubuntu_pro(self) -> None:
""" Display an overlay that shows information about Ubuntu Pro. """
self.show_stretchy_overlay(AboutProWidget(self))
def show_how_to_register(self) -> None:
""" Display an overlay that shows instructions to register to
Ubuntu Pro. """
self.show_stretchy_overlay(HowToRegisterWidget(self))
def show_invalid_token(self) -> None:
""" Display an overlay that indicates that the user-supplied token is
invalid. """
self.show_stretchy_overlay(
SomethingFailed(self,
"Invalid token.",
"The Ubuntu Pro token that you provided"
" is invalid. Please make sure that you typed"
" your token correctly."))
self.show_stretchy_overlay(InvalidTokenWidget(self))
def show_expired_token(self) -> None:
""" Display an overlay that indicates that the user-supplied token has
expired. """
self.show_stretchy_overlay(
SomethingFailed(self,
"Token expired.",
"The Ubuntu Pro token that you provided"
" has expired. Please use a different token."))
self.show_stretchy_overlay(ExpiredTokenWidget(self))
def show_unknown_error(self) -> None:
""" Display an overlay that indicates that we were unable to retrieve
@ -206,47 +465,224 @@ class UbuntuProView(BaseView):
def show_activable_services(self,
services: List[UbuntuProService]) -> None:
""" Display an overlay with the list of services that can be enabled
""" Display a screen with the list of services that can be enabled
via Ubuntu Pro subscription. After the user confirms, we will
quit the current view and move on. """
self.show_stretchy_overlay(ShowServicesWidget(self, services))
self._w = self.services_screen(services)
class ShowServicesWidget(Stretchy):
""" Widget to show the activable services for UA subscription. """
class ExpiredTokenWidget(Stretchy):
""" Widget that shows that the supplied token is expired.
+--------------------- Expired token ---------------------+
| |
| Your token has expired. Please use another token to |
| continue. |
| |
| [ Okay ] |
+---------------------------------------------------------+
"""
def __init__(self, parent: BaseView) -> None:
""" Initializes the widget. """
self.parent = parent
cont = done_btn(label=_("Okay"), on_press=lambda unused: self.close())
widgets = [
Text(_("Your token has expired. Please use another token"
" to continue.")),
Text(""),
button_pile([cont]),
]
super().__init__("Expired token", widgets,
stretchy_index=0, focus_index=2)
def close(self) -> None:
""" Close the overlay. """
self.parent.remove_overlay()
class InvalidTokenWidget(Stretchy):
""" Widget that shows that the supplied token is invalid.
+--------------------- Invalid token ---------------------+
| |
| Your token could not be verified. Please ensure it is |
| correct and try again. |
| |
| [ Okay ] |
+---------------------------------------------------------+
"""
def __init__(self, parent: BaseView) -> None:
""" Initializes the widget. """
self.parent = parent
cont = done_btn(label=_("Okay"), on_press=lambda unused: self.close())
widgets = [
Text(_("Your token could not be verified. Please ensure it is"
" correct and try again.")),
Text(""),
button_pile([cont]),
]
super().__init__("Invalid token", widgets,
stretchy_index=0, focus_index=2)
def close(self) -> None:
""" Close the overlay. """
self.parent.remove_overlay()
class TokenAddedWidget(Stretchy):
""" Widget that shows that the supplied token is valid and was "added".
+---------------- Token added successfully ---------------+
| |
| Your token has been added successfully and your |
| subscription configuration will be applied at the first |
| boot. |
| |
| [ Continue ] |
+---------------------------------------------------------+
"""
title = _("Token added successfully")
done_label = _("Continue")
def __init__(self, parent: UbuntuProView,
services: List[UbuntuProService]) -> None:
""" Initializes the widget by including the list of services as a
bullet-point list. """
on_continue: Callable[[], None]) -> None:
""" Initializes the widget. """
self.parent = parent
cont = done_btn(
label=self.__class__.done_label,
on_press=lambda unused: on_continue())
widgets = [
Text(_("Your token has been added successfully and your"
" subscription configuration will be applied at the first"
" boot.")),
Text(""),
button_pile([cont]),
]
super().__init__(self.__class__.title, widgets,
stretchy_index=0, focus_index=2)
class AboutProWidget(Stretchy):
""" Widget showing some information about what Ubuntu Pro offers.
+------------------- About Ubuntu Pro --------------------+
| |
| Ubuntu Pro subscription gives you access to multiple |
| security & compliance services, including: |
| |
| Security patching for over 30.000 packages, with a |
| focus on High and Critical CVEs (extended from 2.500) |
| ... |
| ... |
| |
| Ubuntu Pro is free for personal use on up to 3 machines.|
| More information on ubuntu.com/pro |
| |
| [ Continue ] |
+---------------------------------------------------------+
"""
def __init__(self, parent: UbuntuProView) -> None:
""" Initializes the widget."""
self.parent = parent
ok = ok_btn(label=_("OK"), on_press=self.ok)
ok = ok_btn(label=_("Continue"), on_press=lambda unused: self.close())
title = _("Activable Services")
header = _("List of services that are activable through your "
"Ubuntu Pro subscription:")
title = _("About Ubuntu Pro")
header = _("Ubuntu Pro subscription gives you access to multiple"
" security & compliance services, including:")
services = [
_("Security patching for over 30.000 packages, with a focus on"
" High and Critical CVEs (extended from 2.500)"),
_("10 years of security Maintenance (extended from 5 years)"),
_("Kernel Livepatch service for increased uptime and security"),
_("Ubuntu Security Guide for hardening profiles, including CIS"
" and DISA-STIG"),
_("FIPS 140-2 NIST-certified crypto-modules for FedRAMP"
" compliance"),
]
def itemize(item: str, marker: str = "") -> Columns:
""" Return the text specified in a Text widget prepended with a
bullet point / marker. If the text is too long to fit in a single
line, the continuation lines are indented as shown below:
+---------------------------+
| * This is an example of |
| what such element would |
| look like. |
+---------------------------+
"""
return Columns(
[(len(marker), Text(marker)), Text(item)], dividechars=1)
widgets: List[Widget] = [
Text(header),
Text(""),
Pile([Text(f"* {svc.description}") for svc in services]),
Pile([itemize(svc) for svc in services]),
Text(""),
Text("Once the installation has finished, you can enable these "
"services using the `ua` command-line tool."),
Text(_("Ubuntu Pro is free for personal use on up to 3"
" machines.")),
Text(_("More information on ubuntu.com/pro")),
Text(""),
button_pile([ok]),
]
super().__init__(title, widgets, 2, 6)
super().__init__(title, widgets, stretchy_index=2, focus_index=7)
def ok(self, sender) -> None:
""" Close the overlay and submit the token. """
self.parent.controller.done(self.parent.form.token.value)
def close(self) -> None:
""" Close the overlay. """
self.parent.remove_overlay()
class HowToRegisterWidget(Stretchy):
""" Widget showing some instructions to register to Ubuntu Pro.
+-------------------- How to register --------------------+
| |
| You can register for a free Ubuntu One account and get |
| a personal token for up to 3 machines. |
| |
| To register an account, visit ubuntu.com/pro on another |
| device. |
| |
| [ Continue ] |
+---------------------------------------------------------+
"""
def __init__(self, parent: UbuntuProView) -> None:
""" Initializes the widget."""
self.parent = parent
ok = ok_btn(label=_("Continue"), on_press=lambda unused: self.close())
title = _("How to register")
header = _("You can register for a free Ubuntu One account and get a"
" personal token for up to 3 machines.")
widgets: List[Widget] = [
Text(header),
Text(""),
Text("To register an account, visit ubuntu.com/pro on another"
" device."),
Text(""),
button_pile([ok]),
]
super().__init__(title, widgets, stretchy_index=2, focus_index=4)
def close(self) -> None:
""" Close the overlay. """
self.parent.remove_overlay()
class ContinueAnywayWidget(Stretchy):
""" Widget that requests the user if he wants to go back or continue
anyway. """
anyway.
+--------------------- Unknown error ---------------------+
| |
| Unable to check your subscription information. Do you |
| want to go back or continue anyway? |
| |
| [ Back ] |
| [ Continue anyway ] |
+---------------------------------------------------------+
"""
def __init__(self, parent: UbuntuProView) -> None:
""" Initializes the widget by showing two buttons, one to go back and
one to move forward anyway. """
@ -267,4 +703,5 @@ class ContinueAnywayWidget(Stretchy):
def cont(self, sender) -> None:
""" Move on to the next screen. """
self.parent.controller.done(self.parent.form.token.value)
subform = self.parent.upgrade_mode_form.with_contract_token_subform
self.parent.controller.done(subform.value["token"])