Этот коммит содержится в:
Asaad Mahmood
2015-07-29 19:29:41 +05:00
родитель ac8706b9f7 54265271fa
Коммит 5f2aa88a08
26 изменённых файлов: 599 добавлений и 178 удалений

Просмотреть файл

@@ -5,6 +5,7 @@ GOFLAGS ?= $(GOFLAGS:)
BUILD_NUMBER ?= $(BUILD_NUMBER:) BUILD_NUMBER ?= $(BUILD_NUMBER:)
GO=$(GOPATH)/bin/godep go GO=$(GOPATH)/bin/godep go
ESLINT=web/react/node_modules/eslint/bin/eslint.js
ifeq ($(BUILD_NUMBER),) ifeq ($(BUILD_NUMBER),)
BUILD_NUMBER := dev BUILD_NUMBER := dev
@@ -62,6 +63,14 @@ install:
@cd web/react/ && npm install @cd web/react/ && npm install
check: install
@echo Running ESLint...
@$(ESLINT) web/react/components/*
@$(ESLINT) web/react/dispatcher/*
@$(ESLINT) web/react/pages/*
@$(ESLINT) web/react/stores/*
@$(ESLINT) web/react/utils/*
test: install test: install
@mkdir -p logs @mkdir -p logs
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1 @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1

167
STYLE-GUIDE.md Обычный файл
Просмотреть файл

@@ -0,0 +1,167 @@
# Mattermost Style Guide
1. [GO](#go)
2. [Javascript](#javascript)
3. [React-JSX](#react-jsx)
## Go
All go code must follow the golang official [Style Guide](https://golang.org/doc/effective_go.html)
In addition all code must be run though the official go formatter tool [gofmt](https://golang.org/cmd/gofmt/)
## Javascript
Part of the build process is running ESLint. ESLint is the final authority on all style issues. PRs will not be accepted unless there are no errors or warnings running ESLint. The ESLint configuration file can be found in: [web/react/.eslintrc](https://github.com/mattermost/platform/blob/master/web/react/.eslintrc.json)
Instructions on how to use ESLint with your favourite editor can be found here: [http://eslint.org/docs/user-guide/integrations](http://eslint.org/docs/user-guide/integrations)
The following is an abridged version of the [Airbnb Javascript Style Guide](https://github.com/airbnb/javascript/blob/master/README.md#airbnb-javascript-style-guide-), with modifications. Anything that is unclear here follow that guide. If there is a conflict, follow what is said below.
### Whitespace
- Indentation is four spaces
- Use a space before the leading brace
- Use one space between the comma and the next argument in a bracketed list. No other space.
- Use whitespace to make code more readable.
- Do not use more than one newline to separate code blocks.
- Do not use a newline as the first line of a function
```javascript
// Correct
function myFunction(parm1, parm2) {
stuff...;
morestuff;
}
// Incorrect
function myFunction ( parm1, parm2 ){
stuff...;
morestuff;
}
```
### Semicolons
- You must use them always
```javascript
// Correct
var x = 1;
// Incorrect
var x = 1
```
### Variables
- Declarations must always use var, let or const.
- Prefer let or const over var.
- camelCase for all variable names.
```javascript
// Correct
let myVariable = 4;
// OK
var myVariable = 4;
// Incorrect
myVariable = 4;
var my_variable = 4;
```
### Blocks
- Braces must be used on all blocks.
- Braces must start on the same line as the statement starting the block.
- Else and else if must be on the same line as the if block closing brace.
```javascript
// Correct
if (somthing) {
stuff...;
} else if (otherthing) {
stuff...;
}
// Incorrect
if (somthing)
{
stuff...;
}
else
{
stuff...;
}
// Incorrect
if (somthing) stuff...;
if (somthing)
stuff...;
```
### Strings
- Use template strings instead of concatenation.
```javascript
// Correct
function getStr(stuff) {
return "This is the ${stuff} string";
}
// Incorrect
function wrongGetStr(stuff) {
return "This is the " + stuff + " string";
}
```
## React-JSX
Part of the build process is running ESLint. ESLint is the final authority on all style issues. PRs will not be accepted unless there are no errors or warnings running ESLint. The ESLint configuration file can be found in: [web/react/.eslintrc](https://github.com/mattermost/platform/blob/master/web/react/.eslintrc.json)
Instructions on how to use ESLint with your favourite editor can be found here: [http://eslint.org/docs/user-guide/integrations](http://eslint.org/docs/user-guide/integrations)
This is an abridged version of the [Airbnb React/JSX Style Guide](https://github.com/airbnb/javascript/tree/master/react#airbnb-reactjsx-style-guide). Anything that is unclear here follow that guide. If there is a conflict, follow what is said below.
### General
- Include only one React component per file.
- Use class \<name\> extends React.Component over React.createClass unless you need mixins
- CapitalCamelCase with .jsx extension for component filenames.
- Filenames should be the component name.
### Alignment
- Follow alignment styles shown below:
```xml
// Correct
<Tag
propertyOne="1"
propertyTwo="2"
>
<Child />
</Tag>
// Correct
<Tag propertyOne="1" />
```
### Naming
- Property names use camelCase.
- React component names use CapitalCamelCase.
- Do not use an understore for internal methods in a react component.
```xml
// Correct
<ReactComponent propertyOne="value" />
```

Просмотреть файл

@@ -35,25 +35,18 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
m := model.MapFromJson(r.Body) m := model.MapFromJson(r.Body)
email := strings.ToLower(strings.TrimSpace(m["email"])) email := strings.ToLower(strings.TrimSpace(m["email"]))
displayName := strings.TrimSpace(m["display_name"])
if len(email) == 0 { if len(email) == 0 {
c.SetInvalidParam("signupTeam", "email") c.SetInvalidParam("signupTeam", "email")
return return
} }
if len(displayName) == 0 {
c.SetInvalidParam("signupTeam", "display_name")
return
}
subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL()) subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL()) bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL())
bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
props := make(map[string]string) props := make(map[string]string)
props["email"] = email props["email"] = email
props["display_name"] = displayName
props["time"] = fmt.Sprintf("%v", model.GetMillis()) props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props) data := model.MapToJson(props)

139
web/react/.eslintrc Обычный файл
Просмотреть файл

@@ -0,0 +1,139 @@
{
"ecmaFeatures": {
"jsx": true,
"blockBindings": true,
"modules": true
},
"plugins": [
"react"
],
"env": {
"browser": true,
"node": true,
"jquery": true,
"es6": true
},
"globals": {
"React": false
},
"rules": {
"comma-dangle": [2, "never"],
"no-cond-assign": [2, "except-parens"],
"no-console": 1,
"no-constant-condition": 1,
"no-debugger": 1,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 1,
"no-ex-assign": 1,
"no-extra-semi": 2,
"no-func-assign": 1,
"no-inner-declarations": 0,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-unreachable": 2,
"valid-typeof": 2,
"no-unexpected-multiline": 2,
"block-scoped-var": 1,
"complexity": [1, 8],
"consistent-return": 2,
"curly": [2, "all"],
"dot-notation": 2,
"dot-location": [2, "object"],
"eqeqeq": [2, "smart"],
"guard-for-in": 1,
"no-alert": 1,
"no-caller": 2,
"no-div-regex": 1,
"no-else-return": 1,
"no-eval": 2,
"no-extend-native": 2,
"no-floating-decimal": 2,
"no-labels": 2,
"no-lone-blocks": 1,
"no-multi-spaces": [2, { "exceptions": { "Property": false } }],
"no-multi-str": 0,
"no-param-reassign": 2,
"no-process-env": 2,
"no-redeclare": 2,
"no-return-assign": [2, "always"],
"no-script-url": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-unused-expressions": 2,
"no-void": 2,
"no-warning-comments": 0,
"no-with": 2,
"radix": 2,
"vars-on-top": 0,
"wrap-iife": [2, "outside"],
"yoda": [2, "never", {"exceptRange": false, "onlyEquality": false}],
"no-undefined": 2,
"no-shadow": [2, {"hoist": "functions"}],
"no-unused-vars": [2, {"vars": "all", "args": "all"}],
"no-use-before-define": [2, "nofunc"],
// Style
"array-bracket-spacing": [2, "never"],
"brace-style": [2, "1tbs", { "allowSingleLine": false }],
"camelcase": [2, {"properties": "always"}],
"comma-spacing": [2, {"before": false, "after": true}],
"comma-style": [2, "last"],
"computed-property-spacing": [2, "never"],
"consistent-this": [2, "self"],
"func-names": 2,
"func-style": [2, "declaration"],
"indent": [2, 4, {"indentSwitchCase": false}],
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
"lines-around-comment": [2, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, "allowBlockEnd": true }],
"linebreak-style": 2,
"new-cap": 2,
"new-parens": 2,
"no-lonely-if": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": [2, {"max": 1}],
"no-spaced-func": 2,
"no-ternary": 2,
"no-trailing-spaces": [2, { "skipBlankLines": false }],
"no-underscore-dangle": 2,
"no-unneeded-ternary": 2,
"object-curly-spacing": [2, "never"],
"one-var": [2, "never"],
"operator-linebreak": [2, "after"],
"padded-blocks": [2, "never"],
"quote-props": [2, "as-needed"],
"quotes": [2, "single", "avoid-escape"],
"semi-spacing": [2, {"before": false, "after": true}],
"semi": [2, "always"],
"space-after-keywords": [2, "always"],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-return-throw-case": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"wrap-regex": 2,
// React Specific
"react/display-name": [2, { "acceptTranspilerName": true }],
"react/jsx-boolean-value": [2, "always"],
"react/jsx-curly-spacing": [2, "never"],
"react/jsx-no-duplicate-props": [2, { "ignoreCase": false }],
"react/jsx-no-undef": 2,
"react/jsx-quotes": [2, "single", "avoid-escape"],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/no-danger": 0,
"react/no-did-mount-set-state": 2,
"react/no-did-update-set-state": 2,
"react/no-multi-comp": 2,
"react/no-unknown-property": 2,
"react/prop-types": 2,
"react/sort-comp": 0,
"react/wrap-multilines": 2
}
}

Просмотреть файл

@@ -98,7 +98,7 @@ module.exports = React.createClass({
: "" } : "" }
</div> </div>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" className="btn btn-danger" data-dismiss="modal" onClick={this.handleDelete}>Delete</button> <button type="button" className="btn btn-danger" data-dismiss="modal" onClick={this.handleDelete}>Delete</button>
</div> </div>
</div> </div>

Просмотреть файл

@@ -24,7 +24,7 @@ module.exports = React.createClass({
if (filename.indexOf("/api/v1/files/get") != -1) { if (filename.indexOf("/api/v1/files/get") != -1) {
filename = filename.split("/api/v1/files/get")[1]; filename = filename.split("/api/v1/files/get")[1];
} }
filename = window.location.origin + "/api/v1/files/get" + filename; filename = utils.getWindowLocationOrigin() + "/api/v1/files/get" + filename;
if (type === "image") { if (type === "image") {
previews.push( previews.push(

Просмотреть файл

@@ -103,13 +103,9 @@ module.exports = React.createClass({
return ( return (
<div className="signup-team__container"> <div className="signup-team__container">
<div> <h5 className="margin--less">Sign in to:</h5>
<span className="signup-team__name">{ teamDisplayName }</span> <h2 className="signup-team__name">{ teamDisplayName }</h2>
<br/> <h2 className="signup-team__subdomain">on { config.SiteName }</h2>
<span className="signup-team__subdomain">/{ teamName }/</span>
<br/>
<br/>
</div>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<div className={server_error ? 'form-group has-error' : 'form-group'}> <div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error } { server_error }
@@ -124,13 +120,13 @@ module.exports = React.createClass({
<button type="submit" className="btn btn-primary">Sign in</button> <button type="submit" className="btn btn-primary">Sign in</button>
</div> </div>
{ login_message } { login_message }
<div className="form-group form-group--small"> <div className="form-group margin--extra form-group--small">
<span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span> <span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span>
</div> </div>
<div className="form-group"> <div className="form-group">
<a href={"/" + teamName + "/reset_password"}>I forgot my password</a> <a href={"/" + teamName + "/reset_password"}>I forgot my password</a>
</div> </div>
<div className="external-link"> <div className="margin--extra">
<span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span> <span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span>
</div> </div>
</form> </form>

Просмотреть файл

@@ -32,7 +32,7 @@ module.exports = React.createClass({
if (fileInfo.path.indexOf("/api/v1/files/get") != -1) { if (fileInfo.path.indexOf("/api/v1/files/get") != -1) {
fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
} }
fileInfo.path = window.location.origin + "/api/v1/files/get" + fileInfo.path; fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;
if (type === "image") { if (type === "image") {
$('<img/>').attr('src', fileInfo.path+'_thumb.jpg').load(function(path, name){ return function() { $('<img/>').attr('src', fileInfo.path+'_thumb.jpg').load(function(path, name){ return function() {
@@ -112,7 +112,7 @@ module.exports = React.createClass({
if (fileInfo.path.indexOf("/api/v1/files/get") != -1) { if (fileInfo.path.indexOf("/api/v1/files/get") != -1) {
fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
} }
fileInfo.path = window.location.origin + "/api/v1/files/get" + fileInfo.path; fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;
if (type === "image") { if (type === "image") {
if (i < Constants.MAX_DISPLAY_FILES) { if (i < Constants.MAX_DISPLAY_FILES) {

Просмотреть файл

@@ -98,7 +98,7 @@ RootPost = React.createClass({
if (fileInfo.path.indexOf("/api/v1/files/get") != -1) { if (fileInfo.path.indexOf("/api/v1/files/get") != -1) {
fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
} }
fileInfo.path = window.location.origin + "/api/v1/files/get" + fileInfo.path; fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;
if (ftype === "image") { if (ftype === "image") {
var url = fileInfo.path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); var url = fileInfo.path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
@@ -208,7 +208,7 @@ CommentPost = React.createClass({
if (fileInfo.path.indexOf("/api/v1/files/get") != -1) { if (fileInfo.path.indexOf("/api/v1/files/get") != -1) {
fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
} }
fileInfo.path = window.location.origin + "/api/v1/files/get" + fileInfo.path; fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;
if (type === "image") { if (type === "image") {
var url = fileInfo.path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); var url = fileInfo.path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');

Просмотреть файл

@@ -68,10 +68,10 @@ var NavbarDropdown = React.createClass({
for (var i = 0; i < this.state.teams.length; i++) { for (var i = 0; i < this.state.teams.length; i++) {
var teamName = this.state.teams[i]; var teamName = this.state.teams[i];
teams.push(<li key={ teamName }><a href={window.location.origin + "/" + teamName }>Switch to { teamName }</a></li>); teams.push(<li key={ teamName }><a href={utils.getWindowLocationOrigin() + "/" + teamName }>Switch to { teamName }</a></li>);
} }
} }
teams.push(<li><a href={window.location.origin + "/signup_team" }>Create a New Team</a></li>); teams.push(<li><a href={utils.getWindowLocationOrigin() + "/signup_team" }>Create a New Team</a></li>);
return ( return (
<ul className="nav navbar-nav navbar-right"> <ul className="nav navbar-nav navbar-right">

Просмотреть файл

@@ -20,21 +20,12 @@ module.exports = React.createClass({
state.email_error = ""; state.email_error = "";
} }
team.display_name = this.refs.name.getDOMNode().value.trim();
if (!team.display_name) {
state.name_error = "This field is required";
state.inValid = true;
}
else {
state.name_error = "";
}
if (state.inValid) { if (state.inValid) {
this.setState(state); this.setState(state);
return; return;
} }
client.signupTeam(team.email, team.display_name, client.signupTeam(team.email,
function(data) { function(data) {
if (data["follow_link"]) { if (data["follow_link"]) {
window.location.href = data["follow_link"]; window.location.href = data["follow_link"];
@@ -55,7 +46,6 @@ module.exports = React.createClass({
render: function() { render: function() {
var email_error = this.state.email_error ? <label className='control-label'>{ this.state.email_error }</label> : null; var email_error = this.state.email_error ? <label className='control-label'>{ this.state.email_error }</label> : null;
var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null;
var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null; var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null;
return ( return (
@@ -64,15 +54,11 @@ module.exports = React.createClass({
<input autoFocus={true} type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" /> <input autoFocus={true} type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
{ email_error } { email_error }
</div> </div>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<input type="text" ref="name" className="form-control" placeholder={utils.toTitleCase(strings.Company) + " Name"} maxLength="64" />
{ name_error }
</div>
{ server_error } { server_error }
<div className="form-group"> <div className="form-group">
<button className="btn btn-md btn-primary" type="submit">Sign up for Free</button> <button className="btn btn-md btn-primary" type="submit">Sign up</button>
</div> </div>
<div className="form-group form-group--small"> <div className="form-group margin--extra-2x">
<span><a href="/find_team">{"Find my " + strings.Team}</a></span> <span><a href="/find_team">{"Find my " + strings.Team}</a></span>
</div> </div>
</form> </form>

Просмотреть файл

@@ -42,11 +42,15 @@ WelcomePage = React.createClass({
state.email_error = ""; state.email_error = "";
} }
client.signupTeam(email, this.props.state.team.name, client.signupTeam(email,
function(data) { function(data) {
this.props.state.wizard = "finished"; if (data["follow_link"]) {
this.props.updateParent(this.props.state); window.location.href = data["follow_link"];
window.location.href = "/signup_team_confirm/?email=" + encodeURI(email); } else {
this.props.state.wizard = "finished";
this.props.updateParent(this.props.state);
window.location.href = "/signup_team_confirm/?email=" + encodeURIComponent(team.email);
}
}.bind(this), }.bind(this),
function(err) { function(err) {
this.state.server_error = err.message; this.state.server_error = err.message;
@@ -80,19 +84,25 @@ WelcomePage = React.createClass({
<div> <div>
<p> <p>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Welcome!</h2> <h3 className="sub-heading">Welcome to:</h3>
<h3>{"Let's set up your " + strings.Team + " on " + config.SiteName + "."}</h3> <h1 className="margin--top-none">{config.SiteName}</h1>
</p> </p>
<p className="margin--less">Let's setup your new team</p>
<p> <p>
Please confirm your email address:<br /> Please confirm your email address:<br />
<span className="black">{ this.props.state.team.email }</span><br /> <div className="inner__content">
<div className="block--gray">{ this.props.state.team.email }</div>
</div>
</p>
<p className="margin--extra color--light">
Your account will administer the new team site. <br />
You can add other administrators later.
</p> </p>
<div className="form-group"> <div className="form-group">
<button className="btn-primary btn form-group" type="submit" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button> <button className="btn-primary btn form-group" type="submit" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
{ storage_error } { storage_error }
</div> </div>
<hr /> <hr />
<p>If this is not correct, you can switch to a different email. We'll send you a new invite right away.</p>
<div className={ this.state.use_diff ? "" : "hidden" }> <div className={ this.state.use_diff ? "" : "hidden" }>
<div className={ email_error ? "form-group has-error" : "form-group" }> <div className={ email_error ? "form-group has-error" : "form-group" }>
<div className="row"> <div className="row">
@@ -105,7 +115,7 @@ WelcomePage = React.createClass({
{ server_error } { server_error }
<button className="btn btn-md btn-primary" type="button" onClick={this.handleDiffSubmit} type="submit">Use this instead</button> <button className="btn btn-md btn-primary" type="button" onClick={this.handleDiffSubmit} type="submit">Use this instead</button>
</div> </div>
<button type="button" onClick={this.handleDiffEmail} className={ this.state.use_diff ? "btn-default btn hidden" : "btn-default btn" }>Use a different address</button> <a href="#" onClick={this.handleDiffEmail} className={ this.state.use_diff ? "hidden" : "" }>Use a different email</a>
</div> </div>
); );
} }
@@ -128,6 +138,7 @@ TeamDisplayNamePage = React.createClass({
this.props.state.wizard = "team_url"; this.props.state.wizard = "team_url";
this.props.state.team.display_name = display_name; this.props.state.team.display_name = display_name;
this.props.state.team.name = utils.cleanUpUrlable(display_name);
this.props.updateParent(this.props.state); this.props.updateParent(this.props.state);
}, },
getInitialState: function() { getInitialState: function() {
@@ -158,9 +169,11 @@ TeamDisplayNamePage = React.createClass({
</div> </div>
{ name_error } { name_error }
</div> </div>
<p>{"Your " + strings.Team + " name shows in menus and headings. It may include the name of your " + strings.Company + ", but it's not required."}</p> <div>{"Name your " + strings.Team + " in any language. Your " + strings.Team + " name shows in menus and headings."}</div>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp; <button type="submit" className="btn btn-primary margin--extra" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button> <div className="margin--extra">
<a href="#" onClick={this.submitBack}>Back to previous step</a>
</div>
</form> </form>
</div> </div>
); );
@@ -248,17 +261,23 @@ TeamURLPage = React.createClass({
<div className="row"> <div className="row">
<div className="col-sm-11"> <div className="col-sm-11">
<div className="input-group"> <div className="input-group">
<span className="input-group-addon">{ window.location.origin + "/" }</span> <span className="input-group-addon">{ utils.getWindowLocationOrigin() + "/" }</span>
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} autoFocus={true} onFocus={this.handleFocus}/> <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} autoFocus={true} onFocus={this.handleFocus}/>
</div> </div>
</div> </div>
</div> </div>
{ name_error } { name_error }
</div> </div>
<p className="black">{"Pick something short and memorable for your " + strings.Team + "'s web address."}</p> <p>{"Choose the web address of your new " + strings.Team + ":"}</p>
<p>{"Your " + strings.Team + " URL can only contain lowercase letters, numbers and dashes. Also, it needs to start with a letter and cannot end in a dash."}</p> <ul className="color--light">
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp; <li>Short and memorable is best</li>
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button> <li>Use lower case letters, numbers and dashes</li>
<li>Must start with a letter and can't end in a dash</li>
</ul>
<button type="submit" className="btn btn-primary margin--extra" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<div className="margin--extra">
<a href="#" onClick={this.submitBack}>Back to previous step</a>
</div>
</form> </form>
</div> </div>
); );
@@ -461,14 +480,16 @@ SendInivtesPage = React.createClass({
return ( return (
<div> <div>
<form> <form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Send Invitations</h2> <h2>{"Invite " + utils.toTitleCase(strings.Team) + " Members"}</h2>
{ emails } { emails }
<div className="form-group"><button type="button" className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</button></div> <div className="form-group text-right"><a href="#" onClick={this.submitAddInvite}>Add Invitation</a></div>
<div className="form btn-default-group"><button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div> <div className="form-group"><button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div>
</form> </form>
<p>{"If you'd prefer, you can send invitations after you finish setting up the "+ strings.Team + "."}</p> <p className="color--light">{"if you prefer, you can invite " + strings.Team + " members later"}<br /> and <a href="#" onClick={this.submitSkip}>skip this step</a> for now.</p>
<div><a href="#" onClick={this.submitSkip}>Skip this step</a></div> <div className="margin--extra">
<a href="#" onClick={this.submitBack}>Back to previous step</a>
</div>
</div> </div>
); );
} }
@@ -512,19 +533,24 @@ UsernamePage = React.createClass({
<div> <div>
<form> <form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a username</h2> <h2 className="margin--less">Your username</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }> <h5 className="color--light">{"Select a memorable username that makes it easy for " + strings.Team + "mates to identify you:"}</h5>
<div className="row"> <div className="inner__content margin--extra">
<div className="col-sm-9"> <div className={ name_error ? "form-group has-error" : "form-group" }>
<input autoFocus={true} type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" /> <div className="row">
<div className="col-sm-11">
<h5><strong>Choose your username</strong></h5>
<input autoFocus={true} type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" />
<div className="color--light form__hint">Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'</div>
</div>
</div>
{ name_error }
</div> </div>
</div> </div>
{ name_error } <button type="submit" className="btn btn-primary margin--extra" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<div className="margin--extra">
<a href="#" onClick={this.submitBack}>Back to previous step</a>
</div> </div>
<p>{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}</p>
<p>It can be made of lowercase letters and numbers.</p>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form> </form>
</div> </div>
); );
@@ -542,15 +568,15 @@ PasswordPage = React.createClass({
var password = this.refs.password.getDOMNode().value.trim(); var password = this.refs.password.getDOMNode().value.trim();
if (!password || password.length < 5) { if (!password || password.length < 5) {
this.setState({name_error: "Please enter at least 5 characters"}); this.setState({password_error: "Please enter at least 5 characters"});
return; return;
} }
this.setState({name_error: ""}); this.setState({password_error: null, server_error: null});
$('#finish-button').button('loading'); $('#finish-button').button('loading');
var teamSignup = JSON.parse(JSON.stringify(this.props.state)); var teamSignup = JSON.parse(JSON.stringify(this.props.state));
teamSignup.user.password = password; teamSignup.user.password = password;
teamSignup.user.allow_marketing = this.refs.email_service.getDOMNode().checked; teamSignup.user.allow_marketing = true;
delete teamSignup.wizard; delete teamSignup.wizard;
var ctl = this; var ctl = this;
@@ -566,7 +592,7 @@ PasswordPage = React.createClass({
props.state.wizard = "finished"; props.state.wizard = "finished";
props.updateParent(props.state, true); props.updateParent(props.state, true);
window.location.href = window.location.origin + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); window.location.href = utils.getWindowLocationOrigin() + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email);
// client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password,
// function(data) { // function(data) {
@@ -582,7 +608,7 @@ PasswordPage = React.createClass({
}, 5000); }, 5000);
}.bind(this), }.bind(this),
function(err) { function(err) {
this.setState({name_error: err.message}); this.setState({server_error: err.message});
$('#sign-up-button').button('reset'); $('#sign-up-button').button('reset');
}.bind(this) }.bind(this)
); );
@@ -594,30 +620,37 @@ PasswordPage = React.createClass({
client.track('signup', 'signup_team_07_password'); client.track('signup', 'signup_team_07_password');
var name_error = this.state.name_error ? <label className="control-label">{ this.state.name_error }</label> : null; var password_error = this.state.password_error ? <label className="control-label">{ this.state.password_error }</label> : null;
var server_error = this.state.server_error ? <label className="control-label">{ this.state.server_error }</label> : null;
return ( return (
<div> <div>
<form> <form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a password</h2> <h2 className="margin--less">Your password</h2>
<p>You'll use your email address ({this.props.state.team.email}) and password to log into {config.SiteName}.</p> <h5 className="color--light">Select a password that you'll use to login with your email address:</h5>
<div className={ name_error ? "form-group has-error" : "form-group" }> <div className="inner__content margin--extra">
<div className="row"> <h5><strong>Email</strong></h5>
<div className="col-sm-9"> <div className="block--gray form-group">{this.props.state.team.email}</div>
<input autoFocus={true} type="password" ref="password" className="form-control" placeholder="" maxLength="128" /> <div className={ password_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-11">
<h5><strong>Choose your password</strong></h5>
<input autoFocus={true} type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
<div className="color--light form__hint">Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.</div>
</div>
</div>
{ password_error }
{ server_error }
</div> </div>
</div>
{ name_error }
</div>
<div className="form-group checkbox">
<label><input type="checkbox" ref="email_service" /> It's ok to send me occassional email with updates about the {config.SiteName} service.</label>
</div> </div>
<div className="form-group"> <div className="form-group">
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp; <button type="submit" className="btn btn-primary margin--extra" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
<button type="submit" className="btn-primary btn" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
</div> </div>
<p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p> <p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p>
<div className="margin--extra">
<a href="#" onClick={this.submitBack}>Back to previous step</a>
</div>
</form> </form>
</div> </div>
); );
@@ -640,9 +673,6 @@ module.exports = React.createClass({
props.wizard = "welcome"; props.wizard = "welcome";
props.team = {}; props.team = {};
props.team.email = this.props.email; props.team.email = this.props.email;
props.team.display_name = this.props.name;
props.team.company_name = this.props.name;
props.team.name = utils.cleanUpUrlable(this.props.name);
props.team.allowed_domains = ""; props.team.allowed_domains = "";
props.invites = []; props.invites = [];
props.invites.push(""); props.invites.push("");

Просмотреть файл

@@ -40,7 +40,7 @@ module.exports = React.createClass({
this.setState({name_error: "", email_error: "", password_error: "", server_error: ""}); this.setState({name_error: "", email_error: "", password_error: "", server_error: ""});
this.state.user.allow_marketing = this.refs.email_service.getDOMNode().checked; this.state.user.allow_marketing = true;
client.createUser(this.state.user, this.state.data, this.state.hash, client.createUser(this.state.user, this.state.data, this.state.hash,
function(data) { function(data) {
@@ -104,8 +104,8 @@ module.exports = React.createClass({
var yourEmailIs = this.state.user.email == "" ? "" : <span>Your email address is { this.state.user.email }. </span> var yourEmailIs = this.state.user.email == "" ? "" : <span>Your email address is { this.state.user.email }. </span>
var email = ( var email = (
<div className={ this.state.original_email == "" ? "" : "hidden"} > <div className={ this.state.original_email == "" ? "margin--extra" : "hidden"} >
<label className="control-label">Email</label> <h5><strong>What's your email address?</strong></h5>
<div className={ email_error ? "form-group has-error" : "form-group" }> <div className={ email_error ? "form-group has-error" : "form-group" }>
<input type="email" ref="email" className="form-control" defaultValue={ this.state.user.email } placeholder="" maxLength="128" /> <input type="email" ref="email" className="form-control" defaultValue={ this.state.user.email } placeholder="" maxLength="128" />
{ email_error } { email_error }
@@ -124,29 +124,33 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h3 className="text-center extra-margin">Signup to { config.SiteName }</h3> <h5 className="margin--less">Welcome to:</h5>
<div className="form-group form-group--small"> <h2 className="signup-team__name">{ this.props.teamDisplayName }</h2>
<span></span> <h2 className="signup-team__subdomain">on { config.SiteName }</h2>
</div> <h4 className="color--light">Let's create your account</h4>
{ signup_message } { signup_message }
<label className="control-label">Username</label> <div className="inner__content">
<div className={ name_error ? "form-group has-error" : "form-group" }> { email }
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" /> <p className={ this.state.original_email == "" ? "hidden" : ""}>{ yourEmailIs } Youll use this address to sign in to {config.SiteName}.</p>
{ name_error } <div className="margin--extra">
<p className="form__hint">Your username can be made of lowercase letters and numbers.</p> <h5><strong>Choose your username</strong></h5>
<p className="form__hint">{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others"}</p> <div className={ name_error ? "form-group has-error" : "form-group" }>
</div> <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" />
{ email } { name_error }
<label className="control-label">Password</label> <p className="form__hint">Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"</p>
<div className={ password_error ? "form-group has-error" : "form-group" }> </div>
<input type="password" ref="password" className="form-control" placeholder="" maxLength="128" /> </div>
{ password_error } <div className="margin--extra">
</div> <h5><strong>Choose your password</strong></h5>
<p className={ this.state.original_email == "" ? "hidden" : ""}>{ yourEmailIs } Youll use this address to sign in to {config.SiteName}.</p> <div className={ password_error ? "form-group has-error" : "form-group" }>
<div className="checkbox"><label><input type="checkbox" ref="email_service" /> It's ok to send me occassional email with updates about the {config.SiteName} service. </label></div> <input type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
<p><button onClick={this.handleSubmit} className="btn-primary btn">Create Account</button></p> { password_error }
</div>
</div>
</div>
<p className="margin--extra"><button onClick={this.handleSubmit} className="btn-primary btn">Create Account</button></p>
{ server_error } { server_error }
<p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p> <p>By creating an account and using Mattermost you are agreeing to our <a href={ config.TermsLink }>Terms of Service</a>. If you do not agree, you cannot use this service.</p>
</div> </div>
); );
} }

Просмотреть файл

@@ -40,7 +40,7 @@ module.exports = React.createClass({
if (fileInfo.path.indexOf("/api/v1/files/get") !== -1) { if (fileInfo.path.indexOf("/api/v1/files/get") !== -1) {
fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1]; fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
} }
fileInfo.path = window.location.origin + "/api/v1/files/get" + fileInfo.path; fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;
src = fileInfo['path'] + '_preview.jpg'; src = fileInfo['path'] + '_preview.jpg';
} }
@@ -148,7 +148,7 @@ module.exports = React.createClass({
if (info.path.indexOf("/api/v1/files/get") !== -1) { if (info.path.indexOf("/api/v1/files/get") !== -1) {
info.path = info.path.split("/api/v1/files/get")[1]; info.path = info.path.split("/api/v1/files/get")[1];
} }
info.path = window.location.origin + "/api/v1/files/get" + info.path; info.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + info.path;
preview_filename = info['path'] + '_preview.jpg'; preview_filename = info['path'] + '_preview.jpg';
} }
@@ -166,7 +166,7 @@ module.exports = React.createClass({
if (download_link.indexOf("/api/v1/files/get") !== -1) { if (download_link.indexOf("/api/v1/files/get") !== -1) {
download_link = download_link.split("/api/v1/files/get")[1]; download_link = download_link.split("/api/v1/files/get")[1];
} }
download_link = window.location.origin + "/api/v1/files/get" + download_link; download_link = utils.getWindowLocationOrigin() + "/api/v1/files/get" + download_link;
return ( return (
<div className="modal fade image_modal" ref="modal" id={this.props.modalId} tabIndex="-1" role="dialog" aria-hidden="true"> <div className="modal fade image_modal" ref="modal" id={this.props.modalId} tabIndex="-1" role="dialog" aria-hidden="true">

Просмотреть файл

@@ -16,7 +16,9 @@
"jest-cli": "~0.1.17", "jest-cli": "~0.1.17",
"reactify": "^0.15.2", "reactify": "^0.15.2",
"uglify-js": "~2.4.15", "uglify-js": "~2.4.15",
"watchify": "^2.1.1" "watchify": "^2.1.1",
"eslint": "^0.24.1",
"eslint-plugin-react": "^3.0.0"
}, },
"scripts": { "scripts": {
"start": "watchify --extension=jsx -o ../static/js/bundle.js -v -d ./**/*.jsx", "start": "watchify --extension=jsx -o ../static/js/bundle.js -v -d ./**/*.jsx",

Просмотреть файл

@@ -3,9 +3,9 @@
var SignupTeamComplete =require('../components/signup_team_complete.jsx'); var SignupTeamComplete =require('../components/signup_team_complete.jsx');
global.window.setup_signup_team_complete_page = function(email, name, data, hash) { global.window.setup_signup_team_complete_page = function(email, data, hash) {
React.render( React.render(
<SignupTeamComplete name={name} email={email} hash={hash} data={data}/>, <SignupTeamComplete email={email} hash={hash} data={data}/>,
document.getElementById('signup-team-complete') document.getElementById('signup-team-complete')
); );
}; };

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var SignupUserComplete =require('../components/signup_user_complete.jsx'); var SignupUserComplete = require('../components/signup_user_complete.jsx');
global.window.setup_signup_user_complete_page = function(email, name, ui_name, id, data, hash, auth_services) { global.window.setup_signup_user_complete_page = function(email, name, ui_name, id, data, hash, auth_services) {
React.render( React.render(

Просмотреть файл

@@ -11,6 +11,12 @@ var BrowserStore = require('../stores/browser_store.jsx');
var CHANGE_EVENT = 'change'; var CHANGE_EVENT = 'change';
var utils;
function getWindowLocationOrigin() {
if (!utils) utils = require('../utils/utils.jsx');
return utils.getWindowLocationOrigin();
}
var TeamStore = assign({}, EventEmitter.prototype, { var TeamStore = assign({}, EventEmitter.prototype, {
emitChange: function() { emitChange: function() {
this.emit(CHANGE_EVENT); this.emit(CHANGE_EVENT);
@@ -58,7 +64,7 @@ var TeamStore = assign({}, EventEmitter.prototype, {
return null; return null;
}, },
getCurrentTeamUrl: function() { getCurrentTeamUrl: function() {
return window.location.origin + "/" + this.getCurrent().name; return getWindowLocationOrigin() + "/" + this.getCurrent().name;
}, },
storeTeam: function(team) { storeTeam: function(team) {
var teams = this._getTeams(); var teams = this._getTeams();

Просмотреть файл

@@ -341,13 +341,13 @@ module.exports.updateTeamDisplayName = function(data, success, error) {
module.exports.track('api', 'api_teams_update_name'); module.exports.track('api', 'api_teams_update_name');
}; };
module.exports.signupTeam = function(email, display_name, success, error) { module.exports.signupTeam = function(email, success, error) {
$.ajax({ $.ajax({
url: "/api/v1/teams/signup", url: "/api/v1/teams/signup",
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
type: 'POST', type: 'POST',
data: JSON.stringify({email: email, display_name: display_name}), data: JSON.stringify({email: email}),
success: success, success: success,
error: function(xhr, status, err) { error: function(xhr, status, err) {
e = handleError("singupTeam", xhr, status, err); e = handleError("singupTeam", xhr, status, err);

Просмотреть файл

@@ -22,8 +22,6 @@ module.exports.cleanUpUrlable = function(input) {
return cleaned; return cleaned;
}; };
module.exports.isTestDomain = function() { module.exports.isTestDomain = function() {
if ((/^localhost/).test(window.location.hostname)) if ((/^localhost/).test(window.location.hostname))
@@ -841,3 +839,12 @@ module.exports.getDisplayName = function(user) {
} }
} }
}; };
//IE10 does not set window.location.origin automatically so this must be called instead when using it
module.exports.getWindowLocationOrigin = function() {
var windowLocationOrigin = window.location.origin;
if (!windowLocationOrigin) {
windowLocationOrigin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
}
return windowLocationOrigin;
};

Просмотреть файл

@@ -426,13 +426,15 @@
} }
body { body {
&.white { &.white {
.row.content { .inner__wrap {
>.row.content {
margin-bottom: -185px; margin-bottom: -185px;
} }
}
} }
} }
.footer, .footer-pane, .footer-push { .footer, .footer-pane, .footer-push {
height: auto; height: 187px;
} }
.footer-pane { .footer-pane {
.footer-link { .footer-link {

Просмотреть файл

@@ -9,65 +9,108 @@
max-width: 380px; max-width: 380px;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;
&.padding--less {
padding-top: 50px;
}
h1, h2, h3, h4, h5, h6, p { h1, h2, h3, h4, h5, h6, p {
line-height: 1.3; line-height: 1.3;
} }
h1 {
font-weight: 600;
}
h2 { h2 {
font-weight: 600; font-weight: 600;
margin-bottom: 0.5em; margin-bottom: 0.8em;
letter-spacing: -0.5px; letter-spacing: -0.5px;
font-size: em(30px); font-size: em(30px);
} }
h3 { h3 {
font-weight: 600; font-weight: 600;
margin: 0 0 1.3em 0; margin: 0 0 1.3em 0;
font-size: 1.5em; font-size: 1.5em;
&.extra-margin {
margin-bottom: 2.5em;
}
} }
h4 { h4 {
font-size: em(20px); font-size: em(20px);
font-weight: 600; font-weight: 600;
margin-bottom: 1em; margin-bottom: 1em;
&.text--light {
font-weight: 300;
color: #999;
}
} }
h5 {
font-size: em(16px);
}
hr {
margin: 2em 0;
}
p { p {
color: #555; color: #555;
line-height: 1.5; line-height: 1.5;
margin-bottom: 1em; margin-bottom: 1em;
.black, &.black {
color: #000;
}
} }
.inner__content {
padding: 0 15px;
margin: 30px 0 20px;
}
.block--gray {
background: #f2f2f2;
display: inline-block;
padding: 0.85em 1.2em;
font-weight: 600;
@include border-radius(3px);
}
form { form {
margin-bottom: 0.8em; margin-bottom: 0.8em;
} }
.form__hint { .form__hint {
font-size: 0.95em; font-size: 0.95em;
color: #999; color: #999;
margin: 10px 0; margin: 10px 0;
} }
.external-link {
position: absolute; .signup-team-confirm__container {
bottom: 0; padding: 100px 0px 100px 0px;
left: 0;
} }
.signup-team-logo {
display: none;
width: 210px;
margin: 0 0 2em 0;
}
.signup-team-login {
padding-bottom: 10px;
font-weight: 700;
}
.signup-team__name { .signup-team__name {
margin: 0.5em 0 0;
font-size: 2.2em; font-size: 2.2em;
font-weight: 600; font-weight: 600;
padding-left: 1rem;
}
.signup-team__subdomain {
margin: 0.2em 0 1.2em;
font-size: 1.5em;
padding-left: 1rem;
font-weight: 300;
text-transform: uppercase; text-transform: uppercase;
} }
.signup-team__subdomain {
font-size: 1.5em;
padding-left: 1em;
}
.form-control { .form-control {
height: em(38px); height: em(38px);
} }
.or__container { .or__container {
height: 1px; height: 1px;
background: #dddddd; background: #dddddd;
@@ -84,6 +127,12 @@
display: inline-block; display: inline-block;
} }
} }
ul {
margin-bottom: 0;
padding-left: 18px;
}
.btn { .btn {
padding: em(7px) em(15px); padding: em(7px) em(15px);
font-weight: 600; font-weight: 600;
@@ -121,8 +170,8 @@
} }
.glyphicon { .glyphicon {
&.glyphicon-ok, &.glyphicon-refresh { &.glyphicon-ok, &.glyphicon-refresh {
margin-right: 0.3em; margin-right: 0.5em;
left: -5px; left: -2px;
font-size: 0.9em; font-size: 0.9em;
} }
&.glyphicon-chevron-right { &.glyphicon-chevron-right {
@@ -137,6 +186,7 @@
} }
} }
} }
.has-error { .has-error {
.control-label { .control-label {
background: #f2f2f2; background: #f2f2f2;
@@ -148,13 +198,14 @@
color: #999; color: #999;
width: 100%; width: 100%;
&:before { &:before {
@extend .fa; @extend .fa;
content: "\f071"; content: "\f071";
margin-right: 4px; margin-right: 4px;
color: #aaa; color: #aaa;
} }
} }
} }
.reset-form { .reset-form {
@include border-radius(3px); @include border-radius(3px);
position: relative; position: relative;
@@ -163,19 +214,49 @@
color: inherit; color: inherit;
} }
} }
}
.signup-team-confirm__container { // Modifier Styles
padding: 100px 0px 100px 0px; h1, h2, h3, h4, h5, h6 {
} &.margin--top-none {
margin-top: 0;
}
&.margin--bottom-none {
margin-bottom: 0;
}
&.margin--less {
margin-bottom: 0.3em;
}
&.sub-heading {
font-weight: 400;
margin-bottom: 0;
}
&.color--light {
font-weight: 300;
}
}
.signup-team-logo { p {
display: none; &.margin--extra {
width: 210px; margin-bottom: 1.5em;
margin: 0 0 2em 0; }
} &.margin--less {
margin-bottom: 0.3em;
}
.black, &.black {
color: #000;
}
}
.color--light {
color: #777;
}
.margin--extra {
margin-top: 3em;
}
.margin--extra-2x {
margin-top: 6em;
}
.signup-team-login {
padding-bottom: 10px;
font-weight: 700;
} }

Просмотреть файл

@@ -9,8 +9,8 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="signup-team__container"> <div class="signup-team__container">
<img class="signup-team-logo" src="/static/images/logo.png" /> <img class="signup-team-logo" src="/static/images/logo.png" />
<h2>All team communication in one place, searchable and accessible anywhere</h2> <h1>Mattermost</h1>
<h4 class="text--light">{{ .SiteName }} is free for an unlimited time, for unlimited users </h4 class="text--light"> <h4 class="color--light">All team communication in one place, searchable and accesible anywhere</h4>
<div id="signup-team"></div> <div id="signup-team"></div>
</div> </div>
</div> </div>

Просмотреть файл

@@ -19,7 +19,7 @@
</div> </div>
</div> </div>
<script> <script>
window.setup_signup_team_complete_page('{{.Props.Email}}', '{{.Props.DisplayName}}', '{{.Props.Data}}', '{{.Props.Hash}}'); window.setup_signup_team_complete_page('{{.Props.Email}}', '{{.Props.Data}}', '{{.Props.Hash}}');
</script> </script>
</body> </body>
</html> </html>

Просмотреть файл

@@ -7,7 +7,7 @@
<div class="inner__wrap"> <div class="inner__wrap">
<div class="row content"> <div class="row content">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="signup-team__container"> <div class="signup-team__container padding--less">
<div id="signup-user-complete"></div> <div id="signup-user-complete"></div>
</div> </div>
</div> </div>

Просмотреть файл

@@ -219,7 +219,6 @@ func signupTeamComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
page := NewHtmlTemplatePage("signup_team_complete", "Complete Team Sign Up") page := NewHtmlTemplatePage("signup_team_complete", "Complete Team Sign Up")
page.Props["Email"] = props["email"] page.Props["Email"] = props["email"]
page.Props["DisplayName"] = props["display_name"]
page.Props["Data"] = data page.Props["Data"] = data
page.Props["Hash"] = hash page.Props["Hash"] = hash
page.Render(c, w) page.Render(c, w)