diff options
-rw-r--r-- | doc/README.md | 1 | ||||
-rw-r--r-- | doc/api/Overview.md | 94 | ||||
-rw-r--r-- | doc/install/Production-Ubuntu.md | 2 | ||||
-rw-r--r-- | web/react/components/signup_user_complete.jsx | 27 | ||||
-rw-r--r-- | web/react/package.json | 1 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 23 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 14 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_base.scss | 7 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_mentions.scss | 8 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_modal.scss | 3 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_search.scss | 10 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_settings.scss | 4 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_signup.scss | 3 |
13 files changed, 161 insertions, 36 deletions
diff --git a/doc/README.md b/doc/README.md index d711b5d53..66af8f34c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -19,6 +19,7 @@ - [Code Contribution Guidelines](developer/Code-Contribution-Guidelines.md) - [Developer Machine Setup](developer/Setup.md) - [Mattermost Style Guide](developer/Style-Guide.md) +- [API Overview] (api/Overview.md) ## Usage Help diff --git a/doc/api/Overview.md b/doc/api/Overview.md new file mode 100644 index 000000000..02e11974e --- /dev/null +++ b/doc/api/Overview.md @@ -0,0 +1,94 @@ +# API Overview + +This provides a basic overview of the Mattermost API. All examples assume there is a Mattermost instance running at http://localhost:8065. + +## Schema + +All API access is done through `yourdomain.com/api/v1/`, with all data being sent and received as JSON. + + +## Authentication + +The majority of the Mattermost API involves interacting with teams. Therefore, most API methods require authentication as a user. There are two ways to authenticate into a Mattermost system. + +##### Session Token + +Make an HTTP POST to `yourdomain.com/api/v1/users/login` with a JSON body indicating the `name` of the team, the user's `email` and `password`. + +``` +curl -i -d '{"name":"exampleteam","email":"someone@nowhere.com","password":"thisisabadpassword"}' http://localhost:8065/api/v1/users/login +``` + +If successful, the response will contain a `Token` header and a User object in the body. + +``` +HTTP/1.1 200 OK +Set-Cookie: MMSID=hyr5dmb1mbb49c44qmx4whniso; Path=/; Max-Age=2592000; HttpOnly +Token: hyr5dmb1mbb49c44qmx4whniso +X-Ratelimit-Limit: 10 +X-Ratelimit-Remaining: 9 +X-Ratelimit-Reset: 1 +X-Request-Id: smda55ckcfy89b6tia58shk5fh +X-Version-Id: developer +Date: Fri, 11 Sep 2015 13:21:14 GMT +Content-Length: 657 +Content-Type: application/json; charset=utf-8 + +{{user object as json}} +``` + +Include the `Token` as part of the `Authentication` header on your future API requests with the `Bearer` method. + +``` +curl -i -H 'Authorization: Bearer hyr5dmb1mbb49c44qmx4whniso' http://localhost:8065/api/v1/users/me +``` + +That's it! You should now be able to access the API as the user you logged in as. + +##### OAuth2 + +Coming soon... + + +## Client Errors + +All errors will return an appropriate HTTP response code along with the following JSON body: + +``` +{ + "message": "", // the reason for the error + "detailed_error": "", // some extra details about the error + "request_id": "", // the ID of the request + "status_code": 0 // the HTTP status code +} +``` + + +## Rate Limiting + +Whenever you make an HTTP request to the Mattermost API you might notice the following headers included in the response: +``` +X-Ratelimit-Limit: 10 +X-Ratelimit-Remaining: 9 +X-Ratelimit-Reset: 1441983590 + +``` + +These headers are telling you your current rate limit status. + +Header | Description +--------------------- | ----------- +X-Ratelimit-Limit | The maximum number of requests you can make per second. +X-Ratelimit-Remaining | The number of requests remaining in the current window. +X-Ratelimit-Reset | The remaining UTC epoch seconds before the rate limit resets. + +If you exceed your rate limit for a window you will receive the following error in the body of the response: +``` +HTTP/1.1 429 Too Many Requests +Date: Tue, 10 Sep 2015 11:20:28 GMT +X-RateLimit-Limit: 10 +X-RateLimit-Remaining: 0 +X-RateLimit-Reset: 1 + +limit exceeded +``` diff --git a/doc/install/Production-Ubuntu.md b/doc/install/Production-Ubuntu.md index 0f1373c1a..05a56c412 100644 --- a/doc/install/Production-Ubuntu.md +++ b/doc/install/Production-Ubuntu.md @@ -8,7 +8,7 @@ ## Set up Database Server 1. For the purposes of this guide we will assume this server has an IP address of 10.10.10.1 -1. Install PostgreSQL 9.3+ (or MySQL 5.2+) +1. Install PostgreSQL 9.3+ (or MySQL 5.6+) * ``` sudo apt-get install postgresql postgresql-contrib``` 1. PostgreSQL created a user account called `postgres`. You will need to log into that account with: * ``` sudo -i -u postgres``` diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index ae3075495..4e17c6d06 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -20,8 +20,6 @@ export default class SignupUserComplete extends React.Component { initialState.user = {}; initialState.user.team_id = this.props.teamId; initialState.user.email = this.props.email; - initialState.hash = this.props.hash; - initialState.data = this.props.data; initialState.original_email = this.props.email; } @@ -47,7 +45,7 @@ export default class SignupUserComplete extends React.Component { return; } - const usernameError = Utils.isValidUsername(this.state.user.username); + const usernameError = Utils.isValidUsername(providedUsername); if (usernameError === 'Cannot use a reserved word as a username.') { this.setState({nameError: 'This username is reserved, please choose a new one.', emailError: '', passwordError: '', serverError: ''}); return; @@ -67,26 +65,29 @@ export default class SignupUserComplete extends React.Component { return; } + const user = { + team_id: this.props.teamId, + email: providedEmail, + username: providedUsername, + password: providedPassword, + allow_marketing: true + }; + this.setState({ - user: { - email: providedEmail, - username: providedUsername, - password: providedPassword, - allow_marketing: true - }, + user, nameError: '', emailError: '', passwordError: '', serverError: '' }); - client.createUser(this.state.user, this.state.data, this.state.hash, + client.createUser(user, this.props.data, this.props.hash, function createUserSuccess() { client.track('signup', 'signup_user_02_complete'); - client.loginByEmail(this.props.teamName, this.state.user.email, this.state.user.password, + client.loginByEmail(this.props.teamName, user.email, user.password, function emailLoginSuccess(data) { - UserStore.setLastEmail(this.state.user.email); + UserStore.setLastEmail(user.email); UserStore.setCurrentUser(data); if (this.props.hash > 0) { BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'})); @@ -95,7 +96,7 @@ export default class SignupUserComplete extends React.Component { }.bind(this), function emailLoginFailure(err) { if (err.message === 'Login failed because email address has not been verified') { - window.location.href = '/verify_email?email=' + encodeURIComponent(this.state.user.email) + '&teamname=' + encodeURIComponent(this.props.teamName); + window.location.href = '/verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.props.teamName); } else { this.setState({serverError: err.message}); } diff --git a/web/react/package.json b/web/react/package.json index 31295873b..a097b6aa8 100644 --- a/web/react/package.json +++ b/web/react/package.json @@ -10,7 +10,6 @@ "keymirror": "0.1.1", "marked": "0.3.5", "object-assign": "3.0.0", - "react-zeroclipboard-mixin": "0.1.0", "twemoji": "1.4.1" }, "devDependencies": { diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 67414dc3b..aba63b91c 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -138,7 +138,8 @@ module.exports = { newMessageSeparator: '#FF8800', linkColor: '#2389d7', buttonBg: '#2389d7', - buttonColor: '#FFFFFF' + buttonColor: '#FFFFFF', + mentionHighlightBg: '#fff2bb' }, organization: { type: 'Organization', @@ -159,7 +160,8 @@ module.exports = { newMessageSeparator: '#FF8800', linkColor: '#2f81b7', buttonBg: '#1dacfc', - buttonColor: '#FFFFFF' + buttonColor: '#FFFFFF', + mentionHighlightBg: '#fff2bb' }, mattermostDark: { type: 'Mattermost Dark', @@ -180,15 +182,16 @@ module.exports = { newMessageSeparator: '#5de5da', linkColor: '#A4FFEB', buttonBg: '#4CBBA4', - buttonColor: '#FFFFFF' + buttonColor: '#FFFFFF', + mentionHighlightBg: '#338886' }, windows10: { type: 'Windows Dark', sidebarBg: '#171717', - sidebarText: '#eee', + sidebarText: '#999', sidebarUnreadText: '#fff', sidebarTextHoverBg: '#302e30', - sidebarTextHoverColor: '#fff', + sidebarTextHoverColor: '#999', sidebarTextActiveBg: '#484748', sidebarTextActiveColor: '#FFFFFF', sidebarHeaderBg: '#1f1f1f', @@ -201,7 +204,8 @@ module.exports = { newMessageSeparator: '#CC992D', linkColor: '#0177e7', buttonBg: '#0177e7', - buttonColor: '#FFFFFF' + buttonColor: '#FFFFFF', + mentionHighlightBg: '#276198' } }, THEME_ELEMENTS: [ @@ -263,7 +267,7 @@ module.exports = { }, { id: 'newMessageSeparator', - uiName: 'New message separator' + uiName: 'New Message Separator' }, { id: 'linkColor', @@ -273,10 +277,13 @@ module.exports = { id: 'buttonBg', uiName: 'Button BG' }, - { id: 'buttonColor', uiName: 'Button Text' + }, + { + id: 'mentionHighlightBg', + uiName: 'Mention Highlight BG' } ] }; diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 307a311ab..b51d71254 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -468,6 +468,7 @@ export function applyTheme(theme) { changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1); changeCss('.post-image__column .post-image__details', 'background:' + theme.centerChannelBg, 1); changeCss('.sidebar--right, .dropdown-menu, .popover', 'background:' + theme.centerChannelBg, 1); + changeCss('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1); } if (theme.centerChannelColor) { @@ -491,20 +492,21 @@ export function applyTheme(theme) { changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2); changeCss('.post-image__column .post-image__details', 'color:' + theme.centerChannelColor, 2); changeCss('.post-image__column a, .post-image__column a:hover, .post-image__column a:focus', 'color:' + theme.centerChannelColor, 1); - changeCss('.search-bar__container .search__form .search-bar', 'background: transparent; color:' + theme.centerChannelColor, 1); + changeCss('.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2); changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1); - changeCss('.search-bar__container .search__form', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.channel-intro .channel-intro__content', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2); changeCss('.date-separator .separator__hr, .modal-footer, .modal .custom-textarea, .post-right__container .post.post--root hr, .search-item-container', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); changeCss('.modal .custom-textarea:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.channel-intro, .settings-modal .settings-table .settings-content .divider-dark, hr, .settings-modal .settings-table .settings-links', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment, .post.post--comment.other--root .post-comment, .post.same--root .post-body', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); + changeCss('.post.current--user .post-body, .post.post--comment.other--root.current--user .post-comment, .post.post--comment.other--root .post-comment, .post.same--root .post-body, .modal .more-channel-table tbody>tr td, .member-div:first-child, .member-div', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 2); changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); - changeCss('.post:hover, .sidebar--right .sidebar--right__header, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post:hover, .modal .more-channel-table tbody>tr:hover td, .sidebar--right .sidebar--right__header, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.date-separator.hovered--before:after, .date-separator.hovered--after:before, .new-separator.hovered--after:before, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.command-name:hover, .mentions-name:hover, .mentions-focus, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); changeCss('.post.current--user:hover .post-body ', 'background: none;', 1); @@ -529,6 +531,10 @@ export function applyTheme(theme) { if (theme.buttonColor) { changeCss('.btn.btn-primary', 'color:' + theme.buttonColor, 2); } + + if (theme.mentionHighlightBg) { + changeCss('.mention-highlight, .search-highlight', 'background:' + theme.mentionHighlightBg, 1); + } } export function changeCss(className, classValue, classRepeat) { // we need invisible container to store additional css definitions diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index 841db7c5a..4237ee1fa 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -97,6 +97,9 @@ a:focus, a:hover { .form-control { @include border-radius(2px); + &:focus { + @include box-shadow(none); + } &.no-resize { resize: none; } @@ -120,6 +123,10 @@ a:focus, a:hover { z-index: 100; } +.nav>li>a:focus, .nav>li>a:hover { + background: transparent; +} + .btn { @include single-transition(all, 0.25s, ease-in); @include border-radius(1px); diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss index a2bd0dcea..fb74eb4f5 100644 --- a/web/sass-files/sass/partials/_mentions.scss +++ b/web/sass-files/sass/partials/_mentions.scss @@ -57,5 +57,11 @@ .mention-highlight { background-color:#fff2bb; - color: #333; + a { + color: inherit; + text-decoration: underline; + &:hover, &:active { + color: inherit; + } + } }
\ No newline at end of file diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss index 96b26f251..2722333a4 100644 --- a/web/sass-files/sass/partials/_modal.scss +++ b/web/sass-files/sass/partials/_modal.scss @@ -144,11 +144,10 @@ font-size: 0.9em; overflow: hidden; text-overflow: ellipsis; - color: #999; + @include opacity(0.8); margin: 5px 0; } .more-channel-name { - color: #444; font-weight: 600; font-size: 0.95em; } diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index bcb8b5eac..a7b1ab190 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -104,7 +104,13 @@ padding: 10px; } -.search-highlight.theme, .search-highlight { +.search-highlight { background-color: #FFF2BB; - color: #333; + a { + color: inherit; + text-decoration: underline; + &:hover, &:active { + color: inherit; + } + } } diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index 42475efd5..9369cc097 100644 --- a/web/sass-files/sass/partials/_settings.scss +++ b/web/sass-files/sass/partials/_settings.scss @@ -240,10 +240,6 @@ margin-right:5px; } -.member-list-holder { - background-color:#fff; -} - .member-div { border-bottom:1px solid lightgrey; position:relative; diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss index 924f0718a..47235eb50 100644 --- a/web/sass-files/sass/partials/_signup.scss +++ b/web/sass-files/sass/partials/_signup.scss @@ -12,6 +12,9 @@ &.padding--less { padding-top: 50px; } + .form-control:focus { + @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)); + } h1, h2, h3, h4, h5, h6, p { line-height: 1.3; |