mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 03:06:41 +00:00
Add rel=noopener (#5046)
This commit is contained in:
parent
196540afc7
commit
d3105b6846
@ -57,7 +57,9 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
</h1>
|
||||
<p class="description">
|
||||
Maintained by ${repo.maintainer}<br />
|
||||
<a class="repo" href=${repo.url} target="_blank">${repo.url}</a>
|
||||
<a class="repo" href=${repo.url} target="_blank" rel="noreferrer">
|
||||
${repo.url}
|
||||
</a>
|
||||
</p>
|
||||
<div class="card-group">
|
||||
${addons.map(
|
||||
|
@ -185,14 +185,19 @@ class HassioAddonInfo extends LitElement {
|
||||
<div class="description light-color">
|
||||
${this.addon.description}.<br />
|
||||
Visit
|
||||
<a href="${this.addon.url}" target="_blank">
|
||||
<a href="${this.addon.url}" target="_blank" rel="noreferrer">
|
||||
${this.addon.name} page</a
|
||||
>
|
||||
for details.
|
||||
</div>
|
||||
${this.addon.logo
|
||||
? html`
|
||||
<a href="${this.addon.url}" target="_blank" class="logo">
|
||||
<a
|
||||
href="${this.addon.url}"
|
||||
target="_blank"
|
||||
class="logo"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img src="/api/hassio/addons/${this.addon.slug}/logo" />
|
||||
</a>
|
||||
`
|
||||
@ -428,6 +433,7 @@ class HassioAddonInfo extends LitElement {
|
||||
tabindex="-1"
|
||||
target="_blank"
|
||||
class="right"
|
||||
rel="noopener"
|
||||
>
|
||||
<mwc-button>
|
||||
Open web UI
|
||||
|
@ -123,7 +123,7 @@ export class HassioUpdate extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="${releaseNotesUrl}" target="_blank">
|
||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
||||
<mwc-button>Release notes</mwc-button>
|
||||
</a>
|
||||
<ha-call-api-button
|
||||
|
@ -53,6 +53,7 @@ class HaMarkdown extends UpdatingElement {
|
||||
node.host !== document.location.host
|
||||
) {
|
||||
node.target = "_blank";
|
||||
node.rel = "noreferrer";
|
||||
|
||||
// protect referrer on external links and deny window.opener access for security reasons
|
||||
// (see https://mathiasbynens.github.io/rel-noopener/)
|
||||
|
@ -38,7 +38,7 @@ class StepFlowExternal extends LitElement {
|
||||
<div class="content">
|
||||
${this.flowConfig.renderExternalStepDescription(this.hass, this.step)}
|
||||
<div class="open-button">
|
||||
<a href=${this.step.url} target="_blank">
|
||||
<a href=${this.step.url} target="_blank" rel="noreferrer">
|
||||
<mwc-button raised>
|
||||
${localize(
|
||||
"ui.panel.config.integrations.config_flow.external_step.open_site"
|
||||
|
@ -97,6 +97,7 @@ class StepFlowPickHandler extends LitElement {
|
||||
)}<a
|
||||
href="https://www.home-assistant.io/integrations/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.home_assistant_website"
|
||||
)}</a
|
||||
|
@ -113,6 +113,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
class="button"
|
||||
href="${this._agentInfo.onboarding.url}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
><mwc-button unelevated>Yes!</mwc-button></a
|
||||
>
|
||||
<mwc-button outlined>No</mwc-button>
|
||||
@ -185,6 +186,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
||||
href=${this._agentInfo.attribution.url}
|
||||
class="attribution"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this._agentInfo.attribution.name}</a
|
||||
>
|
||||
`
|
||||
|
@ -156,6 +156,7 @@ export class HaAutomationEditor extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/automation/trigger/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.learn_more"
|
||||
@ -184,6 +185,7 @@ export class HaAutomationEditor extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/scripts/conditions/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.learn_more"
|
||||
@ -212,6 +214,7 @@ export class HaAutomationEditor extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/automation/action/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.learn_more"
|
||||
|
@ -63,6 +63,7 @@ class HaAutomationPicker extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/automation/editor/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.picker.learn_more"
|
||||
|
@ -123,6 +123,7 @@ class DialogThingtalk extends LitElement {
|
||||
<a
|
||||
href="https://almond.stanford.edu/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="attribution"
|
||||
>Powered by Almond</a
|
||||
>
|
||||
|
@ -95,11 +95,15 @@ class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
</div>
|
||||
|
||||
<div class="card-actions">
|
||||
<a href="https://account.nabucasa.com" target="_blank"
|
||||
><mwc-button
|
||||
>[[localize('ui.panel.config.cloud.account.manage_account')]]</mwc-button
|
||||
></a
|
||||
<a
|
||||
href="https://account.nabucasa.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<mwc-button
|
||||
>[[localize('ui.panel.config.cloud.account.manage_account')]]</mwc-button
|
||||
>
|
||||
</a>
|
||||
<mwc-button style="float: right" on-click="handleLogout"
|
||||
>[[localize('ui.panel.config.cloud.account.sign_out')]]</mwc-button
|
||||
>
|
||||
@ -117,8 +121,12 @@ class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
</p>
|
||||
<p>
|
||||
[[localize('ui.panel.config.cloud.account.integrations_introduction2')]]
|
||||
<a href="https://www.nabucasa.com" target="_blank"
|
||||
>[[localize('ui.panel.config.cloud.account.integrations_link_all_features')]]</a
|
||||
<a
|
||||
href="https://www.nabucasa.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
[[localize('ui.panel.config.cloud.account.integrations_link_all_features')]] </a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -49,6 +49,7 @@ export class CloudAlexaPref extends LitElement {
|
||||
<a
|
||||
href="https://skills-store.amazon.com/deeplink/dp/B0772J1QKB?deviceType=app"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.enable_ha_skill"
|
||||
@ -59,6 +60,7 @@ export class CloudAlexaPref extends LitElement {
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/amazon_alexa/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.alexa.config_documentation"
|
||||
|
@ -55,6 +55,7 @@ export class CloudGooglePref extends LitElement {
|
||||
<a
|
||||
href="https://assistant.google.com/services/a/uid/00000091fd5fb875?hl=en-US"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.google.enable_ha_skill"
|
||||
@ -65,6 +66,7 @@ export class CloudGooglePref extends LitElement {
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/google_assistant/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.google.config_documentation"
|
||||
|
@ -77,12 +77,21 @@ export class CloudRemotePref extends LitElement {
|
||||
: this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.remote.instance_will_be_available"
|
||||
)}
|
||||
<a href="https://${remote_domain}" target="_blank" class="break-word">
|
||||
<a
|
||||
href="https://${remote_domain}"
|
||||
target="_blank"
|
||||
class="break-word"
|
||||
rel="noreferrer"
|
||||
>
|
||||
https://${remote_domain}</a
|
||||
>.
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="https://www.nabucasa.com/config/remote/" target="_blank">
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/remote/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<mwc-button
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.remote.link_learn_how_it_works"
|
||||
|
@ -46,7 +46,11 @@ export class CloudWebhooks extends LitElement {
|
||||
${this._renderBody()}
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://www.nabucasa.com/config/webhooks" target="_blank">
|
||||
<a
|
||||
href="https://www.nabucasa.com/config/webhooks"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.cloud.account.webhooks.link_learn_more"
|
||||
)}
|
||||
|
@ -78,7 +78,7 @@ export class DialogManageCloudhook extends LitElement {
|
||||
</div>
|
||||
|
||||
<div class="paper-dialog-buttons">
|
||||
<a href="${docsUrl}" target="_blank">
|
||||
<a href="${docsUrl}" target="_blank" rel="noreferrer">
|
||||
<mwc-button
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.cloud.dialog_cloudhook.view_documentation"
|
||||
|
@ -84,18 +84,26 @@ class CloudLogin extends LocalizeMixin(
|
||||
</p>
|
||||
<p>
|
||||
[[localize('ui.panel.config.cloud.login.introduction2')]]
|
||||
<a href="https://www.nabucasa.com" target="_blank"
|
||||
>Nabu Casa, Inc</a
|
||||
<a
|
||||
href="https://www.nabucasa.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Nabu Casa, Inc
|
||||
</a>
|
||||
[[localize('ui.panel.config.cloud.login.introduction2a')]]
|
||||
</p>
|
||||
<p>
|
||||
[[localize('ui.panel.config.cloud.login.introduction3')]]
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://www.nabucasa.com" target="_blank"
|
||||
>[[localize('ui.panel.config.cloud.login.learn_more_link')]]</a
|
||||
<a
|
||||
href="https://www.nabucasa.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
[[localize('ui.panel.config.cloud.login.learn_more_link')]]
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -74,8 +74,8 @@ class CloudRegister extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
<p>
|
||||
[[localize('ui.panel.config.cloud.register.information4')]]
|
||||
</p><ul>
|
||||
<li><a href="https://home-assistant.io/tos/" target="_blank">[[localize('ui.panel.config.cloud.register.link_terms_conditions')]]</a></li>
|
||||
<li><a href="https://home-assistant.io/privacy/" target="_blank">[[localize('ui.panel.config.cloud.register.link_privacy_policy')]]</a></li>
|
||||
<li><a href="https://home-assistant.io/tos/" target="_blank" rel="noreferrer">[[localize('ui.panel.config.cloud.register.link_terms_conditions')]]</a></li>
|
||||
<li><a href="https://home-assistant.io/privacy/" target="_blank" rel="noreferrer">[[localize('ui.panel.config.cloud.register.link_privacy_policy')]]</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -31,6 +31,7 @@ class HaFormCustomize extends LocalizeMixin(PolymerElement) {
|
||||
<a
|
||||
href="https://www.home-assistant.io/docs/configuration/customizing-devices/#customization-using-the-ui"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.config.customize.warning.include_link')]]</a
|
||||
>.<br />
|
||||
[[localize('ui.panel.config.customize.warning.not_applied')]]
|
||||
|
@ -128,6 +128,7 @@ class DialogPersonDetail extends LitElement {
|
||||
<a
|
||||
href="https://www.home-assistant.io/integrations/#presence-detection"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.person.detail.link_presence_detection_integrations"
|
||||
)}</a
|
||||
|
@ -54,6 +54,7 @@ class HaSceneDashboard extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/scene/editor/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.scene.picker.learn_more")}
|
||||
</a>
|
||||
|
@ -122,6 +122,7 @@ export class HaScriptEditor extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/scripts/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.link_available_actions"
|
||||
|
@ -54,6 +54,7 @@ class HaScriptPicker extends LitElement {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/scripts/editor/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.picker.learn_more"
|
||||
|
@ -71,6 +71,7 @@ export class ZwaveNetwork extends LitElement {
|
||||
<a
|
||||
href="https://www.home-assistant.io/docs/z-wave/control-panel/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.zwave.learn_more")}
|
||||
</a>
|
||||
|
@ -61,9 +61,11 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
<a
|
||||
href="https://www.home-assistant.io/docs/configuration/events/"
|
||||
target="_blank"
|
||||
>[[localize( 'ui.panel.developer-tools.tabs.events.documentation'
|
||||
)]]</a
|
||||
rel="noreferrer"
|
||||
>
|
||||
[[localize( 'ui.panel.developer-tools.tabs.events.documentation'
|
||||
)]]
|
||||
</a>
|
||||
</p>
|
||||
<div class="ha-form">
|
||||
<paper-input
|
||||
|
@ -27,7 +27,7 @@ class HaPanelDevInfo extends LitElement {
|
||||
return html`
|
||||
<div class="about">
|
||||
<p class="version">
|
||||
<a href="https://www.home-assistant.io" target="_blank"
|
||||
<a href="https://www.home-assistant.io" target="_blank" rel="noreferrer"
|
||||
><img
|
||||
src="/static/icons/favicon-192x192.png"
|
||||
height="192"
|
||||
@ -48,7 +48,7 @@ class HaPanelDevInfo extends LitElement {
|
||||
<p class="develop">
|
||||
<a
|
||||
href="https://www.home-assistant.io/developers/credits/"
|
||||
target="_blank"
|
||||
target="_blank" rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.developed_by"
|
||||
@ -62,7 +62,7 @@ class HaPanelDevInfo extends LitElement {
|
||||
${this.hass.localize("ui.panel.developer-tools.tabs.info.source")}
|
||||
<a
|
||||
href="https://github.com/home-assistant/home-assistant"
|
||||
target="_blank"
|
||||
target="_blank" rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.server"
|
||||
)}</a
|
||||
@ -70,7 +70,7 @@ class HaPanelDevInfo extends LitElement {
|
||||
—
|
||||
<a
|
||||
href="https://github.com/home-assistant/home-assistant-polymer"
|
||||
target="_blank"
|
||||
target="_blank" rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.frontend"
|
||||
)}</a
|
||||
@ -80,14 +80,14 @@ class HaPanelDevInfo extends LitElement {
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.built_using"
|
||||
)}
|
||||
<a href="https://www.python.org">Python 3</a>,
|
||||
<a href="https://www.polymer-project.org" target="_blank">Polymer</a>,
|
||||
<a href="https://www.python.org" target="_blank" rel="noreferrer">Python 3</a>,
|
||||
<a href="https://www.polymer-project.org" target="_blank" rel="noreferrer">Polymer</a>,
|
||||
${this.hass.localize("ui.panel.developer-tools.tabs.info.icons_by")}
|
||||
<a href="https://www.google.com/design/icons/" target="_blank"
|
||||
<a href="https://www.google.com/design/icons/" target="_blank" rel="noreferrer"
|
||||
>Google</a
|
||||
>
|
||||
and
|
||||
<a href="https://MaterialDesignIcons.com" target="_blank"
|
||||
<a href="https://MaterialDesignIcons.com" target="_blank" rel="noreferrer"
|
||||
>MaterialDesignIcons.com</a
|
||||
>.
|
||||
</p>
|
||||
|
@ -32,12 +32,20 @@ class IntegrationsCard extends LitElement {
|
||||
<tr>
|
||||
<td>${domain}</td>
|
||||
<td>
|
||||
<a href=${integrationDocsUrl(domain)} target="_blank">
|
||||
<a
|
||||
href=${integrationDocsUrl(domain)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href=${integrationIssuesUrl(domain)} target="_blank">
|
||||
<a
|
||||
href=${integrationIssuesUrl(domain)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Issues
|
||||
</a>
|
||||
</td>
|
||||
|
@ -60,10 +60,16 @@ class DialogSystemLogDetail extends LitElement {
|
||||
? html`
|
||||
<br />
|
||||
Integration: ${domainToName(this.hass.localize, integration)}
|
||||
(<a href=${integrationDocsUrl(integration)} target="_blank"
|
||||
(<a
|
||||
href=${integrationDocsUrl(integration)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>documentation</a
|
||||
>,
|
||||
<a href=${integrationIssuesUrl(integration)} target="_blank"
|
||||
<a
|
||||
href=${integrationIssuesUrl(integration)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>issues</a
|
||||
>)
|
||||
`
|
||||
|
@ -68,6 +68,7 @@ class HaPanelDevTemplate extends LocalizeMixin(PolymerElement) {
|
||||
<a
|
||||
href="http://jinja.pocoo.org/docs/dev/templates/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.developer-tools.tabs.templates.jinja_documentation')]]</a
|
||||
>
|
||||
</li>
|
||||
@ -75,6 +76,7 @@ class HaPanelDevTemplate extends LocalizeMixin(PolymerElement) {
|
||||
<a
|
||||
href="https://home-assistant.io/docs/configuration/templating/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.developer-tools.tabs.templates.template_extensions')]]</a
|
||||
>
|
||||
</li>
|
||||
|
@ -40,6 +40,7 @@ class HuiWeblinkRow extends LitElement implements LovelaceRow {
|
||||
<a
|
||||
href=${this._config.url}
|
||||
target=${this._config.url.indexOf("://") !== -1 ? "_blank" : ""}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ha-icon .icon="${this._config.icon}"></ha-icon>
|
||||
<div>${this._config.name}</div>
|
||||
|
@ -33,6 +33,7 @@ class AdvancedModeRow extends LitElement {
|
||||
<a
|
||||
href="https://www.home-assistant.io/blog/2019/07/17/release-96/#advanced-mode"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize("ui.panel.profile.advanced_mode.link_promo")}
|
||||
</a>
|
||||
</span>
|
||||
|
@ -39,6 +39,7 @@ class HaLongLivedTokens extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
<a
|
||||
href="https://developers.home-assistant.io/docs/en/auth_api.html#making-authenticated-requests"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
[[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]]
|
||||
</a>
|
||||
|
@ -36,6 +36,7 @@ class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
<a
|
||||
href="https://developers.home-assistant.io/docs/en/internationalization_translation.html"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.profile.language.link_promo')]]</a
|
||||
>
|
||||
</span>
|
||||
|
@ -31,6 +31,7 @@ class HaPickThemeRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
<a
|
||||
href="https://www.home-assistant.io/integrations/frontend/#defining-themes"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.profile.themes.link_promo')]]</a
|
||||
>
|
||||
</span>
|
||||
|
@ -30,6 +30,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) {
|
||||
<a
|
||||
href="https://www.home-assistant.io/integrations/html5"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.profile.push_notifications.link_promo')]]</a
|
||||
>
|
||||
</span>
|
||||
|
Loading…
x
Reference in New Issue
Block a user