diff options
Diffstat (limited to 'webapp')
45 files changed, 1109 insertions, 518 deletions
diff --git a/webapp/components/about_build_modal.jsx b/webapp/components/about_build_modal.jsx index a47225f7e..4fd946401 100644 --- a/webapp/components/about_build_modal.jsx +++ b/webapp/components/about_build_modal.jsx @@ -6,6 +6,7 @@ import {Modal} from 'react-bootstrap'; import {FormattedMessage} from 'react-intl'; import React from 'react'; +import Constants from 'utils/constants.jsx'; export default class AboutBuildModal extends React.Component { constructor(props) { @@ -20,6 +21,7 @@ export default class AboutBuildModal extends React.Component { render() { const config = global.window.mm_config; const license = global.window.mm_license; + const mattermostLogo = Constants.MATTERMOST_ICON_SVG; let title = ( <FormattedMessage @@ -28,6 +30,28 @@ export default class AboutBuildModal extends React.Component { /> ); + let subTitle = ( + <FormattedMessage + id='about.teamEditionSt' + defaultMessage='All your team communication in one place, instantly searchable and accessible anywhere.' + /> + ); + + let learnMore = ( + <div> + <FormattedMessage + id='about.teamEditionLearn' + defaultMessage='Join the Mattermost community at ' + /> + <a + target='_blank' + href='http://www.mattermost.org/' + > + {'mattermost.org'} + </a> + </div> + ); + let licensee; if (config.BuildEnterpriseReady === 'true') { title = ( @@ -36,6 +60,29 @@ export default class AboutBuildModal extends React.Component { defaultMessage='Enterprise Edition' /> ); + + subTitle = ( + <FormattedMessage + id='about.enterpriseEditionSt' + defaultMessage='Modern enterprise communication from behind your firewall.' + /> + ); + + learnMore = ( + <div> + <FormattedMessage + id='about.enterpriseEditionLearn' + defaultMessage='Learn more about Enterprise Edition at ' + /> + <a + target='_blank' + href='http://about.mattermost.com/' + > + {'about.mattermost.com'} + </a> + </div> + ); + if (license.IsLicensed === 'true') { title = ( <FormattedMessage @@ -44,14 +91,12 @@ export default class AboutBuildModal extends React.Component { /> ); licensee = ( - <div className='row form-group'> - <div className='col-sm-3 info__label'> - <FormattedMessage - id='about.licensed' - defaultMessage='Licensed by:' - /> - </div> - <div className='col-sm-9'>{license.Company}</div> + <div className='form-group'> + <FormattedMessage + id='about.licensed' + defaultMessage='Licensed by:' + /> + {license.Company} </div> ); } @@ -59,6 +104,7 @@ export default class AboutBuildModal extends React.Component { return ( <Modal + dialogClassName='about-modal' show={this.props.show} onHide={this.doHide} > @@ -71,57 +117,54 @@ export default class AboutBuildModal extends React.Component { </Modal.Title> </Modal.Header> <Modal.Body> - <h4 className='padding-bottom x2'>{'Mattermost'} {title}</h4> - {licensee} - <div className='row form-group'> - <div className='col-sm-3 info__label'> - <FormattedMessage - id='about.version' - defaultMessage='Version:' + <div className='about-modal__content'> + <div className='about-modal__logo'> + <span + className='icon' + dangerouslySetInnerHTML={{__html: mattermostLogo}} /> </div> - <div className='col-sm-9'>{config.Version}</div> - </div> - <div className='row form-group'> - <div className='col-sm-3 info__label'> - <FormattedMessage - id='about.number' - defaultMessage='Build Number:' - /> + <div> + <h3 className='about-modal__title'>{'Mattermost'} {title}</h3> + <p className='about-modal__subtitle padding-bottom'>{subTitle}</p> + <div className='form-group less'> + <div> + <FormattedMessage + id='about.version' + defaultMessage='Version:' + /> + {config.Version} ({config.BuildNumber}) + </div> + </div> + {licensee} </div> - <div className='col-sm-9'>{config.BuildNumber}</div> </div> - <div className='row form-group'> - <div className='col-sm-3 info__label'> + <div className='about-modal__footer'> + {learnMore} + <div className='form-group about-modal__copyright'> <FormattedMessage - id='about.date' - defaultMessage='Build Date:' + id='about.copyright' + defaultMessage='Copyright 2016 Mattermost, Inc. All rights reserved' /> </div> - <div className='col-sm-9'>{config.BuildDate}</div> </div> - <div className='row form-group'> - <div className='col-sm-3 info__label'> + <div className='about-modal__hash form-group padding-top x2'> + <p> <FormattedMessage id='about.hash' defaultMessage='Build Hash:' /> - </div> - <div className='col-sm-9'>{config.BuildHash}</div> + {config.BuildHash} + </p> + <p> + <FormattedMessage + id='about.date' + defaultMessage='Build Date:' + /> + {config.BuildDate} + </p> </div> </Modal.Body> - <Modal.Footer> - <button - type='button' - className='btn btn-default' - onClick={this.doHide} - > - <FormattedMessage - id='about.close' - defaultMessage='Close' - /> - </button> - </Modal.Footer> </Modal> ); } diff --git a/webapp/components/admin_console/compliance_settings.jsx b/webapp/components/admin_console/compliance_settings.jsx index fb2ae26f9..206bb0faa 100644 --- a/webapp/components/admin_console/compliance_settings.jsx +++ b/webapp/components/admin_console/compliance_settings.jsx @@ -223,7 +223,7 @@ export default class ComplianceSettings extends React.Component { </label> <p className='help-text'> <FormattedMessage - id='admin.compliance.enableDesc' + id='admin.compliance.enableDailyDesc' defaultMessage='When true, Mattermost will generate a daily compliance report.' /> </p> diff --git a/webapp/components/admin_console/service_settings.jsx b/webapp/components/admin_console/service_settings.jsx index c72c97326..2c3f4081c 100644 --- a/webapp/components/admin_console/service_settings.jsx +++ b/webapp/components/admin_console/service_settings.jsx @@ -87,7 +87,7 @@ class ServiceSettings extends React.Component { config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked; config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked; - if (this.refs.EnablMultifactorAuthentication) { + if (this.refs.EnableMultifactorAuthentication) { config.ServiceSettings.EnableMultifactorAuthentication = ReactDOM.findDOMNode(this.refs.EnableMultifactorAuthentication).checked; } diff --git a/webapp/components/admin_console/user_item.jsx b/webapp/components/admin_console/user_item.jsx index 91f567d4d..c00050584 100644 --- a/webapp/components/admin_console/user_item.jsx +++ b/webapp/components/admin_console/user_item.jsx @@ -333,7 +333,7 @@ export default class UserItem extends React.Component { <div> <FormattedMessage id='admin.user_item.confirmDemoteDescription' - defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command." + defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command." /> <br/> <br/> diff --git a/webapp/components/analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics.jsx index efc965f24..9b4eb1f94 100644 --- a/webapp/components/analytics/team_analytics.jsx +++ b/webapp/components/analytics/team_analytics.jsx @@ -154,7 +154,7 @@ class TeamAnalytics extends React.Component { <TableChart title={ <FormattedMessage - id='analytics.team.activeUsers' + id='analytics.team.recentUsers' defaultMessage='Recent Active Users' /> } diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/backstage/add_incoming_webhook.jsx index fa7531fc6..83027c6b3 100644 --- a/webapp/components/backstage/add_incoming_webhook.jsx +++ b/webapp/components/backstage/add_incoming_webhook.jsx @@ -96,10 +96,10 @@ export default class AddIncomingWebhook extends React.Component { render() { return ( - <div className='backstage row'> + <div className='backstage-content row'> <div className='add-incoming-webhook'> - <div className='backstage__header'> - <h1 className='text'> + <div className='backstage-header'> + <h1> <FormattedMessage id='add_incoming_webhook.header' defaultMessage='Add Incoming Webhook' @@ -107,81 +107,91 @@ export default class AddIncomingWebhook extends React.Component { </h1> </div> </div> - <form className='add-incoming-webhook__body'> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='name' - > - <FormattedMessage - id='add_incoming_webhook.name' - defaultMessage='Name' - /> - </label> - <input - id='name' - type='text' - value={this.state.name} - onChange={this.updateName} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='description' - > - <FormattedMessage - id='add_incoming_webhook.description' - defaultMessage='Description' - /> - </label> - <input - id='description' - type='text' - value={this.state.description} - onChange={this.updateDescription} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='channelId' - > - <FormattedMessage - id='add_incoming_webhook.channel' - defaultMessage='Channel' - /> - </label> - <ChannelSelect - id='channelId' - value={this.state.channelId} - onChange={this.updateChannelId} - /> - </div> - <div className='add-integration__submit-row'> - <Link - className='btn btn-sm' - to={'/settings/integrations/add'} - > - <FormattedMessage - id='add_incoming_webhook.cancel' - defaultMessage='Cancel' - /> - </Link> - <SpinnerButton - className='btn btn-primary' - type='submit' - spinning={this.state.saving} - onClick={this.handleSubmit} - > - <FormattedMessage - id='add_incoming_webhook.save' - defaultMessage='Save' - /> - </SpinnerButton> - </div> - <FormError errors={[this.state.serverError, this.state.clientError]}/> - </form> + <div className='backstage-form'> + <form className='form-horizontal'> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='name' + > + <FormattedMessage + id='add_incoming_webhook.name' + defaultMessage='Name' + /> + </label> + <div className='col-md-5 col-sm-9'> + <input + id='name' + type='text' + className='form-control' + value={this.state.name} + onChange={this.updateName} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='description' + > + <FormattedMessage + id='add_incoming_webhook.description' + defaultMessage='Description' + /> + </label> + <div className='col-md-5 col-sm-9'> + <input + id='description' + type='text' + className='form-control' + value={this.state.description} + onChange={this.updateDescription} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='channelId' + > + <FormattedMessage + id='add_incoming_webhook.channel' + defaultMessage='Channel' + /> + </label> + <div className='col-md-5 col-sm-9'> + <ChannelSelect + id='channelId' + value={this.state.channelId} + onChange={this.updateChannelId} + /> + </div> + </div> + <div className='backstage-form__footer'> + <FormError errors={[this.state.serverError, this.state.clientError]}/> + <Link + className='btn btn-sm' + to={'/settings/integrations/add'} + > + <FormattedMessage + id='add_incoming_webhook.cancel' + defaultMessage='Cancel' + /> + </Link> + <SpinnerButton + className='btn btn-primary' + type='submit' + spinning={this.state.saving} + onClick={this.handleSubmit} + > + <FormattedMessage + id='add_incoming_webhook.save' + defaultMessage='Save' + /> + </SpinnerButton> + </div> + </form> + </div> </div> ); } diff --git a/webapp/components/backstage/add_integration.jsx b/webapp/components/backstage/add_integration.jsx index cebc1e8b0..5f4a69bfe 100644 --- a/webapp/components/backstage/add_integration.jsx +++ b/webapp/components/backstage/add_integration.jsx @@ -57,18 +57,16 @@ export default class AddIntegration extends React.Component { } return ( - <div className='backstage row'> - <div className='add-integration'> - <div className='backstage__header'> - <h1 className='text'> - <FormattedMessage - id='add_integration.header' - defaultMessage='Add Integration' - /> - </h1> - </div> + <div className='backstage-content row'> + <div className='backstage-header'> + <h1> + <FormattedMessage + id='add_integration.header' + defaultMessage='Add Integration' + /> + </h1> </div> - <div className='add-integration__options'> + <div> {options} </div> </div> diff --git a/webapp/components/backstage/add_integration_option.jsx b/webapp/components/backstage/add_integration_option.jsx index 3c3caf2f4..b17ebb185 100644 --- a/webapp/components/backstage/add_integration_option.jsx +++ b/webapp/components/backstage/add_integration_option.jsx @@ -21,16 +21,16 @@ export default class AddIntegrationOption extends React.Component { return ( <Link to={link} - className='add-integration-option' + className='add-integration' > <img - className='add-integration-option__image' + className='add-integration__image' src={image} /> - <div className='add-integration-option__title'> + <div className='add-integration__title'> {title} </div> - <div className='add-integration-option__description'> + <div className='add-integration__description'> {description} </div> </Link> diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/backstage/add_outgoing_webhook.jsx index 3ae2f8606..5d98138df 100644 --- a/webapp/components/backstage/add_outgoing_webhook.jsx +++ b/webapp/components/backstage/add_outgoing_webhook.jsx @@ -128,10 +128,10 @@ export default class AddOutgoingWebhook extends React.Component { render() { return ( - <div className='backstage row'> + <div className='backstage-content row'> <div className='add-outgoing-webhook'> - <div className='backstage__header'> - <h1 className='text'> + <div className='backstage-header'> + <h1> <FormattedMessage id='add_outgoing_webhook.header' defaultMessage='Add Outgoing Webhook' @@ -139,115 +139,131 @@ export default class AddOutgoingWebhook extends React.Component { </h1> </div> </div> - <form className='add-outgoing-webhook__body'> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='name' - > - <FormattedMessage - id='add_outgoing_webhook.name' - defaultMessage='Name' - /> - </label> - <input - id='name' - type='text' - value={this.state.name} - onChange={this.updateName} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='description' - > - <FormattedMessage - id='add_outgoing_webhook.description' - defaultMessage='Description' - /> - </label> - <input - id='description' - type='text' - value={this.state.description} - onChange={this.updateDescription} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='channelId' - > - <FormattedMessage - id='add_outgoing_webhook.channel' - defaultMessage='Channel' - /> - </label> - <ChannelSelect - id='channelId' - value={this.state.channelId} - onChange={this.updateChannelId} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='triggerWords' - > - <FormattedMessage - id='add_outgoing_webhook.triggerWords' - defaultMessage='Trigger Words (One Per Line)' - /> - </label> - <textarea - id='triggerWords' - rows='3' - value={this.state.triggerWords} - onChange={this.updateTriggerWords} - /> - </div> - <div className='add-integration__row'> - <label - className='add-integration__label' - htmlFor='callbackUrls' - > - <FormattedMessage - id='add_outgoing_webhook.callbackUrls' - defaultMessage='Callback URLs (One Per Line)' - /> - </label> - <textarea - id='callbackUrls' - rows='3' - value={this.state.callbackUrls} - onChange={this.updateCallbackUrls} - /> - </div> - <div className='add-integration__submit-row'> - <Link - className='btn btn-sm' - to={'/settings/integrations/add'} - > - <FormattedMessage - id='add_outgoing_webhook.cancel' - defaultMessage='Cancel' - /> - </Link> - <SpinnerButton - className='btn btn-primary' - type='submit' - spinning={this.state.saving} - onClick={this.handleSubmit} - > - <FormattedMessage - id='add_outgoing_webhook.save' - defaultMessage='Save' - /> - </SpinnerButton> - </div> - <FormError errors={[this.state.serverError, this.state.clientError]}/> - </form> + <div className='backstage-form'> + <form className='form-horizontal'> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='name' + > + <FormattedMessage + id='add_outgoing_webhook.name' + defaultMessage='Name' + /> + </label> + <div className='col-md-5 col-sm-9'> + <input + id='name' + type='text' + className='form-control' + value={this.state.name} + onChange={this.updateName} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='description' + > + <FormattedMessage + id='add_outgoing_webhook.description' + defaultMessage='Description' + /> + </label> + <div className='col-md-5 col-sm-9'> + <input + id='description' + type='text' + className='form-control' + value={this.state.description} + onChange={this.updateDescription} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='channelId' + > + <FormattedMessage + id='add_outgoing_webhook.channel' + defaultMessage='Channel' + /> + </label> + <div className='col-md-5 col-sm-9'> + <ChannelSelect + id='channelId' + value={this.state.channelId} + onChange={this.updateChannelId} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='triggerWords' + > + <FormattedMessage + id='add_outgoing_webhook.triggerWords' + defaultMessage='Trigger Words (One Per Line)' + /> + </label> + <div className='col-md-5 col-sm-9'> + <textarea + id='triggerWords' + rows='3' + className='form-control' + value={this.state.triggerWords} + onChange={this.updateTriggerWords} + /> + </div> + </div> + <div className='form-group'> + <label + className='control-label col-sm-3' + htmlFor='callbackUrls' + > + <FormattedMessage + id='add_outgoing_webhook.callbackUrls' + defaultMessage='Callback URLs (One Per Line)' + /> + </label> + <div className='col-md-5 col-sm-9'> + <textarea + id='callbackUrls' + rows='3' + className='form-control' + value={this.state.callbackUrls} + onChange={this.updateCallbackUrls} + /> + </div> + </div> + <div className='backstage-form__footer'> + <FormError errors={[this.state.serverError, this.state.clientError]}/> + <Link + className='btn btn-sm' + to={'/settings/integrations/add'} + > + <FormattedMessage + id='add_outgoing_webhook.cancel' + defaultMessage='Cancel' + /> + </Link> + <SpinnerButton + className='btn btn-primary' + type='submit' + spinning={this.state.saving} + onClick={this.handleSubmit} + > + <FormattedMessage + id='add_outgoing_webhook.save' + defaultMessage='Save' + /> + </SpinnerButton> + </div> + </form> + </div> </div> ); } diff --git a/webapp/components/backstage/backstage_category.jsx b/webapp/components/backstage/backstage_category.jsx index e8b0b57ae..913c7562c 100644 --- a/webapp/components/backstage/backstage_category.jsx +++ b/webapp/components/backstage/backstage_category.jsx @@ -50,7 +50,7 @@ export default class BackstageCategory extends React.Component { } return ( - <li className='backstage__sidebar__category'> + <li className='backstage-sidebar__category'> <Link to={link} className='category-title' diff --git a/webapp/components/backstage/backstage_navbar.jsx b/webapp/components/backstage/backstage_navbar.jsx index 555165791..d1dac6043 100644 --- a/webapp/components/backstage/backstage_navbar.jsx +++ b/webapp/components/backstage/backstage_navbar.jsx @@ -39,9 +39,9 @@ export default class BackstageNavbar extends React.Component { } return ( - <div className='backstage__navbar row'> + <div className='backstage-navbar row'> <Link - className='backstage__navbar__back' + className='backstage-navbar__back' to={`/${this.state.team.display_name}/channels/town-square`} > <i className='fa fa-angle-left'/> diff --git a/webapp/components/backstage/backstage_sidebar.jsx b/webapp/components/backstage/backstage_sidebar.jsx index 63a0df5cb..13c4f8b50 100644 --- a/webapp/components/backstage/backstage_sidebar.jsx +++ b/webapp/components/backstage/backstage_sidebar.jsx @@ -10,7 +10,7 @@ import {FormattedMessage} from 'react-intl'; export default class BackstageSidebar extends React.Component { render() { return ( - <div className='backstage__sidebar'> + <div className='backstage-sidebar'> <ul> <BackstageCategory name='integrations' diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/backstage/installed_incoming_webhook.jsx index 4ca421a02..f65cf6327 100644 --- a/webapp/components/backstage/installed_incoming_webhook.jsx +++ b/webapp/components/backstage/installed_incoming_webhook.jsx @@ -35,26 +35,26 @@ export default class InstalledIncomingWebhook extends React.Component { const channelName = channel ? channel.display_name : 'cannot find channel'; return ( - <div className='installed-integrations__item installed-integrations__incoming-webhook'> - <div className='details'> - <div className='details-row'> - <span className='name'> + <div className='backstage-list__item'> + <div className='item-details'> + <div className='item-details__row'> + <span className='item-details__name'> {channelName} </span> - <span className='type'> + <span className='item-details__type'> <FormattedMessage id='installed_integrations.incomingWebhookType' defaultMessage='(Incoming Webhook)' /> </span> </div> - <div className='details-row'> - <span className='description'> + <div className='item-details__row'> + <span className='item-details__description'> {Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id} </span> </div> </div> - <div className='actions'> + <div className='item-actions'> <a href='#' onClick={this.handleDeleteClick} diff --git a/webapp/components/backstage/installed_integrations.jsx b/webapp/components/backstage/installed_integrations.jsx index ff0b6e4ec..fe84ae81a 100644 --- a/webapp/components/backstage/installed_integrations.jsx +++ b/webapp/components/backstage/installed_integrations.jsx @@ -98,9 +98,9 @@ export default class InstalledIntegrations extends React.Component { const fields = []; if (incomingWebhooks.length > 0 || outgoingWebhooks.length > 0) { - let filterClassName = 'type-filter'; + let filterClassName = 'filter-sort'; if (this.state.typeFilter === '') { - filterClassName += ' type-filter--selected'; + filterClassName += ' filter-sort--active'; } fields.push( @@ -131,9 +131,9 @@ export default class InstalledIntegrations extends React.Component { </span> ); - let filterClassName = 'type-filter'; + let filterClassName = 'filter-sort'; if (this.state.typeFilter === 'incomingWebhooks') { - filterClassName += ' type-filter--selected'; + filterClassName += ' filter-sort--active'; } fields.push( @@ -164,9 +164,9 @@ export default class InstalledIntegrations extends React.Component { </span> ); - let filterClassName = 'type-filter'; + let filterClassName = 'filter-sort'; if (this.state.typeFilter === 'outgoingWebhooks') { - filterClassName += ' type-filter--selected'; + filterClassName += ' filter-sort--active'; } fields.push( @@ -188,7 +188,7 @@ export default class InstalledIntegrations extends React.Component { } return ( - <div className='type-filters'> + <div className='backstage-filters__sort'> {fields} </div> ); @@ -243,10 +243,10 @@ export default class InstalledIntegrations extends React.Component { } return ( - <div className='backstage row'> + <div className='backstage-content row'> <div className='installed-integrations'> - <div className='backstage__header'> - <h1 className='text'> + <div className='backstage-header'> + <h1> <FormattedMessage id='installed_integrations.header' defaultMessage='Installed Integrations' @@ -269,17 +269,21 @@ export default class InstalledIntegrations extends React.Component { </button> </Link> </div> - <div className='installed-integrations__filters'> + <div className='backstage-filters'> {this.renderTypeFilters(this.state.incomingWebhooks, this.state.outgoingWebhooks)} - <input - type='search' - placeholder={Utils.localizeMessage('installed_integrations.search', 'Search Integrations')} - value={this.state.filter} - onChange={this.updateFilter} - style={{flexGrow: 0, flexShrink: 0}} - /> + <div className='backstage-filter__search'> + <i className='fa fa-search'></i> + <input + type='search' + className='form-control' + placeholder={Utils.localizeMessage('installed_integrations.search', 'Search Integrations')} + value={this.state.filter} + onChange={this.updateFilter} + style={{flexGrow: 0, flexShrink: 0}} + /> + </div> </div> - <div className='installed-integrations__list'> + <div className='backstage-list'> {integrations} </div> </div> diff --git a/webapp/components/backstage/installed_outgoing_webhook.jsx b/webapp/components/backstage/installed_outgoing_webhook.jsx index 12e1a5c81..fee427260 100644 --- a/webapp/components/backstage/installed_outgoing_webhook.jsx +++ b/webapp/components/backstage/installed_outgoing_webhook.jsx @@ -43,21 +43,21 @@ export default class InstalledOutgoingWebhook extends React.Component { const channelName = channel ? channel.display_name : 'cannot find channel'; return ( - <div className='installed-integrations__item installed-integrations__outgoing-webhook'> - <div className='details'> - <div className='details-row'> - <span className='name'> + <div className='backstage-list__item'> + <div className='item-details'> + <div className='item-details__row'> + <span className='item-details__name'> {channelName} </span> - <span className='type'> + <span className='item-details__type'> <FormattedMessage id='installed_integrations.outgoingWebhookType' defaultMessage='(Outgoing Webhook)' /> </span> </div> - <div className='details-row'> - <span className='description'> + <div className='item-details__row'> + <span className='item-details__description'> {Utils.getWindowLocationOrigin() + '/hooks/' + outgoingWebhook.id} {' - '} {outgoingWebhook.token} diff --git a/webapp/components/error_page.jsx b/webapp/components/error_page.jsx new file mode 100644 index 000000000..53f0fce82 --- /dev/null +++ b/webapp/components/error_page.jsx @@ -0,0 +1,58 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import $ from 'jquery'; + +import React from 'react'; +import {Link} from 'react-router'; + +import * as Utils from 'utils/utils.jsx'; + +export default class ErrorPage extends React.Component { + componentDidMount() { + $('body').attr('class', 'sticky error'); + } + componentWillUnmount() { + $('body').attr('class', ''); + } + render() { + let title = this.props.location.query.title; + if (!title || title === '') { + title = Utils.localizeMessage('error.generic.title', 'Error'); + } + + let message = this.props.location.query.message; + if (!message || message === '') { + message = Utils.localizeMessage('error.generic.message', 'An error has occoured.'); + } + + let link = this.props.location.query.link; + if (!link || link === '') { + link = '/'; + } + + let linkMessage = this.props.location.query.linkmessage; + if (!linkMessage || linkMessage === '') { + linkMessage = Utils.localizeMessage('error.generic.link_message', 'Back to Mattermost'); + } + + return ( + <div className='container-fluid'> + <div className='error__container'> + <div className='error__icon'> + <i className='fa fa-exclamation-triangle'/> + </div> + <h2>{title}</h2> + <p>{message}</p> + <Link to={link}>{linkMessage}</Link> + </div> + </div> + ); + } +} + +ErrorPage.defaultProps = { +}; +ErrorPage.propTypes = { + location: React.PropTypes.object +}; diff --git a/webapp/components/post.jsx b/webapp/components/post.jsx index 30c47ee22..7294cf163 100644 --- a/webapp/components/post.jsx +++ b/webapp/components/post.jsx @@ -129,6 +129,7 @@ export default class Post extends React.Component { const post = this.props.post; const parentPost = this.props.parentPost; const posts = this.props.posts; + const mattermostLogo = Constants.MATTERMOST_ICON_SVG; if (!post.props) { post.props = {}; @@ -184,24 +185,22 @@ export default class Post extends React.Component { let profilePic = null; if (!this.props.hideProfilePic) { - let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp; - if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { - if (post.props.override_icon_url) { - src = post.props.override_icon_url; - } else { - src = Constants.DEFAULT_WEBHOOK_LOGO; - } - } else if (Utils.isSystemMessage(post)) { - src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE; - } - profilePic = ( <img - src={src} + src={Utils.getProfilePicSrcForPost(post, timestamp)} height='36' width='36' /> ); + + if (Utils.isSystemMessage(post)) { + profilePic = ( + <span + className='icon' + dangerouslySetInnerHTML={{__html: mattermostLogo}} + /> + ); + } } return ( diff --git a/webapp/components/post_body_additional_content.jsx b/webapp/components/post_body_additional_content.jsx index 2cd82f213..452597dde 100644 --- a/webapp/components/post_body_additional_content.jsx +++ b/webapp/components/post_body_additional_content.jsx @@ -70,7 +70,7 @@ export default class PostBodyAdditionalContent extends React.Component { return this.getSlackAttachment(); } - const link = Utils.extractLinks(this.props.post.message)[0]; + const link = Utils.extractFirstLink(this.props.post.message); if (!link) { return null; } diff --git a/webapp/components/posts_view.jsx b/webapp/components/posts_view.jsx index 917411549..aa7f445ce 100644 --- a/webapp/components/posts_view.jsx +++ b/webapp/components/posts_view.jsx @@ -204,10 +204,12 @@ export default class PostsView extends React.Component { // the previous post was made by the same user as the current post, // the previous post is not a comment, // the current post is not a comment, + // the previous post is not from a webhook // the current post is not from a webhook if (prevPostUserId === postUserId && !prevPostIsComment && !postIsComment && + !prevPostFromWebhook && !postFromWebhook) { hideProfilePic = true; } @@ -308,7 +310,7 @@ export default class PostsView extends React.Component { if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) { this.scrollToBottom(); } else if (this.props.scrollType === PostsView.SCROLL_TYPE_NEW_MESSAGE) { - window.requestAnimationFrame(() => { + window.setTimeout(window.requestAnimationFrame(() => { // If separator exists scroll to it. Otherwise scroll to bottom. if (this.refs.newMessageSeparator) { var objDiv = this.refs.postlist; @@ -316,7 +318,7 @@ export default class PostsView extends React.Component { } else if (this.refs.postlist) { this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight; } - }); + }), 0); } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) { window.requestAnimationFrame(() => { const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]); diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx index 7a7c5f692..9a207e429 100644 --- a/webapp/components/rhs_root_post.jsx +++ b/webapp/components/rhs_root_post.jsx @@ -213,21 +213,10 @@ export default class RhsRootPost extends React.Component { ); } - let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp; - if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { - if (post.props.override_icon_url) { - src = post.props.override_icon_url; - } else { - src = Constants.DEFAULT_WEBHOOK_LOGO; - } - } else if (Utils.isSystemMessage(post)) { - src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE; - } - const profilePic = ( <img className='post-profile-img' - src={src} + src={Utils.getProfilePicSrcForPost(post, timestamp)} height='36' width='36' /> diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx index 75cbcb2a0..58f09c0e0 100644 --- a/webapp/components/search_results_item.jsx +++ b/webapp/components/search_results_item.jsx @@ -1,11 +1,13 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import UserStore from 'stores/user_store.jsx'; import UserProfile from './user_profile.jsx'; + +import UserStore from 'stores/user_store.jsx'; + import * as GlobalActions from 'action_creators/global_actions.jsx'; import * as TextFormatting from 'utils/text_formatting.jsx'; - +import * as Utils from 'utils/utils.jsx'; import Constants from 'utils/constants.jsx'; import {FormattedMessage, FormattedDate} from 'react-intl'; @@ -29,6 +31,7 @@ export default class SearchResultsItem extends React.Component { const channel = this.props.channel; const timestamp = UserStore.getCurrentUser().update_at; const user = this.props.user || {}; + const post = this.props.post; if (channel) { channelName = channel.display_name; @@ -47,13 +50,23 @@ export default class SearchResultsItem extends React.Component { mentionHighlight: this.props.isMentionSearch }; + let overrideUsername; + let disableProfilePopover = false; + if (post.props && + post.props.from_webhook && + post.props.override_username && + global.window.mm_config.EnablePostUsernameOverride === 'true') { + overrideUsername = post.props.override_username; + disableProfilePopover = true; + } + return ( <div className='search-item__container'> <div className='date-separator'> <hr className='separator__hr'/> <div className='separator__text'> <FormattedDate - value={this.props.post.create_at} + value={post.create_at} day='numeric' month='long' year='numeric' @@ -67,18 +80,24 @@ export default class SearchResultsItem extends React.Component { <div className='post__content'> <div className='post__img'> <img - src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp} + src={Utils.getProfilePicSrcForPost(post, timestamp)} height='36' width='36' /> </div> <div> <ul className='post__header'> - <li className='col__name'><strong><UserProfile user={user}/></strong></li> + <li className='col__name'><strong> + <UserProfile + user={user} + overwriteName={overrideUsername} + disablePopover={disableProfilePopover} + /> + </strong></li> <li className='col'> <time className='search-item-time'> <FormattedDate - value={this.props.post.create_at} + value={post.create_at} hour12={true} hour='2-digit' minute='2-digit' @@ -87,7 +106,7 @@ export default class SearchResultsItem extends React.Component { </li> <li> <Link - to={'/' + window.location.pathname.split('/')[1] + '/pl/' + this.props.post.id} + to={'/' + window.location.pathname.split('/')[1] + '/pl/' + post.id} className='search-item__jump' > <FormattedMessage @@ -112,7 +131,7 @@ export default class SearchResultsItem extends React.Component { <div className='search-item-snippet'> <span onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message, formattingOptions)}} /> </div> </div> diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx index b22d3ec34..500e73cf2 100644 --- a/webapp/components/sidebar.jsx +++ b/webapp/components/sidebar.jsx @@ -138,7 +138,9 @@ export default class Sidebar extends React.Component { unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())), showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER, currentTeam: TeamStore.getCurrent(), - currentUser: UserStore.getCurrentUser() + currentUser: UserStore.getCurrentUser(), + townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL), + offTopic: ChannelStore.getByName(Constants.OFFTOPIC_CHANNEL) }; } @@ -290,6 +292,16 @@ export default class Sidebar extends React.Component { createTutorialTip() { const screens = []; + let townSquareDisplayName = Constants.DEFAULT_CHANNEL_UI_NAME; + if (this.state.townSquare) { + townSquareDisplayName = this.state.townSquare.display_name; + } + + let offTopicDisplayName = Constants.OFFTOPIC_CHANNEL_UI_NAME; + if (this.state.offTopic) { + offTopicDisplayName = this.state.offTopic.display_name; + } + screens.push( <div> <FormattedHTMLMessage @@ -303,10 +315,14 @@ export default class Sidebar extends React.Component { <div> <FormattedHTMLMessage id='sidebar.tutorialScreen2' - defaultMessage='<h4>"Town Square" and "Off-Topic" channels</h4> + defaultMessage='<h4>"{townsquare}" and "{offtopic}" channels</h4> <p>Here are two public channels to start:</p> - <p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p> - <p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>' + <p><strong>{townsquare}</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p> + <p><strong>{offtopic}</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>' + values={{ + townsquare: townSquareDisplayName, + offtopic: offTopicDisplayName + }} /> </div> ); diff --git a/webapp/components/suggestion/at_mention_provider.jsx b/webapp/components/suggestion/at_mention_provider.jsx index b423528c3..90ec6e660 100644 --- a/webapp/components/suggestion/at_mention_provider.jsx +++ b/webapp/components/suggestion/at_mention_provider.jsx @@ -100,13 +100,16 @@ export default class AtMentionProvider { } } - // add dummy users to represent the @all and @channel special mentions - if ('all'.startsWith(usernamePrefix)) { - filtered.push({username: 'all'}); - } + //Don't imply that @all and @channel can be direct messaged + if (!pretext.startsWith('/msg')) { + // add dummy users to represent the @all and @channel special mentions + if ('all'.startsWith(usernamePrefix)) { + filtered.push({username: 'all'}); + } - if ('channel'.startsWith(usernamePrefix)) { - filtered.push({username: 'channel'}); + if ('channel'.startsWith(usernamePrefix)) { + filtered.push({username: 'channel'}); + } } filtered = filtered.sort((a, b) => a.username.localeCompare(b.username)); diff --git a/webapp/components/tutorial/tutorial_intro_screens.jsx b/webapp/components/tutorial/tutorial_intro_screens.jsx index bad426cfc..0358a6a65 100644 --- a/webapp/components/tutorial/tutorial_intro_screens.jsx +++ b/webapp/components/tutorial/tutorial_intro_screens.jsx @@ -19,6 +19,12 @@ const NUM_SCREENS = 3; import React from 'react'; export default class TutorialIntroScreens extends React.Component { + static get propTypes() { + return { + townSquare: React.PropTypes.object, + offTopic: React.PropTypes.object + }; + } constructor(props) { super(props); @@ -153,6 +159,11 @@ export default class TutorialIntroScreens extends React.Component { ); } + let townSquareDisplayName = Constants.DEFAULT_CHANNEL_UI_NAME; + if (this.props.townSquare) { + townSquareDisplayName = this.props.townSquare.display_name; + } + return ( <div> <h3> @@ -171,7 +182,10 @@ export default class TutorialIntroScreens extends React.Component { {supportInfo} <FormattedMessage id='tutorial_intro.end' - defaultMessage='Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.' + defaultMessage='Click “Next” to enter {channel}. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.' + values={{ + channel: townSquareDisplayName + }} /> {circles} </div> diff --git a/webapp/components/tutorial/tutorial_view.jsx b/webapp/components/tutorial/tutorial_view.jsx index d9e0ef40d..5f2c1a257 100644 --- a/webapp/components/tutorial/tutorial_view.jsx +++ b/webapp/components/tutorial/tutorial_view.jsx @@ -1,18 +1,43 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React from 'react'; - import TutorialIntroScreens from './tutorial_intro_screens.jsx'; +import ChannelStore from 'stores/channel_store.jsx'; +import Constants from 'utils/constants.jsx'; + +import React from 'react'; + export default class TutorialView extends React.Component { + constructor(props) { + super(props); + + this.handleChannelChange = this.handleChannelChange.bind(this); + + this.state = { + townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL) + }; + } + componentDidMount() { + ChannelStore.addChangeListener(this.handleChannelChange); + } + componentWillUnmount() { + ChannelStore.removeChangeListener(this.handleChannelChange); + } + handleChannelChange() { + this.setState({ + townSquare: ChannelStore.getByName(Constants.DEFAULT_CHANNEL) + }); + } render() { return ( <div id='app-content' className='app__content' > - <TutorialIntroScreens/> + <TutorialIntroScreens + townSquare={this.state.townSquare} + /> </div> ); } diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx index d815bd371..d169e01b5 100644 --- a/webapp/components/user_settings/user_settings_display.jsx +++ b/webapp/components/user_settings/user_settings_display.jsx @@ -304,7 +304,7 @@ export default class UserSettingsDisplay extends React.Component { describe = ( <FormattedMessage id='user.settings.display.showUsername' - defaultMessage='Show username (team default)' + defaultMessage='Show username (default)' /> ); } else if (this.state.nameFormat === 'full_name') { diff --git a/webapp/components/user_settings/user_settings_security.jsx b/webapp/components/user_settings/user_settings_security.jsx index e4044e6d0..ff5a898a9 100644 --- a/webapp/components/user_settings/user_settings_security.jsx +++ b/webapp/components/user_settings/user_settings_security.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import $ from 'jquery'; import SettingItemMin from '../setting_item_min.jsx'; import SettingItemMax from '../setting_item_max.jsx'; import AccessHistoryModal from '../access_history_modal.jsx'; @@ -247,7 +248,7 @@ class SecurityTab extends React.Component { extraInfo = ( <span> <FormattedMessage - id='user.settings.mfa.addHelp' + id='user.settings.mfa.addHelpQr' defaultMessage='Please scan the QR code with the Google Authenticator app on your smartphone and fill in the token with one provided by the app.' /> </span> diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index 6f23552b3..7dc6486ab 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -3,12 +3,17 @@ "about.date": "Build Date:", "about.enterpriseEditione1": "Enterprise Edition", "about.hash": "Build Hash:", + "about.copyright": "Copyright 2016 Mattermost, Inc. All rights reserved", "about.licensed": "Licensed by:", "about.number": "Build Number:", "about.teamEditiont0": "Team Edition", "about.teamEditiont1": "Enterprise Edition", "about.title": "About Mattermost", "about.version": "Version:", + "about.teamEditionSt": "All your team communication in one place, instantly searchable and accessible anywhere.", + "about.teamEditionLearn": "Join the Mattermost community at ", + "about.enterpriseEditionSt": "Modern enterprise communication from behind your firewall.", + "about.enterpriseEditionLearn": "Learn more about Enterprise Edition at ", "access_history.title": "Access History", "activity_log.activeSessions": "Active Sessions", "activity_log.browser": "Browser: {browser}", @@ -30,10 +35,10 @@ "add_incoming_webhook.name": "Name", "add_incoming_webhook.save": "Save", "add_integration.header": "Add Integration", - "add_integration.incomingWebhook.title": "Incoming Webhook", "add_integration.incomingWebhook.description": "Create webhook URLs for use in external integrations.", - "add_integration.outgoingWebhook.title": "Outgoing Webhook", + "add_integration.incomingWebhook.title": "Incoming Webhook", "add_integration.outgoingWebhook.description": "Create webhooks to send new message events to an external integration.", + "add_integration.outgoingWebhook.title": "Outgoing Webhook", "add_outgoing_webhook.callbackUrls": "Callback URLs (One Per Line)", "add_outgoing_webhook.callbackUrlsRequired": "One or more callback URLs are required", "add_outgoing_webhook.cancel": "Cancel", @@ -43,17 +48,19 @@ "add_outgoing_webhook.name": "Name", "add_outgoing_webhook.save": "Save", "add_outgoing_webhook.triggerWOrds": "Trigger Words (One Per Line)", + "add_outgoing_webhook.triggerWords": "Trigger Words (One Per Line)", "add_outgoing_webhook.triggerWordsOrChannelRequired": "A valid channel or a list of trigger words is required", "admin.audits.reload": "Reload", "admin.audits.title": "User Activity", "admin.compliance.directoryDescription": "Directory to which compliance reports are written. If blank, will be set to ./data/.", "admin.compliance.directoryExample": "Ex \"./data/\"", "admin.compliance.directoryTitle": "Compliance Directory Location:", + "admin.compliance.enableDailyDesc": "When true, Mattermost will generate a daily compliance report.", "admin.compliance.enableDailyTitle": "Enable Daily Report:", - "admin.compliance.enableDesc": "When true, Mattermost will generate a daily compliance report.", + "admin.compliance.enableDesc": "When true, Mattermost allows compliance reporting", "admin.compliance.enableTitle": "Enable Compliance:", "admin.compliance.false": "false", - "admin.compliance.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>Compliance is an enterprise feature. Your current license does not support Compliance. Click <a href=\"http://mattermost.com\" target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>", + "admin.compliance.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>Compliance is an enterprise feature. Your current license does not support Compliance. Click <a href=\"http://mattermost.com\"target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>", "admin.compliance.save": "Save", "admin.compliance.saving": "Saving Config...", "admin.compliance.title": "Compliance Settings", @@ -233,7 +240,7 @@ "admin.ldap.lastnameAttrDesc": "The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.", "admin.ldap.lastnameAttrEx": "Ex \"sn\"", "admin.ldap.lastnameAttrTitle": "Last Name Attribute:", - "admin.ldap.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href=\"http://mattermost.com\" target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>", + "admin.ldap.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href=\"http://mattermost.com\"target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>", "admin.ldap.portDesc": "The port Mattermost will use to connect to the LDAP server. Default is 389.", "admin.ldap.portEx": "Ex \"389\"", "admin.ldap.portTitle": "LDAP Port:", @@ -251,7 +258,10 @@ "admin.ldap.usernameAttrEx": "Ex \"sAMAccountName\"", "admin.ldap.usernameAttrTitle": "Username Attribute:", "admin.licence.keyMigration": "If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, <a href=\"http://mattermost.com\" target=\"_blank\">disable all Enterprise Edition features on this server</a>. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.", + "admin.license.choose": "Choose File", "admin.license.chooseFile": "Choose File", + "admin.license.edition": "Edition: ", + "admin.license.key": "License Key: ", "admin.license.keyRemove": "Remove Enterprise License and Downgrade Server", "admin.license.noFile": "No file uploaded", "admin.license.removing": "Removing License...", @@ -328,15 +338,13 @@ "admin.select_team.close": "Close", "admin.select_team.select": "Select", "admin.select_team.selectTeam": "Select Team", - "admin.service.mfaTitle": "Enable Multi-factor Authentication:", - "admin.service.mfaDesc": "When true, users will be given the option to add multi-factor authentication to their account. They will need a smartphone and an authenticator app such as Google Authenticator.", "admin.service.attemptDescription": "Login attempts allowed before user is locked out and required to reset password via email.", "admin.service.attemptExample": "Ex \"10\"", "admin.service.attemptTitle": "Maximum Login Attempts:", "admin.service.cmdsDesc": "When true, user created slash commands will be allowed.", "admin.service.cmdsTitle": "Enable Slash Commands: ", - "admin.service.corsDescription": "Enable HTTP Cross origin request from specific domains (separate by a spacebar). Use \"*\" if you want to allow CORS from any domain or leave it blank to disable it.", - "admin.service.corsEx": "http://example.com https://example.com", + "admin.service.corsDescription": "Enable HTTP Cross origin request from a specific domain. Use \"*\" if you want to allow CORS from any domain or leave it blank to disable it.", + "admin.service.corsEx": "http://example.com", "admin.service.corsTitle": "Allow Cross-origin Requests from:", "admin.service.developerDesc": "(Developer Option) When true, extra information around errors will be displayed in the UI.", "admin.service.developerTitle": "Enable Developer Mode: ", @@ -353,6 +361,8 @@ "admin.service.listenAddress": "Listen Address:", "admin.service.listenDescription": "The address to which to bind and listen. Entering \":8065\" will bind to all interfaces or you can choose one like \"127.0.0.1:8065\". Changing this will require a server restart before taking effect.", "admin.service.listenExample": "Ex \":8065\"", + "admin.service.mfaDesc": "When true, users will be given the option to add multi-factor authentication to their account. They will need a smartphone and an authenticator app such as Google Authenticator.", + "admin.service.mfaTitle": "Enable Multi-factor Authentication:", "admin.service.mobileSessionDays": "Session Length for Mobile Device in Days:", "admin.service.mobileSessionDaysDesc": "The native mobile session will expire after the number of days specified and will require a user to login again.", "admin.service.outWebhooksDesc": "When true, outgoing webhooks will be allowed.", @@ -512,6 +522,7 @@ "analytics.team.privateGroups": "Private Groups", "analytics.team.publicChannels": "Public Channels", "analytics.team.recentActive": "Recent Active Users", + "analytics.team.recentUsers": "Recent Active Users", "analytics.team.title": "Team Statistics for {team}", "analytics.team.totalPosts": "Total Posts", "analytics.team.totalUsers": "Total Users", @@ -576,10 +587,10 @@ "authorize.title": "An application would like to connect to your {teamName} account", "backstage_navbar.backToMattermost": "Back to {siteName}", "backstage_sidebar.integrations": "Integrations", - "backstage_sidebar.integrations.installed": "Installed Integrations", "backstage_sidebar.integrations.add": "Add Integration", "backstage_sidebar.integrations.add.incomingWebhook": "Incoming Webhook", "backstage_sidebar.integrations.add.outgoingWebhook": "Outgoing Webhook", + "backstage_sidebar.integrations.installed": "Installed Integrations", "center_panel.recent": "Click here to jump to recent messages. ", "chanel_header.addMembers": "Add Members", "change_url.close": "Close", @@ -702,6 +713,7 @@ "claim.oauth_to_email.pwdNotMatch": "Password do not match.", "claim.oauth_to_email.switchTo": "Switch {type} to email and password", "claim.oauth_to_email.title": "Switch {type} Account to Email", + "claim.oauth_to_email_newPwd": "Enter a new password for your {team} {site} account", "confirm_modal.cancel": "Cancel", "create_comment.addComment": "Add a comment...", "create_comment.comment": "Add Comment", @@ -763,8 +775,9 @@ "file_upload.filesAbove": "Files above {max}MB could not be uploaded: {filenames}", "file_upload.limited": "Uploads limited to {count} files maximum. Please use additional posts for more files.", "file_upload.pasted": "Image Pasted at ", - "filtered_user_list.count": "{count, number} {count, plural, one {member} other {members}}", - "filtered_user_list.countTotal": "{count, number} {count, plural, one {member} other {members}} of {total} Total", + "filtered_user_list.count": "{count} {count, plural, one {member} other {members}}", + "filtered_user_list.countTotal": "{count} {count, plural, one {member} other {members}} of {total} Total", + "filtered_user_list.member": "Member", "filtered_user_list.search": "Search members", "find_team.email": "Email", "find_team.findDescription": "An email was sent with links to any teams to which you are a member.", @@ -800,13 +813,13 @@ "get_team_invite_link_modal.helpDisabled": "User creation has been disabled for your team. Please ask your team administrator for details.", "get_team_invite_link_modal.title": "Team Invite Link", "installed_integrations.add": "Add Integration", - "installed_integrations.allFilter": "All", + "installed_integrations.allFilter": "All ({count})", "installed_integrations.delete": "Delete", "installed_integrations.header": "Installed Integrations", - "installed_integrations.incomingWebhooksFilter": "Incoming Webhooks ({count})", "installed_integrations.incomingWebhookType": "(Incoming Webhook)", - "installed_integrations.outgoingWebhooksFilter": "Outgoing Webhooks ({count})", + "installed_integrations.incomingWebhooksFilter": "Incoming Webhooks ({count})", "installed_integrations.outgoingWebhookType": "(Outgoing Webhook)", + "installed_integrations.outgoingWebhooksFilter": "Outgoing Webhooks ({count})", "installed_integrations.regenToken": "Regen Token", "installed_integrations.search": "Search Integrations", "intro_messages.DM": "This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.", @@ -859,10 +872,6 @@ "login.session_expired": " Your session has expired. Please login again.", "login.signTo": "Sign in to:", "login.verified": " Email Verified", - "login_mfa.token": "MFA Token", - "login_mfa.enterToken": "To complete the sign in process, please enter a token from your smartphone's authenticator", - "login_mfa.submit": "Submit", - "login_mfa.tokenReq": "Please enter an MFA token", "login_email.badTeam": "Bad team name", "login_email.email": "Email", "login_email.emailReq": "An email is required", @@ -875,6 +884,10 @@ "login_ldap.pwdReq": "An LDAP password is required", "login_ldap.signin": "Sign in", "login_ldap.username": "LDAP Username", + "login_mfa.enterToken": "To complete the sign in process, please enter a token from your smartphone's authenticator", + "login_mfa.submit": "Submit", + "login_mfa.token": "MFA Token", + "login_mfa.tokenReq": "Please enter an MFA token", "login_username.badTeam": "Bad team name", "login_username.pwd": "Password", "login_username.pwdReq": "A password is required", @@ -934,7 +947,7 @@ "password_form.title": "Password Reset", "password_form.update": "Your password has been updated successfully.", "password_send.checkInbox": "Please check your inbox.", - "password_send.description": "To reset your password, enter the email address you used to sign up.", + "password_send.description": "To reset your password, enter the email address you used to sign up", "password_send.email": "Email", "password_send.error": "Please enter a valid email address.", "password_send.link": "<p>A password reset link has been sent to <b>{email}</b></p>", @@ -1031,7 +1044,7 @@ "sidebar.pg": "Private Groups", "sidebar.removeList": "Remove from list", "sidebar.tutorialScreen1": "<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. They’re open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>", - "sidebar.tutorialScreen2": "<h4>\"Town Square\" and \"Off-Topic\" channels</h4><p>Here are two public channels to start:</p><p><strong>Town Square</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p><p><strong>Off-Topic</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>", + "sidebar.tutorialScreen2": "<h4>\"{townsquare}\" and \"{offtopic}\" channels</h4><p>Here are two public channels to start:</p><p><strong>{townsquare}</strong> is a place for team-wide communication. Everyone in your team is a member of this channel.</p><p><strong>{offtopic}</strong> is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.</p>", "sidebar.tutorialScreen3": "<h4>Creating and Joining Channels</h4><p>Click <strong>\"More...\"</strong> to create a new channel or join an existing one.</p><p>You can also create a new channel or private group by clicking the <strong>\"+\" symbol</strong> next to the channel or private group header.</p>", "sidebar.unreadAbove": "Unread post(s) above", "sidebar.unreadBelow": "Unread post(s) below", @@ -1073,6 +1086,7 @@ "signup_user_completed.validEmail": "Please enter a valid email address", "signup_user_completed.welcome": "Welcome to:", "signup_user_completed.whatis": "What's your email address?", + "signup_user_completed.withLdap": "With your LDAP credentials", "sso_signup.find": "Find my teams", "sso_signup.gitlab": "Create team with GitLab Account", "sso_signup.google": "Create team with Google Apps Account", @@ -1179,7 +1193,7 @@ "textbox.quote": ">quote", "textbox.strike": "strike", "tutorial_intro.allSet": "You’re all set", - "tutorial_intro.end": "Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.", + "tutorial_intro.end": "Click “Next” to enter {channel}. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.", "tutorial_intro.invite": "Invite teammates", "tutorial_intro.next": "Next", "tutorial_intro.screenOne": "<h3>Welcome to:</h3><h1>Mattermost</h1><p>Your team communication all in one place, instantly searchable and available anywhere</p><p>Keep your team connected to help them achieve what matters most.</p>", @@ -1294,11 +1308,11 @@ "user.settings.general.confirmEmail": "Confirm Email", "user.settings.general.email": "Email", "user.settings.general.emailGitlabCantUpdate": "Login occurs through GitLab. Email cannot be updated. Email address used for notifications is {email}.", - "user.settings.general.emailLdapCantUpdate": "Login occurs through LDAP. Email cannot be updated. Email address used for notifications is {email}.", "user.settings.general.emailHelp1": "Email is used for sign-in, notifications, and password reset. Email requires verification if changed.", "user.settings.general.emailHelp2": "Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.", "user.settings.general.emailHelp3": "Email is used for sign-in, notifications, and password reset.", "user.settings.general.emailHelp4": "A verification email was sent to {email}.", + "user.settings.general.emailLdapCantUpdate": "Login occurs through LDAP. Email cannot be updated. Email address used for notifications is {email}.", "user.settings.general.emailMatch": "The new emails you entered do not match.", "user.settings.general.emptyName": "Click 'Edit' to add your full name", "user.settings.general.emptyNickname": "Click 'Edit' to add a nickname", @@ -1333,6 +1347,13 @@ "user.settings.integrations.commandsDescription": "Manage your slash commands", "user.settings.integrations.title": "Integration Settings", "user.settings.languages.change": "Change interface language", + "user.settings.mfa.add": "Add MFA to your account", + "user.settings.mfa.addHelp": "To add multi-factor authentication to your account you must have a smartphone with Google Authenticator installed.", + "user.settings.mfa.addHelpQr": "Please scan the QR code with the Google Authenticator app on your smartphone and fill in the token with one provided by the app.", + "user.settings.mfa.enterToken": "Token", + "user.settings.mfa.qrCode": "QR Code", + "user.settings.mfa.remove": "Remove MFA from your account", + "user.settings.mfa.removeHelp": "Removing multi-factor authentication will make your account more vulnerable to attacks.", "user.settings.modal.advanced": "Advanced", "user.settings.modal.confirmBtns": "Yes, Discard", "user.settings.modal.confirmMsg": "You have unsaved changes, are you sure you want to discard them?", @@ -1388,7 +1409,7 @@ "user.settings.security.switchEmail": "Switch to using email and password", "user.settings.security.switchGitlab": "Switch to using GitLab SSO", "user.settings.security.switchGoogle": "Switch to using Google SSO", - "user.settings.security.switchLda": "Switch to using LDAP", + "user.settings.security.switchLdap": "Switch to using LDAP", "user.settings.security.title": "Security Settings", "user.settings.security.viewHistory": "View Access History", "user_list.notFound": "No users found :(", diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json index d30792b8c..8cc9e5db6 100644 --- a/webapp/i18n/es.json +++ b/webapp/i18n/es.json @@ -22,13 +22,37 @@ "activity_log_modal.android": "Android", "activity_log_modal.androidNativeApp": "Android App Nativa", "activity_log_modal.iphoneNativeApp": "iPhone App Nativa", + "add_incoming_webhook.cancel": "Cancelar", + "add_incoming_webhook.channel": "Canal", + "add_incoming_webhook.channelRequired": "Es obligatorio asignar un canal válido", + "add_incoming_webhook.description": "Descripción", + "add_incoming_webhook.header": "Agregar un Webhook de Entrada", + "add_incoming_webhook.name": "Nombre", + "add_incoming_webhook.save": "Guardar", + "add_integration.header": "Agregar Integración", + "add_integration.incomingWebhook.description": "Crea webhook URLs para utilizarlas con integraciones externas.", + "add_integration.incomingWebhook.title": "Webhook de Entrada", + "add_integration.outgoingWebhook.description": "Crea webhooks para enviar mensajes a integraciones externas.", + "add_integration.outgoingWebhook.title": "Webhook de salida", + "add_outgoing_webhook.callbackUrls": "Callback URLs (Uno por Línea)", + "add_outgoing_webhook.callbackUrlsRequired": "Se require uno o más URLs para los callback", + "add_outgoing_webhook.cancel": "Cancelar", + "add_outgoing_webhook.channel": "Canal", + "add_outgoing_webhook.description": "Descripción", + "add_outgoing_webhook.header": "Agregar Webhook de Salida", + "add_outgoing_webhook.name": "Nombre", + "add_outgoing_webhook.save": "Guardar", + "add_outgoing_webhook.triggerWOrds": "Palabras gatilladoras (Una por línea)", + "add_outgoing_webhook.triggerWords": "Palabras gatilladoras (Una por Línea)", + "add_outgoing_webhook.triggerWordsOrChannelRequired": "Se require al menos un canal válido o una lista de palabras gatilladoras", "admin.audits.reload": "Recargar", "admin.audits.title": "Auditorías del Servidor", "admin.compliance.directoryDescription": "Directorio en el que se escriben los informes de cumplimiento. Si se deja en blanco, se utilizará ./data/.", "admin.compliance.directoryExample": "Ej \"./data/\"", "admin.compliance.directoryTitle": "Ubicación del Directorio de Cumplimiento:", + "admin.compliance.enableDailyDesc": "Cuando es verdadero, Mattermost generará un reporte de cumplimiento diario.", "admin.compliance.enableDailyTitle": "Habilitar Informes Diarios:", - "admin.compliance.enableDesc": "Cuando es verdadero, Mattermost generará un informe diario de cumplimiento.", + "admin.compliance.enableDesc": "Cuando es verdadero, Mattermost permite la creación de reportes de cumplimiento", "admin.compliance.enableTitle": "Habilitar el Cumplimiento:", "admin.compliance.false": "falso", "admin.compliance.noLicense": "<h4 class=\"banner__heading\">Nota:</h4><p>El Cumplimiento es una característica de la edición enterprise. Tu licencia actual no soporta Cumplimiento. Pincha <a href=\"http://mattermost.com\" target=\"_blank\">aquí</a> para información y precio de las licencias enterprise.</p>", @@ -211,7 +235,7 @@ "admin.ldap.lastnameAttrDesc": "El atributo en el servidor LDAP que será utilizado para poblar el apellido de los usuarios en Mattermost.", "admin.ldap.lastnameAttrEx": "Ej \"sn\"", "admin.ldap.lastnameAttrTitle": "Atributo Apellido:", - "admin.ldap.noLicense": "<h4 class=\"banner__heading\">Nota:</h4><p>LDAP es una característica de la edición enterprise. Tu licencia actual no soporta LDAP. Pincha <a href=\"http://mattermost.com\" target=\"_blank\">aquí</a> para obtener información y precios de las licencias de la edición enterprise.</p>", + "admin.ldap.noLicense": "<h4 class=\"banner__heading\">Nota:</h4><p>LDAP es una característica de la edición enterprise. Tu licencia actual no soporta LDAP. Pincha <a href=\"http://mattermost.com\" target=\"_blank\">aquí</a> para obtener información y precios de las licencias enterprise.</p>", "admin.ldap.portDesc": "El puerto que Mattermost utilizará para conectarse al servidor LDAP. El predeterminado es 389.", "admin.ldap.portEx": "Ej \"389\"", "admin.ldap.portTitle": "Puerto LDAP:", @@ -229,7 +253,10 @@ "admin.ldap.usernameAttrEx": "Ej \"sAMAccountName\"", "admin.ldap.usernameAttrTitle": "Atributo Usuario:", "admin.licence.keyMigration": "Si estás migrando servidores es posible que necesites remover tu licencia de este servidor para poder instalarlo en un servidor nuevo. Para empezar, <a href=\"http://mattermost.com\" target=\"_blank\">deshabilita todas las características de la Edición Enterprise de este servidor</a>. Esta operación habilitará la opción para remover la licencia y degradar este servidor de la Edición Enterprise a la Edición Team.", + "admin.license.choose": "Seleccionar Archivo", "admin.license.chooseFile": "Escoger Archivo", + "admin.license.edition": "Edición: ", + "admin.license.key": "Licencia: ", "admin.license.keyRemove": "Remover la Licencia Enterprise y Degradar el Servidor", "admin.license.noFile": "No se subió ningún archivo", "admin.license.removing": "Removiendo Licencia...", @@ -311,8 +338,8 @@ "admin.service.attemptTitle": "Máximo de intentos de conexión:", "admin.service.cmdsDesc": "Cuando es verdadero, se permite la creación de comandos de barra por usuarios.", "admin.service.cmdsTitle": "Habilitar Comandos de Barra: ", - "admin.service.corsDescription": "Habilita las solicitudes HTTP de origen cruzado para dominios en específico (separados por un espacio). Utiliza \"*\" si quieres habilitar CORS desde cualquier dominio o deja el campo en blanco para deshabilitarlo.", - "admin.service.corsEx": "http://ejemplo.com https://ejemplo.com", + "admin.service.corsDescription": "Habilitar solicitudes HTTP de origen cruzado desde un dominio específico. Utiliza \"*\" si quieres permitir CORS desde cualquier dominio o dejalo en blanco para deshabilitarlo.", + "admin.service.corsEx": "http://ejemplo.com", "admin.service.corsTitle": "Permitir Solicitudes de Origen Cruzado desde:", "admin.service.developerDesc": "(Opción de Desarrollador) Cuando está asignado en verdadero, información extra sobre errores se muestra en el UI.", "admin.service.developerTitle": "Habilitar modo de Desarrollador: ", @@ -329,6 +356,8 @@ "admin.service.listenAddress": "Dirección de escucha:", "admin.service.listenDescription": "La dirección a la que se unirá y escuchará. Ingresar \":8065\" se podrá unir a todas las interfaces o podrá seleccionar una como ej: \"127.0.0.1:8065\". Cambiando este valor es necesario reiniciar el servidor.", "admin.service.listenExample": "Ej \":8065\"", + "admin.service.mfaDesc": "Cuando es verdadero, los usuarios tendrán la opción de agregar autenticación de múltiples factores a sus cuentas. Necesitarán un teléfono inteligente y una app de autenticación como Google Authenticator.", + "admin.service.mfaTitle": "Habilitar Autenticación de Múltiples Factores:", "admin.service.mobileSessionDays": "Duración de la Sesión en Días para Dispositivos Moviles:", "admin.service.mobileSessionDaysDesc": "La sesión nativa de los dispositivos moviles expirará luego de transcurrido el numero de días especificado y se solicitará al usuario que inicie sesión nuevamente.", "admin.service.outWebhooksDesc": "Cuando es verdadero, los webhooks de salida serán permitidos.", @@ -488,6 +517,7 @@ "analytics.team.privateGroups": "Grupos Privados", "analytics.team.publicChannels": "Canales Públicos", "analytics.team.recentActive": "Usuarios Recientemente Activos", + "analytics.team.recentUsers": "Usuarios Recientemente Activos", "analytics.team.title": "Estádisticas del Equipo {team}", "analytics.team.totalPosts": "Total de Mensajes", "analytics.team.totalUsers": "Total de Usuarios", @@ -550,6 +580,12 @@ "authorize.app": "La app <strong>{appName}</strong> quiere tener la habilidad de accesar y modificar tu información básica.", "authorize.deny": "Denegar", "authorize.title": "Una aplicación quiere conectarse con tu cuenta de {teamName}", + "backstage_navbar.backToMattermost": "Volver a {siteName}", + "backstage_sidebar.integrations": "Integraciones", + "backstage_sidebar.integrations.add": "Agregar Integración", + "backstage_sidebar.integrations.add.incomingWebhook": "Webhook de Entrada", + "backstage_sidebar.integrations.add.outgoingWebhook": "Webhook de Salida", + "backstage_sidebar.integrations.installed": "Integraciones Instaladas", "center_panel.recent": "Pincha aquí para ir a los mensajes más recientes. ", "chanel_header.addMembers": "Agregar Miembros", "change_url.close": "Cerrar", @@ -626,6 +662,7 @@ "channel_notifications.preferences": "Preferencias de Notificación para ", "channel_notifications.sendDesktop": "Enviar notificaciones de escritorio", "channel_notifications.unreadInfo": "El nombre del canal está en negritas en la barra lateral cuando hay mensajes sin leer. Al elegir \"Sólo para menciones\" sólo lo dejará en negritas cuando seas mencionado.", + "channel_select.placeholder": "--- Selecciona un canal ---", "choose_auth_page.emailCreate": "Crea un nuevo equipo con tu cuenta de correo", "choose_auth_page.find": "Encontrar mi equipo", "choose_auth_page.gitlabCreate": "Crear un nuevo equipo con una cuenta de GitLab", @@ -671,6 +708,7 @@ "claim.oauth_to_email.pwdNotMatch": "Las contraseñas no coinciden.", "claim.oauth_to_email.switchTo": "Cambiar {type} a correo electrónico y contraseña", "claim.oauth_to_email.title": "Cambiar la cuenta de {type} a Correo Electrónico", + "claim.oauth_to_email_newPwd": "Ingresa una nueva contraseña para tu cuenta de {team} en {site}", "confirm_modal.cancel": "Cancelar", "create_comment.addComment": "Agregar un comentario...", "create_comment.comment": "Agregar Comentario", @@ -732,8 +770,9 @@ "file_upload.filesAbove": "No se pueden subir archivos de más de {max}MB: {filenames}", "file_upload.limited": "Se pueden subir un máximo de {count} archivos. Por favor envía otros mensajes para adjuntar más archivos.", "file_upload.pasted": "Imagen Pegada el ", - "filtered_user_list.count": "{count, number} {count, plural, one {Miembro} other {Miembros}}", - "filtered_user_list.countTotal": "{count, number} {count, plural, one {Miembro} other {Miembros}} de {total} Total", + "filtered_user_list.count": "{count} {count, plural, one {miembro} other {miembros}}", + "filtered_user_list.countTotal": "{count} {count, plural, one {miembro} other {miembros}} de {total} Total", + "filtered_user_list.member": "Miembro", "filtered_user_list.search": "Buscar miembros", "find_team.email": "Correo electrónico", "find_team.findDescription": "Enviamos un correo electrónico con los equipos a los que perteneces.", @@ -768,6 +807,16 @@ "get_team_invite_link_modal.help": "Envía el siguiente enlace a tus compañeros para que se registren a este equipo. El enlace de invitación al equipo puede ser compartido con multiples compañeros y el mismo no cambiará a menos que sea regenerado en la Configuración del Equipo por un Administrador del Equipo.", "get_team_invite_link_modal.helpDisabled": "La creación de usuario ha sido deshabilitada para tu equipo. Por favor solicita más detalles a tu administrador de equipo.", "get_team_invite_link_modal.title": "Enlace de Invitación al Equipo", + "installed_integrations.add": "Agregar Integración", + "installed_integrations.allFilter": "Todos ({count})", + "installed_integrations.delete": "Eliminar", + "installed_integrations.header": "Integraciones Instaladas", + "installed_integrations.incomingWebhookType": "(Webhook de Entrada)", + "installed_integrations.incomingWebhooksFilter": "Webhooks de Entrada ({count})", + "installed_integrations.outgoingWebhookType": "(Webhook de Salida)", + "installed_integrations.outgoingWebhooksFilter": "Webhooks de Salida ({count})", + "installed_integrations.regenToken": "Regenerar Token", + "installed_integrations.search": "Buscar Integraciones", "intro_messages.DM": "Este es el inicio de tu historial de mensajes directos con {teammate}.<br />Los mensajes directos y archivos que se comparten aquí no son mostrados a personas fuera de esta área.", "intro_messages.anyMember": " Cualquier miembro se puede unir y leer este canal.", "intro_messages.beginning": "Inicio de {name}", @@ -830,6 +879,10 @@ "login_ldap.pwdReq": "La contraseña LDAP es obligatoria", "login_ldap.signin": "Entrar", "login_ldap.username": "Usuario LDAP", + "login_mfa.enterToken": "Para completar el proceso de inicio de sesión, por favor ingresa el token provisto por el autenticador de tu teléfono inteligente", + "login_mfa.submit": "Enviar", + "login_mfa.token": "Token AMF", + "login_mfa.tokenReq": "Por favor ingresa un token AMF", "login_username.badTeam": "Mal nombre de equipo", "login_username.pwd": "Contraseña", "login_username.pwdReq": "La contraseña es obligatoria", @@ -873,6 +926,7 @@ "navbar_dropdown.console": "Consola de Sistema", "navbar_dropdown.create": "Crear nuevo Equipo", "navbar_dropdown.help": "Ayuda", + "navbar_dropdown.integrations": "Integraciones", "navbar_dropdown.inviteMember": "Invitar Nuevo Miembro", "navbar_dropdown.logout": "Cerrar sesión", "navbar_dropdown.manageMembers": "Administrar Miembros", @@ -888,7 +942,7 @@ "password_form.title": "Restablecer Contraseña", "password_form.update": "Tu contraseña ha sido actualizada satisfactoriamente.", "password_send.checkInbox": "Por favor revisa tu bandeja de entrada.", - "password_send.description": "Para restablecer tu contraseña, ingresa la dirección de correo electrónico que utilizaste para registrarte.", + "password_send.description": "Para reiniciar tu contraseña, ingresa la dirección de correo que utilizaste en el registro", "password_send.email": "Correo electrónico", "password_send.error": "Por favor ingresa una dirección correo electrónico válida.", "password_send.link": "<p>Se ha enviado un enlace para restablecer la contraseña a <b>{email}</b></p>", @@ -985,7 +1039,7 @@ "sidebar.pg": "Grupos Privados", "sidebar.removeList": "Remover de la lista", "sidebar.tutorialScreen1": "<h4>Canales</h4><p><strong>Canales</strong> organizan las conversaciones en diferentes tópicos. Son abiertos para cualquier persona de tu equipo. Para enviar comunicaciones privadas con una sola persona utiliza <strong>Mensajes Directos</strong> o con multiples personas utilizando <strong>Grupos Privados</strong>.</p>", - "sidebar.tutorialScreen2": "<h4>Los canal \"General\" y \"Fuera de Tópico\"</h4><p>Estos son dos canales para comenzar:</p><p><strong>General</strong> es el lugar para tener comunicación con todo el equipo. Todos los integrantes de tu equipo son miembros de este canal.</p><p><strong>Fuera de Tópico</strong> es un lugar para diversión y humor fuera de los canales relacionados con el trabajo. Tu y tu equipo pueden decidir que otros canales crear.</p>", + "sidebar.tutorialScreen2": "<h4>Los canal \"{townsquare}\" y \"{offtopic}\"</h4><p>Estos son dos canales para comenzar:</p><p><strong>{townsquare}</strong> es el lugar para tener comunicación con todo el equipo. Todos los integrantes de tu equipo son miembros de este canal.</p><p><strong>{offtopic}</strong> es un lugar para diversión y humor fuera de los canales relacionados con el trabajo. Tu y tu equipo pueden decidir que otros canales crear.</p>", "sidebar.tutorialScreen3": "<h4>Creando y Uniendose a Canales</h4><p>Pincha en <strong>\"Más...\"</strong> para crear un nuevo canal o unirte a uno existente.</p><p>También puedes crear un nuevo canal o grupo privado al pinchar el simbolo de <strong>\"+\"</strong> que se encuentra al lado del encabezado de Canales o Grupos Privados.</p>", "sidebar.unreadAbove": "Mensaje(s) sin leer ▲", "sidebar.unreadBelow": "Mensaje(s) sin leer ▼", @@ -1027,6 +1081,7 @@ "signup_user_completed.validEmail": "Por favor ingresa una dirección de correo electrónico válida", "signup_user_completed.welcome": "Bienvenido a:", "signup_user_completed.whatis": "¿Cuál es tu dirección de correo electrónico?", + "signup_user_completed.withLdap": "Con tus credenciales de LDAP", "sso_signup.find": "Encontrar mi equipo", "sso_signup.gitlab": "Crea un equipo con una cuenta de GitLab", "sso_signup.google": "Crea un equipo con una cuenta de Google Apps", @@ -1133,7 +1188,7 @@ "textbox.quote": ">cita", "textbox.strike": "tachado", "tutorial_intro.allSet": "Ya estás listo para comenzar", - "tutorial_intro.end": "Pincha “Siguiente” para entrar al Canal General. Este es el primer canal que ven tus compañeros cuando ingresan. Utilizalo para mandar mensajes que todos deben leer.", + "tutorial_intro.end": "Pincha “Siguiente” para entrar al {channel}. Este es el primer canal que ven tus compañeros cuando ingresan. Utilizalo para mandar mensajes que todos deben leer.", "tutorial_intro.invite": "Invitar compañeros", "tutorial_intro.next": "Siguiente", "tutorial_intro.screenOne": "<h3>Bienvenido a:</h3> <h1>Mattermost</h1> <p>Las comunicaciones de tu equipo en un solo lugar, con búsquedas instantáneas y disponible desde donde sea.</p> <p>Mantén a tu equipo conectado para ayudarlos a conseguir lo que realmente importa.</p>", @@ -1247,17 +1302,22 @@ "user.settings.general.close": "Cerrar", "user.settings.general.confirmEmail": "Confirmar Correo electrónico", "user.settings.general.email": "Correo electrónico", + "user.settings.general.emailGitlabCantUpdate": "El inicio de sesión ocurre a través GitLab. El correo electrónico no puede ser actualizado. La dirección de correo electrónico utilizada para las notificaciones es {email}.", "user.settings.general.emailHelp1": "El correo electrónico es utilizado para iniciar sesión, recibir notificaciones y para restablecer la contraseña. Si se cambia el correo electrónico deberás verificarlo nuevamente.", "user.settings.general.emailHelp2": "El correo ha sido deshabilitado por el administrador de sistemas. No llegarán correos de notificación hasta que se vuelva a habilitar.", "user.settings.general.emailHelp3": "El correo electrónico es utilizado para iniciar sesión, recibir notificaciones y para restablecer la contraseña.", "user.settings.general.emailHelp4": "Un correo de verificación ha sido enviado a {email}.", + "user.settings.general.emailLdapCantUpdate": "El inicio de sesión ocurre a través LDAP. El correo electrónico no puede ser actualizado. La dirección de correo electrónico utilizada para las notificaciones es {email}.", "user.settings.general.emailMatch": "El nuevo correo electrónico introducido no coincide.", + "user.settings.general.emptyName": "Pincha 'Editar' para agregar tu nombre completo", + "user.settings.general.emptyNickname": "Pincha 'Edita' para agregar un sobrenombre", "user.settings.general.firstName": "Nombre", "user.settings.general.fullName": "Nombre completo", "user.settings.general.imageTooLarge": "No se puede subir la imagen del perfil. El archivo es muy grande.", "user.settings.general.imageUpdated": "Última actualizacón de la imagen {date}", "user.settings.general.lastName": "Apellido", "user.settings.general.loginGitlab": "Inicio de sesión realizado a través de GitLab ({email})", + "user.settings.general.loginLdap": "Inicio de sesión realizado a través de LDAP ({email})", "user.settings.general.newAddress": "Nueva dirección: {email}<br />Revisa tu correo electrónico para verificar tu nueva dirección.", "user.settings.general.nickname": "Sobrenombre", "user.settings.general.nicknameExtra": "Utiliza un Sobrenombre por el cual te conocen que sea diferente de tu nombre y del nombre de tu usuario. Esto se utiliza con mayor frecuencia cuando dos o más personas tienen nombres y nombres de usuario que suenan similares.", @@ -1282,6 +1342,13 @@ "user.settings.integrations.commandsDescription": "Administra tus comandos de barra", "user.settings.integrations.title": "Configuraciones de Integración", "user.settings.languages.change": "Cambia el idioma con el que se muestra la intefaz de usuario", + "user.settings.mfa.add": "Agrega AMF a tu cuenta", + "user.settings.mfa.addHelp": "Para agregar autenticación de múltiples factores a tu cuenta debes tener un teléfono inteligente con Google Authenticator instalado.", + "user.settings.mfa.addHelpQr": "Por favor escanea el código QR con la app de Google Authenticator en tu teléfono inteligente e ingresa el token provisto por la app.", + "user.settings.mfa.enterToken": "Token", + "user.settings.mfa.qrCode": "Código QR", + "user.settings.mfa.remove": "Remover AMF de tu cuenta", + "user.settings.mfa.removeHelp": "Al remover la autenticación de múltples factores hará que tu cuenta sea vulnerable a ataques.", "user.settings.modal.advanced": "Avanzada", "user.settings.modal.confirmBtns": "Sí, Descartar", "user.settings.modal.confirmMsg": "Tienes cambios sin guardar, ¿Estás seguro que los quieres descartar?", @@ -1322,18 +1389,22 @@ "user.settings.security.emailPwd": "Correo electrónico y Contraseña", "user.settings.security.gitlab": "GitLab SSO", "user.settings.security.lastUpdated": "Última actualización {date} a las {time}", + "user.settings.security.loginGitlab": "Inicio de sesión realizado a través de Gitlab", + "user.settings.security.loginLdap": "Inicio de sesión realizado a través de LDAP", "user.settings.security.logoutActiveSessions": "Visualizar y cerrar las sesiones activas", "user.settings.security.method": "Método de inicio de sesión", "user.settings.security.newPassword": "Nueva Contraseña", "user.settings.security.oneSignin": "Sólo puedes tener un método de inicio de sesión a la vez. El cambio del método de inicio de sesión te enviará un correo notificandote que el cambio se realizó con éxito.", "user.settings.security.password": "Contraseña", + "user.settings.security.passwordGitlabCantUpdate": "El inicio de sesión ocurre a través GitLab. La contraseña no se puede actualizar.", + "user.settings.security.passwordLdapCantUpdate": "El inicio de sesión ocurre a través LDAP. La contraseña no se puede actualizar.", "user.settings.security.passwordLengthError": "La nueva contraseña debe contener al menos {chars} carácteres", "user.settings.security.passwordMatchError": "La nueva contraseña que ingresaste no coincide", "user.settings.security.retypePassword": "Reescribe la Nueva Contraseña", "user.settings.security.switchEmail": "Cambiar para utilizar correo electrónico y contraseña", "user.settings.security.switchGitlab": "Cambiar para utilizar GitLab SSO", "user.settings.security.switchGoogle": "Cambiar para utilizar Google SSO", - "user.settings.security.switchLda": "Cambiar a utilizar LDAP", + "user.settings.security.switchLdap": "Cambiar a utilizar LDAP", "user.settings.security.title": "Configuración de Seguridad", "user.settings.security.viewHistory": "Visualizar historial de acceso", "user_list.notFound": "No se encontraron usuarios :(", diff --git a/webapp/i18n/fr.json b/webapp/i18n/fr.json index f05987e15..be207f0da 100644 --- a/webapp/i18n/fr.json +++ b/webapp/i18n/fr.json @@ -986,7 +986,7 @@ "sidebar.pg": "Groupes privés", "sidebar.removeList": "Retirer de la liste", "sidebar.tutorialScreen1": "<h4>Canaux</h4><p><strong>Les canaux</strong> organisent les conversations en sujets distincts. Ils sont ouverts à tout le monde dans votre équipe. Pour envoyer des messages privés, utilisez <strong>Messages Privés</strong> pour une personne ou <strong>Groupes Privés</strong> pour plusieurs personnes.</p>", - "sidebar.tutorialScreen2": "<h4>Canaux \"Town Square\" et \"Off-Topic\"</h4><p>Voici deux canaux publics pour commencer :</p><p><strong>Town Square</strong> (\"centre-ville\") est l'endroit idéal pour communiquer avec toute l'équipe. Tous les membres de votre équipe sont membres de ce canal.</p><p><strong>Off-Topic</strong> (\"hors-sujet\") est l'endroit pour se détendre et parler d'autre chose que de travail. Vous et votre équipe décidez des autres canaux à créer.</p>", + "sidebar.tutorialScreen2": "<h4>Canaux \"{townsquare}\" et \"{offtopic}\"</h4><p>Voici deux canaux publics pour commencer :</p><p><strong>{townsquare}</strong> (\"centre-ville\") est l'endroit idéal pour communiquer avec toute l'équipe. Tous les membres de votre équipe sont membres de ce canal.</p><p><strong>{offtopic}</strong> (\"hors-sujet\") est l'endroit pour se détendre et parler d'autre chose que de travail. Vous et votre équipe décidez des autres canaux à créer.</p>", "sidebar.tutorialScreen3": "<h4>Créer et rejoindre des canaux</h4><p>Cliquez sur <strong>\"Plus...\"</strong> pour créer un nouveau canal ou rejoindre un canal existant.</p><p>Vous pouvez aussi créer un nouveau canal ou un groupe privé en cliquant sur le symbole <strong>\"+\"</strong> à côté du nom du canal ou de l'en-tête du groupe privé.</p>", "sidebar.unreadAbove": "Message(s) non-lu(s) ci-dessus", "sidebar.unreadBelow": "Message(s) non-lu(s) ci-dessous", @@ -1134,7 +1134,7 @@ "textbox.quote": ">citation", "textbox.strike": "barré", "tutorial_intro.allSet": "C'est parti !", - "tutorial_intro.end": "Cliquez sur \"Suivant\" pour entrer dans Town Square. C'est le premier canal que les membres voient quand ils s'inscrivent. Utilisez-le pour poster des messages que tout le monde doit lire en premier.", + "tutorial_intro.end": "Cliquez sur \"Suivant\" pour entrer dans {channel}. C'est le premier canal que les membres voient quand ils s'inscrivent. Utilisez-le pour poster des messages que tout le monde doit lire en premier.", "tutorial_intro.invite": "Inviter des membres", "tutorial_intro.next": "Suivant", "tutorial_intro.screenOne": "<h3>Bienvenue sur :</h3><h1>Mattermost</h1><p>Toute la communication de votre équipe à un seul endroit, Your team communication all in one place, instantanément consultable et disponible partout.</p><p>Gardez le lien avec votre équipe pour accomplir les tâches les plus importantes.</p>", diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt.json index 466d4b6ea..7525306e6 100644 --- a/webapp/i18n/pt.json +++ b/webapp/i18n/pt.json @@ -22,6 +22,28 @@ "activity_log_modal.android": "Android", "activity_log_modal.androidNativeApp": "App Nativo Android", "activity_log_modal.iphoneNativeApp": "App Nativo para iPhone", + "add_incoming_webhook.cancel": "Cancelar", + "add_incoming_webhook.channel": "Canal", + "add_incoming_webhook.channelRequired": "Um canal válido é necessário", + "add_incoming_webhook.description": "Descrição", + "add_incoming_webhook.header": "Adicionar Webhooks Entrada", + "add_incoming_webhook.name": "Nome", + "add_incoming_webhook.save": "Salvar", + "add_integration.header": "Adicionar Integração", + "add_integration.incomingWebhook.title": "Webhooks Entrada", + "add_integration.incomingWebhook.description": "Criar URLs webhook para usar em integrações externas", + "add_integration.outgoingWebhook.title": "Webhooks Saída", + "add_integration.outgoingWebhook.description": "Criar webhook para enviar novos eventos de mensagens para uma integração externa.", + "add_outgoing_webhook.callbackUrls": "URLs Callback (Uma Por Linha)", + "add_outgoing_webhook.callbackUrlsRequired": "Uma ou mais URLs callback são necessárias", + "add_outgoing_webhook.cancel": "Cancelar", + "add_outgoing_webhook.channel": "Canal", + "add_outgoing_webhook.description": "Descrição", + "add_outgoing_webhook.header": "Adicionar Webhooks Saída", + "add_outgoing_webhook.name": "Nome", + "add_outgoing_webhook.save": "Salvar", + "add_outgoing_webhook.triggerWOrds": "Palavras Gatilho (Uma Por Linha)", + "add_outgoing_webhook.triggerWordsOrChannelRequired": "Um canal válido ou uma lista de palavras gatilho é necessário", "admin.audits.reload": "Recarregar", "admin.audits.title": "Atividade de Usuário", "admin.compliance.directoryDescription": "Diretório o qual os relatórios compliance são gravados, Se estiver em branco, será usado ./data/.", @@ -306,6 +328,8 @@ "admin.select_team.close": "Fechar", "admin.select_team.select": "Selecionar", "admin.select_team.selectTeam": "Selecione Equipe", + "admin.service.mfaTitle": "Ativar Autenticação Multi-Fator:", + "admin.service.mfaDesc": "Quando verdadeiro, vai ser dada a opção do usuário adicionar autenticação multi-fator em sua conta. Eles irão precisar de um smartphone e um app autenticador como o Google Authenticator.", "admin.service.attemptDescription": "Tentativas de login permitidas antes que do usuário ser bloqueado e necessário redefinir a senha por e-mail.", "admin.service.attemptExample": "Ex \"10\"", "admin.service.attemptTitle": "Máxima Tentativas de Login:", @@ -550,6 +574,12 @@ "authorize.app": "O app <strong>{appName}</strong> gostaria de ter a capacidade de acessar e modificar suas informações básicas.", "authorize.deny": "Negar", "authorize.title": "Um aplicativo gostaria de conectar na sua conta {teamName}", + "backstage_navbar.backToMattermost": "Voltar para {siteName}", + "backstage_sidebar.integrations": "Integrações", + "backstage_sidebar.integrations.installed": "Integrações Instaladas", + "backstage_sidebar.integrations.add": "Adicionar Integração", + "backstage_sidebar.integrations.add.incomingWebhook": "Webhooks Entrada", + "backstage_sidebar.integrations.add.outgoingWebhook": "Webhooks Saída", "center_panel.recent": "Clique aqui para pular para mensagens recentes. ", "chanel_header.addMembers": "Adicionar Membros", "change_url.close": "Fechar", @@ -626,6 +656,7 @@ "channel_notifications.preferences": "Preferências de Notificação para ", "channel_notifications.sendDesktop": "Enviar notificações de desktop", "channel_notifications.unreadInfo": "O nome do canal fica em negrito na barra lateral quando houver mensagens não lidas. Selecionando \"Apenas menções\" o canal vai ficar em negrito apenas quando você for mencionado.", + "channel_select.placeholder": "--- Selecione um canal ---", "choose_auth_page.emailCreate": "Criar uma nova equipe com endereço de email", "choose_auth_page.find": "Encontrar minhas equipes", "choose_auth_page.gitlabCreate": "Criar uma equipe com uma conta GitLab", @@ -768,6 +799,16 @@ "get_team_invite_link_modal.help": "Enviar o link abaixo para sua equipe de trabalho para que eles se inscrevam no site da sua equipe. O Link de Convite de Equipe como ele não muda pode ser compartilhado com várias pessoas ao menos que seja re-gerado em Configurações de Equipe pelo Administrador de Equipe.", "get_team_invite_link_modal.helpDisabled": "Criação de usuários está desabilitada para sua equipe. Por favor peça ao administrador de equipe por detalhes.", "get_team_invite_link_modal.title": "Link para Convite de Equipe", + "installed_integrations.add": "Adicionar Integração", + "installed_integrations.allFilter": "Todos", + "installed_integrations.delete": "Deletar", + "installed_integrations.header": "Integrações Instaladas", + "installed_integrations.incomingWebhooksFilter": "Webhooks Entrada ({count})", + "installed_integrations.incomingWebhookType": "(Webhooks Entrada)", + "installed_integrations.outgoingWebhooksFilter": "Webhooks Saída ({count})", + "installed_integrations.outgoingWebhookType": "(Webhooks Saída)", + "installed_integrations.regenToken": "Regen Token", + "installed_integrations.search": "Pesquisar Integrações", "intro_messages.DM": "Este é o início do seu histórico de mensagens diretas com {teammate}.<br />Mensagens diretas e arquivos compartilhados aqui não são mostrados para pessoas de fora desta área.", "intro_messages.anyMember": " Qualquer membro pode participar e ler este canal.", "intro_messages.beginning": "Início do {name}", @@ -818,6 +859,10 @@ "login.session_expired": " Sua sessão expirou. Por favor faça login novamente.", "login.signTo": "Login em:", "login.verified": " Email Verificado", + "login_mfa.token": "Token MFA", + "login_mfa.enterToken": "Para completar o login em processo, por favor entre um token do seu autenticador no smartphone", + "login_mfa.submit": "Enviar", + "login_mfa.tokenReq": "Por favor entre um token MFA", "login_email.badTeam": "Nome ruim de equipe", "login_email.email": "E-mail", "login_email.emailReq": "Um email é necessário", @@ -873,6 +918,7 @@ "navbar_dropdown.console": "Console do Sistema", "navbar_dropdown.create": "Criar uma Nova Equipe", "navbar_dropdown.help": "Ajuda", + "navbar_dropdown.integrations": "Integrações", "navbar_dropdown.inviteMember": "Convidar Membros da Equipe", "navbar_dropdown.logout": "Logout", "navbar_dropdown.manageMembers": "Gerenciar Membros", @@ -985,7 +1031,7 @@ "sidebar.pg": "Grupos Privados", "sidebar.removeList": "Remover da lista", "sidebar.tutorialScreen1": "<h4>Canais</h4><p><strong>Canais</strong> organizar conversas em diferentes tópicos. Eles estão abertos a todos em sua equipe. Para enviar comunicações privadas utilize <strong>Mensagens Diretas</strong> para uma única pessoa ou <strong>Grupos Privados</strong> para várias pessoas.</p>", - "sidebar.tutorialScreen2": "<h4>Canais \"Town Square\" e \"Off-Topic\"</h4><p>Aqui estão dois canais públicos para começar:</p><p><strong>Town Square</strong> é um lugar comunicação de toda equipe. Todo mundo em sua equipe é um membro deste canal.</p><p><strong>Off-Topic</strong> é um lugar para diversão e humor fora dos canais relacionados com o trabalho. Você e sua equipe podem decidir qual outros canais serão criados.</p>", + "sidebar.tutorialScreen2": "<h4>Canais \"{townsquare}\" e \"{offtopic}\"</h4><p>Aqui estão dois canais públicos para começar:</p><p><strong>{townsquare}</strong> é um lugar comunicação de toda equipe. Todo mundo em sua equipe é um membro deste canal.</p><p><strong>{offtopic}</strong> é um lugar para diversão e humor fora dos canais relacionados com o trabalho. Você e sua equipe podem decidir qual outros canais serão criados.</p>", "sidebar.tutorialScreen3": "<h4>Criando e participando de Canais</h4><p>Clique em <strong>\"Mais...\"</strong> para criar um novo canal ou participar de um já existente.</p><p>Você também pode criar um novo canal ou grupo privado ao clicar em <strong>no símbolo \"+\"</strong> ao lado do canal ou grupo privado no cabeçalho.</p>", "sidebar.unreadAbove": "Post(s) não lidos abaixo", "sidebar.unreadBelow": "Post(s) não lidos abaixo", @@ -1133,7 +1179,7 @@ "textbox.quote": ">citar", "textbox.strike": "tachado", "tutorial_intro.allSet": "Está tudo pronto", - "tutorial_intro.end": "Clique em “Próximo” para entrar Town Square. Este é o primeiro canal que sua equipe de trabalho vê quando eles se inscrevem. Use para postar atualizações que todos precisam saber.", + "tutorial_intro.end": "Clique em “Próximo” para entrar {channel}. Este é o primeiro canal que sua equipe de trabalho vê quando eles se inscrevem. Use para postar atualizações que todos precisam saber.", "tutorial_intro.invite": "Convidar pessoas para equipe", "tutorial_intro.next": "Próximo", "tutorial_intro.screenOne": "<h3>Bem vindo ao:</h3><h1>Mattermost</h1><p>Sua equipe de comunicação em um só lugar, pesquisas instantâneas disponível em qualquer lugar</p><p>Mantenha sua equipe conectada para ajudá-los a conseguir o que mais importa.</p>", @@ -1238,6 +1284,7 @@ "user.settings.display.theme.customTheme": "Tema Customizado", "user.settings.display.theme.describe": "Abrir para gerenciar seu tema", "user.settings.display.theme.import": "Importar tema de cores do Slack", + "user.settings.display.theme.otherThemes": "Veja outros temas", "user.settings.display.theme.themeColors": "Tema de Cores", "user.settings.display.theme.title": "Tema", "user.settings.display.title": "Configurações de Exibição", @@ -1246,17 +1293,22 @@ "user.settings.general.close": "Fechar", "user.settings.general.confirmEmail": "Confirmar o email", "user.settings.general.email": "E-mail", + "user.settings.general.emailGitlabCantUpdate": "Login ocorre através do GitLab. Email não pode ser atualizado. Endereço de email utilizado para notificações é {email}.", + "user.settings.general.emailLdapCantUpdate": "Login ocorre através de LDAP. Email não pode ser atualizado. Endereço de email utilizado para notificações é {email}.", "user.settings.general.emailHelp1": "Email é usado para login, notificações, e redefinição de senha. Requer verificação de email se alterado.", "user.settings.general.emailHelp2": "Email foi desativado pelo seu administrador de sistema. Nenhuma notificação por email será enviada até isto ser habilitado.", "user.settings.general.emailHelp3": "Email é usado para login, notificações e redefinição de senha.", "user.settings.general.emailHelp4": "Uma verificação por email foi enviada para {email}.", "user.settings.general.emailMatch": "Os novos emails que você inseriu não correspondem.", + "user.settings.general.emptyName": "Clique 'Editar' para adicionar seu nome completo", + "user.settings.general.emptyNickname": "Clique 'Editar' para adicionar um apelido", "user.settings.general.firstName": "Primeiro nome", "user.settings.general.fullName": "Nome Completo", "user.settings.general.imageTooLarge": "Não é possível fazer upload da imagem de perfil. O arquivo é muito grande.", "user.settings.general.imageUpdated": "Imagem última atualização {date}", "user.settings.general.lastName": "Último Nome", "user.settings.general.loginGitlab": "Login feito através do GitLab ({email})", + "user.settings.general.loginLdap": "Login feito através de LDAP ({email})", "user.settings.general.newAddress": "Novo Endereço: {email}<br />Verifique seu email para checar o endereço acima.", "user.settings.general.nickname": "Apelido", "user.settings.general.nicknameExtra": "Use Apelidos para um nome você pode ser chamado assim, isso é diferente de seu primeiro nome e nome de usuário. Este é mais frequentemente usado quando duas ou mais pessoas têm nomes semelhantes de usuário.", @@ -1321,11 +1373,15 @@ "user.settings.security.emailPwd": "Email e Senha", "user.settings.security.gitlab": "GitLab SSO", "user.settings.security.lastUpdated": "Última atualização {date} {time}", + "user.settings.security.loginGitlab": "Login feito através do GitLab", + "user.settings.security.loginLdap": "Login feito através de LDAP", "user.settings.security.logoutActiveSessions": "Ver e fazer Logout das Sessões Ativas", "user.settings.security.method": "Método de Login", "user.settings.security.newPassword": "Nova Senha", "user.settings.security.oneSignin": "Você pode ter somente um método de login por vez. Trocando o método de login será enviado um email de notificação se você alterar com sucesso.", "user.settings.security.password": "Senha", + "user.settings.security.passwordGitlabCantUpdate": "Login ocorreu através do GitLab. Senha não pode ser atualizada.", + "user.settings.security.passwordLdapCantUpdate": "Login ocorreu através de LDAP. Senha não pode ser atualizada.", "user.settings.security.passwordLengthError": "Novas senhas precisam ter pelo menos {chars} characters", "user.settings.security.passwordMatchError": "As novas senhas que você inseriu não correspondem", "user.settings.security.retypePassword": "Digite Novamente a nova Senha", @@ -1347,4 +1403,4 @@ "web.footer.terms": "Termos", "web.header.back": "Voltar", "web.root.singup_info": "Toda comunicação em um só lugar, pesquisável e acessível em qualquer lugar" -} +}
\ No newline at end of file diff --git a/webapp/root.jsx b/webapp/root.jsx index 112f0880e..417a13659 100644 --- a/webapp/root.jsx +++ b/webapp/root.jsx @@ -10,7 +10,7 @@ import 'sass/styles.scss'; import React from 'react'; import ReactDOM from 'react-dom'; -import {Router, Route, IndexRoute, IndexRedirect, browserHistory} from 'react-router'; +import {Router, Route, IndexRoute, IndexRedirect, Redirect, browserHistory} from 'react-router'; import Root from 'components/root.jsx'; import LoggedIn from 'components/logged_in.jsx'; import NotLoggedIn from 'components/not_logged_in.jsx'; @@ -28,6 +28,7 @@ import BrowserStore from 'stores/browser_store.jsx'; import SignupTeam from 'components/signup_team.jsx'; import * as Client from 'utils/client.jsx'; import * as Websockets from 'action_creators/websocket_actions.jsx'; +import * as Utils from 'utils/utils.jsx'; import * as GlobalActions from 'action_creators/global_actions.jsx'; import SignupTeamConfirm from 'components/signup_team_confirm.jsx'; import SignupUserComplete from 'components/signup_user_complete.jsx'; @@ -41,6 +42,7 @@ import InstalledIntegrations from 'components/backstage/installed_integrations.j import AddIntegration from 'components/backstage/add_integration.jsx'; import AddIncomingWebhook from 'components/backstage/add_incoming_webhook.jsx'; import AddOutgoingWebhook from 'components/backstage/add_outgoing_webhook.jsx'; +import ErrorPage from 'components/error_page.jsx'; import SignupTeamComplete from 'components/signup_team_complete/components/signup_team_complete.jsx'; import WelcomePage from 'components/signup_team_complete/components/team_signup_welcome_page.jsx'; @@ -61,6 +63,13 @@ import Login from 'components/login/login.jsx'; import * as I18n from 'i18n/i18n.jsx'; +const notFoundParams = { + title: Utils.localizeMessage('error.not_found.title', 'Page not found'), + message: Utils.localizeMessage('error.not_found.message', 'The page you where trying to reach does not exist'), + link: '/', + linkmessage: Utils.localizeMessage('error.not_found.link_message', 'Back to Mattermost') +}; + // This is for anything that needs to be done for ALL react components. // This runs before we start to render anything. function preRenderSetup(callwhendone) { @@ -201,6 +210,10 @@ function renderRootComponent() { component={Root} > <Route + path='error' + component={ErrorPage} + /> + <Route component={LoggedIn} onEnter={preLoggedIn} > @@ -267,6 +280,11 @@ function renderRootComponent() { }} /> </Route> + <Redirect + from='*' + to='/error' + query={notFoundParams} + /> </Route> <Route path='admin_console' @@ -362,6 +380,11 @@ function renderRootComponent() { component={LDAPToEmail} /> </Route> + <Redirect + from='*' + to='/error' + query={notFoundParams} + /> </Route> </Route> </Route> diff --git a/webapp/sass/layout/_forms.scss b/webapp/sass/layout/_forms.scss index 259beeb57..1dd2bb827 100644 --- a/webapp/sass/layout/_forms.scss +++ b/webapp/sass/layout/_forms.scss @@ -12,7 +12,7 @@ text-align: left; &.light { - color: $dark-gray; + @include opacity(.6); font-size: 1.05em; font-style: italic; font-weight: normal; diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss index e2bce5562..947a81318 100644 --- a/webapp/sass/layout/_post.scss +++ b/webapp/sass/layout/_post.scss @@ -644,6 +644,15 @@ body.ios { .post__img { width: 46px; + svg { + height: 36px; + width: 36px; + } + + path { + fill: inherit; + } + img { @include border-radius(50px); height: 36px; diff --git a/webapp/sass/responsive/_desktop.scss b/webapp/sass/responsive/_desktop.scss index 1ae4b6b70..3b36fb75f 100644 --- a/webapp/sass/responsive/_desktop.scss +++ b/webapp/sass/responsive/_desktop.scss @@ -48,6 +48,11 @@ } } + .backstage-content { + margin: 46px 46px 46px 150px; + } + + .inner-wrap { &.move--left { .file-overlay { diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss index 0e1a471cf..38476485d 100644 --- a/webapp/sass/responsive/_mobile.scss +++ b/webapp/sass/responsive/_mobile.scss @@ -1,6 +1,16 @@ @charset 'UTF-8'; @media screen and (max-width: 768px) { + .backstage-filters { + display: block; + + .backstage-filter__search { + border-bottom: 1px solid $light-gray; + margin: 10px 0; + width: 100%; + } + } + .signup-team__container { font-size: 1em; } @@ -675,9 +685,9 @@ } .sidebar--right { - width: 100%; - right: 0; @include translate3d(100%, 0, 0); + right: 0; + width: 100%; z-index: 5; &.move--left { @@ -786,6 +796,40 @@ } @media screen and (max-width: 640px) { + .modal { + .about-modal { + .about-modal__content { + display: block; + } + + .about-modal__hash { + p { + word-break: break-all; + + &:first-child { + float: none; + } + } + } + + .about-modal__logo { + float: none; + padding: 0; + text-align: center; + width: 100%; + + svg { + height: 100px; + width: 100px; + } + } + + .about-modal__logo + div { + padding: 2em 0 0; + } + } + } + .access-history__table { > div { display: block; @@ -819,6 +863,30 @@ } @media screen and (max-width: 480px) { + .backstage-header { + h1 { + float: none; + margin-bottom: 15px; + } + + .add-integrations-link { + float: none; + } + } + + .add-integration { + width: 100%; + } + + .backstage-list__item { + display: block; + + .actions { + margin-top: 10px; + padding: 0; + } + } + .modal { .settings-modal { .settings-table { diff --git a/webapp/sass/responsive/_tablet.scss b/webapp/sass/responsive/_tablet.scss index 6863e660b..db2a8d7b9 100644 --- a/webapp/sass/responsive/_tablet.scss +++ b/webapp/sass/responsive/_tablet.scss @@ -15,6 +15,19 @@ } } + .backstage-content { + margin: 30px; + max-width: 100%; + padding: 0; + } + + .backstage-sidebar { + height: auto; + padding: 30px 15px 0; + position: relative; + width: 100%; + } + .help__format-text { display: none; } diff --git a/webapp/sass/routes/_about-modal.scss b/webapp/sass/routes/_about-modal.scss new file mode 100644 index 000000000..98119c8aa --- /dev/null +++ b/webapp/sass/routes/_about-modal.scss @@ -0,0 +1,78 @@ +@charset 'UTF-8'; + +.modal { + .about-modal { + .modal-header { + background: transparent; + border: none; + color: inherit; + padding: 20px 25px 0; + + .close { + color: inherit; + font-weight: normal; + right: 15px; + } + + .modal-title { + color: inherit; + font-size: 16px; + } + } + + .modal-body { + padding: 20px 25px 5px; + } + + .about-modal__content { + @include clearfix; + @include display-flex; + @include flex-direction(row); + padding: 1em 0 3em; + } + + .about-modal__copyright { + @include opacity(.6); + margin-top: .5em; + } + + .about-modal__footer { + font-size: 13.5px; + } + + .about-modal__title { + line-height: 1.5; + margin: 0 0 10px; + } + + .about-modal__subtitle { + @include opacity(.6); + } + + .about-modal__hash { + @include opacity(.4); + font-size: .75em; + text-align: right; + + p { + &:first-child { + float: left; + } + } + } + + .about-modal__logo { + @include opacity(.9); + padding: 0 40px 0 20px; + + svg { + height: 125px; + width: 125px; + } + + path { + fill: inherit; + } + } + } +} diff --git a/webapp/sass/routes/_backstage.scss b/webapp/sass/routes/_backstage.scss index 70bab99cb..729c8c912 100644 --- a/webapp/sass/routes/_backstage.scss +++ b/webapp/sass/routes/_backstage.scss @@ -1,103 +1,106 @@ -.backstage { - background-color: #f2f2f2; +.backstage-content { + background-color: $bg--gray; height: 100%; - padding-left: 260px; - padding-top: 45px; + margin: 46px auto; + max-width: 960px; + padding-left: 135px; } -.backstage__navbar { - background: white; - border-bottom: lightgray 1px solid; - margin: 0 -15px; - padding: 10px; +.backstage-navbar { + background: $white; + border-bottom: 1px solid $light-gray; + padding: 10px 20px; z-index: 10; } -.backstage__navbar__back { - color: black; +.backstage-navbar__back { + color: inherit; + text-decoration: none; .fa { + font-size: 1.1em; font-weight: bold; - margin-right: 5px; + margin-right: 7px; + } + + &:hover, + &:active { + color: inherit; } } -.backstage__sidebar { - position: absolute; +.backstage-sidebar { + height: 100%; left: 0; + padding: 50px 20px; + position: absolute; width: 260px; - height: 100%; - background-color: #f2f2f2; - padding-bottom: 20px; - padding-left: 20px; - padding-right: 20px; - padding-top: 45px; z-index: 5; ul { - padding: 0px; list-style: none; + padding: 0; } } -.backstage__sidebar__category { - border: lightgray 1px solid; +.backstage-sidebar__category { + border: 1px solid $light-gray; .category-title { - color: gray; display: block; - padding: 5px 10px; + line-height: 36px; + padding: 0 10px; position: relative; } .category-title--active { - color: black; + color: $black; } .category-title__text { - position: absolute; left: 2em; + position: absolute; } .sections { - background: white; - border-top: lightgray 1px solid; + background: $white; + border-top: 1px solid $light-gray; } - .section-title { + .section-title, + .subsection-title { display: block; + font-size: .95em; + line-height: 29px; padding-left: 2em; - } - - .subsection { + text-decoration: none; } .subsection-title { - display: block; padding-left: 3em; } - .section-title--active, .subsection-title--active { - background-color:#2388d6; - color: white; + .section-title--active, + .subsection-title--active { + background-color: $primary-color; + color: $white; + font-weight: 600; } } .backstage__sidebar__category + .backstage__sidebar__category { - border-top-width: 0px; + border-top-width: 0; } -.installed-integrations { - height: 100%; - max-width: 958px; -} - -.backstage__header { +.backstage-header { + @include clearfix; margin-bottom: 20px; width: 100%; - .text { - display: inline; + h1 { + float: left; + font-size: 20px; + margin: 5px 0; } .add-integrations-link { @@ -105,21 +108,22 @@ } } -.installed-integrations__filters { +.backstage-filters { display: flex; flex-direction: row; - margin-bottom: 8px; width: 100%; - .type-filters { + .backstage-filters__sort { flex-grow: 1; flex-shrink: 0; + line-height: 30px; - .type-filter { - &--selected { - color: black; + .filter-sort { + text-decoration: none; + + &.filter-sort--active { + color: inherit; cursor: default; - font-weight: bold; } } @@ -129,84 +133,135 @@ } } - .filter-box { + .backstage-filter__search { flex-grow: 0; flex-shrink: 0; + position: relative; + width: 270px; + + .fa { + @include opacity(.4); + left: 11px; + position: absolute; + top: 11px; + } + + input { + background: $white; + border-bottom: none; + padding-left: 30px; + } } } -.installed-integrations__list { - background-color: white; - border: lightgray 1px solid; - padding-bottom: 30px; - padding-left: 30px; - padding-right: 30px; - padding-top: 10px; +.backstage-list { + background-color: $white; + border: 1px solid $light-gray; + padding: 5px 15px; } -.installed-integrations__item { - border-bottom: lightgray 1px solid; +.backstage-list__item { + border-bottom: 1px solid $light-gray; display: flex; - padding: 20px; + padding: 20px 15px; + + &:last-child { + border: none; + } - .details { + .item-details { flex-grow: 1; flex-shrink: 1; overflow: hidden; text-overflow: ellipsis; + } - .details-row + .details-row { - margin-top: 15px; - } + .item-details__row + .item-details__row { + @include clearfix; + margin-top: 10px; + text-overflow: ellipsis; + } - .name { - font-weight: bold; - margin-bottom: 1em; - } + .item-details__name { + font-weight: 600; + margin-bottom: 1em; + } - .type { - margin-left: 6px; - } + .item-details__type { + margin-left: 6px; + } - .description { - color: gray; - margin-bottom: 1em; - } + .item-details__description { + color: $dark-gray; + margin-bottom: 1em; } - .actions { + .list-item__actions { flex-grow: 0; flex-shrink: 0; padding-left: 20px; } } -.add-integration-option { - background-color: white; - border: lightgray 1px solid; +// Backstage Form + +.backstage-form { + background-color: $white; + border: 1px solid $light-gray; + padding: 40px 30px 30px; + + label { + font-weight: normal; + } + + .form-control { + background: $white; + + &:focus { + border-color: $primary-color; + } + } +} + +.backstage-form__footer { + border-top: 1px solid $light-gray; + margin-top: 2.5em; + padding-top: 1.8em; + text-align: right; + + .has-error { + float: left; + margin: 0; + } +} + +.add-integration { + background-color: $white; + border: 1px solid $light-gray; display: inline-block; height: 210px; - margin-right: 30px; + margin: 0 30px 20px 0; padding: 20px; text-align: center; + vertical-align: top; width: 250px; &:hover { color: default; text-decoration: none; } +} - &__image { - width: 80px; - height: 80px; - } +.add-integration__image { + height: 80px; + width: 80px; +} - &__title { - color: black; - margin-bottom: 10px; - } +.add-integration__title { + color: $black; + margin-bottom: 10px; +} - &__description { - color: gray; - } +.add-integration__description { + color: $dark-gray; } diff --git a/webapp/sass/routes/_module.scss b/webapp/sass/routes/_module.scss index b76dd147f..4f3f6f9cd 100644 --- a/webapp/sass/routes/_module.scss +++ b/webapp/sass/routes/_module.scss @@ -1,4 +1,5 @@ // Only for combining all the files in this folder +@import 'about-modal'; @import 'access-history'; @import 'activity-log'; @import 'admin-console'; diff --git a/webapp/sass/utils/_variables.scss b/webapp/sass/utils/_variables.scss index 345ab11e8..53004520e 100644 --- a/webapp/sass/utils/_variables.scss +++ b/webapp/sass/utils/_variables.scss @@ -8,7 +8,7 @@ $white: rgb(255, 255, 255); $black: rgb(0, 0, 0); $red: rgb(229, 101, 101); $yellow: rgb(255, 255, 0); -$light-gray: rgba(0, 0, 0, .06); +$light-gray: rgba(0, 0, 0, .15); $gray: rgba(0, 0, 0, .3); $dark-gray: rgba(0, 0, 0, .5); diff --git a/webapp/stores/post_store.jsx b/webapp/stores/post_store.jsx index f328ca306..3f2f75796 100644 --- a/webapp/stores/post_store.jsx +++ b/webapp/stores/post_store.jsx @@ -96,7 +96,7 @@ class PostStoreClass extends EventEmitter { let post = null; if (posts.posts.hasOwnProperty(postId)) { - post = Object.assign({}, posts.posts[postId]); + post = posts.posts[postId]; } return post; @@ -104,7 +104,7 @@ class PostStoreClass extends EventEmitter { getAllPosts(id) { if (this.postsInfo.hasOwnProperty(id)) { - return Object.assign({}, this.postsInfo[id].postList); + return this.postsInfo[id].postList; } return null; diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index 68df771f9..d01163b31 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -192,7 +192,9 @@ export default { MOBILE_VIDEO_WIDTH: 480, MOBILE_VIDEO_HEIGHT: 360, DEFAULT_CHANNEL: 'town-square', + DEFAULT_CHANNEL_UI_NAME: 'Town Square', OFFTOPIC_CHANNEL: 'off-topic', + OFFTOPIC_CHANNEL_UI_NAME: 'Off-Topic', GITLAB_SERVICE: 'gitlab', GOOGLE_SERVICE: 'google', EMAIL_SERVICE: 'email', @@ -246,6 +248,7 @@ export default { OPEN_TEAM: 'O', MAX_POST_LEN: 4000, EMOJI_SIZE: 16, + MATTERMOST_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'viewBox='0 0 500 500' style='enable-background:new 0 0 500 500;' xml:space='preserve'> <style type='text/css'> .st0{fill-rule:evenodd;clip-rule:evenodd;fill:#222222;} </style> <g id='XMLID_1_'> <g id='XMLID_3_'> <path id='XMLID_4_' class='st0' d='M396.9,47.7l2.6,53.1c43,47.5,60,114.8,38.6,178.1c-32,94.4-137.4,144.1-235.4,110.9 S51.1,253.1,83,158.7C104.5,95.2,159.2,52,222.5,40.5l34.2-40.4C150-2.8,49.3,63.4,13.3,169.9C-31,300.6,39.1,442.5,169.9,486.7 s272.6-25.8,316.9-156.6C522.7,223.9,483.1,110.3,396.9,47.7z'/> </g> <path id='XMLID_2_' class='st0' d='M335.6,204.3l-1.8-74.2l-1.5-42.7l-1-37c0,0,0.2-17.8-0.4-22c-0.1-0.9-0.4-1.6-0.7-2.2 c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.2c-0.7-1.2-1.8-2.1-3.1-2.6c-1.4-0.5-2.9-0.4-4.2,0.2c0,0-0.1,0-0.1,0 c-0.2,0.1-0.3,0.1-0.4,0.2c-0.6,0.3-1.2,0.7-1.8,1.3c-3,3-13.7,17.2-13.7,17.2l-23.2,28.8l-27.1,33l-46.5,57.8 c0,0-21.3,26.6-16.6,59.4s29.1,48.7,48,55.1c18.9,6.4,48,8.5,71.6-14.7C336.4,238.4,335.6,204.3,335.6,204.3z'/> </g> </svg>", ONLINE_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-243 245 12 12'style='enable-background:new -243 245 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <path class='online--icon' d='M-236,250.5C-236,250.5-236,250.5-236,250.5C-236,250.5-236,250.5-236,250.5C-236,250.5-236,250.5-236,250.5z'/> <ellipse class='online--icon' cx='-238.5' cy='248' rx='2.5' ry='2.5'/> </g> <path class='online--icon' d='M-238.9,253.8c0-0.4,0.1-0.9,0.2-1.3c-2.2-0.2-2.2-2-2.2-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5c0,0.1-0.1,0.5,0,0.6 c0.2,1.3,2.2,2.3,4.4,2.4c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0C-238.7,255.7-238.9,254.8-238.9,253.8z'/> <g> <g> <path class='online--icon' d='M-232.3,250.1l1.3,1.3c0,0,0,0.1,0,0.1l-4.1,4.1c0,0,0,0-0.1,0c0,0,0,0,0,0l-2.7-2.7c0,0,0-0.1,0-0.1l1.2-1.2 c0,0,0.1,0,0.1,0l1.4,1.4l2.9-2.9C-232.4,250.1-232.3,250.1-232.3,250.1z'/> </g> </g> </svg>", AWAY_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-299 391 12 12'style='enable-background:new -299 391 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <ellipse class='away--icon' cx='-294.6' cy='394' rx='2.5' ry='2.5'/> <path class='away--icon' d='M-293.8,399.4c0-0.4,0.1-0.7,0.2-1c-0.3,0.1-0.6,0.2-1,0.2c-2.5,0-2.5-2-2.5-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5 c0,0.1-0.1,0.5,0,0.6c0.2,1.3,2.2,2.3,4.4,2.4c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0.7,0,1.4-0.1,2-0.3 C-293.3,401.5-293.8,400.5-293.8,399.4z'/> </g> <path class='away--icon' d='M-287,400c0,0.1-0.1,0.1-0.1,0.1l-4.9,0c-0.1,0-0.1-0.1-0.1-0.1v-1.6c0-0.1,0.1-0.1,0.1-0.1l4.9,0c0.1,0,0.1,0.1,0.1,0.1 V400z'/> </svg>", OFFLINE_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-299 391 12 12'style='enable-background:new -299 391 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <g> <ellipse class='offline--icon' cx='-294.5' cy='394' rx='2.5' ry='2.5'/> <path class='offline--icon' d='M-294.3,399.7c0-0.4,0.1-0.8,0.2-1.2c-0.1,0-0.2,0-0.4,0c-2.5,0-2.5-2-2.5-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5 c0,0.1-0.1,0.5,0,0.6c0.2,1.3,2.2,2.3,4.4,2.4h0.1h0.1c0.3,0,0.7,0,1-0.1C-293.9,401.6-294.3,400.7-294.3,399.7z'/> </g> </g> <g> <path class='offline--icon' d='M-288.9,399.4l1.8-1.8c0.1-0.1,0.1-0.3,0-0.3l-0.7-0.7c-0.1-0.1-0.3-0.1-0.3,0l-1.8,1.8l-1.8-1.8c-0.1-0.1-0.3-0.1-0.3,0 l-0.7,0.7c-0.1,0.1-0.1,0.3,0,0.3l1.8,1.8l-1.8,1.8c-0.1,0.1-0.1,0.3,0,0.3l0.7,0.7c0.1,0.1,0.3,0.1,0.3,0l1.8-1.8l1.8,1.8 c0.1,0.1,0.3,0.1,0.3,0l0.7-0.7c0.1-0.1,0.1-0.3,0-0.3L-288.9,399.4z'/> </g> </svg>", diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx index b248368fc..3b7583f15 100644 --- a/webapp/utils/utils.jsx +++ b/webapp/utils/utils.jsx @@ -14,7 +14,6 @@ var ActionTypes = Constants.ActionTypes; import * as Client from './client.jsx'; import * as AsyncClient from './async_client.jsx'; import * as client from './client.jsx'; -import Autolinker from 'autolinker'; import React from 'react'; import {browserHistory} from 'react-router'; @@ -314,14 +313,8 @@ export function getTimestamp() { } // extracts links not styled by Markdown -export function extractLinks(text) { - text; // eslint-disable-line no-unused-expressions - Autolinker; // eslint-disable-line no-unused-expressions - - // skip this operation because autolinker is having issues - return []; - - /*const links = []; +export function extractFirstLink(text) { + const pattern = /(^|[\s\n]|<br\/?>)((?:https?|ftp):\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/i; let inText = text; // strip out code blocks @@ -330,32 +323,12 @@ export function extractLinks(text) { // strip out inline markdown images inText = inText.replace(/!\[[^\]]*\]\([^\)]*\)/g, ''); - function replaceFn(autolinker, match) { - let link = ''; - const matchText = match.getMatchedText(); - - if (matchText.trim().indexOf('http') === 0) { - link = matchText; - } else { - link = 'http://' + matchText; - } - - links.push(link); + const match = pattern.exec(inText); + if (match) { + return match[0].trim(); } - Autolinker.link( - inText, - { - replaceFn, - urls: {schemeMatches: true, wwwMatches: true, tldMatches: false}, - emails: false, - twitter: false, - phone: false, - hashtag: false - } - ); - - return links;*/ + return ''; } export function escapeRegExp(string) { @@ -756,6 +729,7 @@ export function applyTheme(theme) { changeCss('.search-help-popover .search-autocomplete__item.selected', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); changeCss('::-webkit-scrollbar-thumb', 'background:' + changeOpacity(theme.centerChannelColor, 0.4), 1); changeCss('body', 'scrollbar-arrow-color:' + theme.centerChannelColor, 4); + changeCss('.modal .about-modal .about-modal__logo svg, .post .post__img svg', 'fill:' + theme.centerChannelColor, 1); } if (theme.newMessageSeparator) { @@ -1405,3 +1379,18 @@ export function localizeMessage(id, defaultMessage) { return id; } + +export function getProfilePicSrcForPost(post, timestamp) { + let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp; + if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { + if (post.props.override_icon_url) { + src = post.props.override_icon_url; + } else { + src = Constants.DEFAULT_WEBHOOK_LOGO; + } + } else if (isSystemMessage(post)) { + src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE; + } + + return src; +} diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js index 478c5de81..4e2d6b70d 100644 --- a/webapp/webpack.config.js +++ b/webapp/webpack.config.js @@ -77,7 +77,9 @@ var config = { }), htmlExtract, new CopyWebpackPlugin([ - {from: 'images/emoji', to: 'emoji'} + {from: 'images/emoji', to: 'emoji'}, + {from: 'images/logo-email.png', to: 'images'}, + {from: 'images/circles.png', to: 'images'} ]), new webpack.LoaderOptionsPlugin({ minimize: !DEV, |