summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/install/Production-Ubuntu.md2
-rw-r--r--doc/install/Upgrade-Guide.md8
-rw-r--r--web/react/components/docs.jsx41
-rw-r--r--web/react/components/posts_view.jsx17
-rw-r--r--web/react/components/textbox.jsx15
-rw-r--r--web/react/pages/docs.jsx16
-rw-r--r--web/react/stores/browser_store.jsx2
-rw-r--r--web/react/utils/async_client.jsx2
-rw-r--r--web/sass-files/sass/partials/_post.scss53
-rw-r--r--web/sass-files/sass/partials/_responsive.scss118
l---------web/static/help/Messaging.md1
-rw-r--r--web/templates/docs.html24
-rw-r--r--web/web.go11
13 files changed, 234 insertions, 76 deletions
diff --git a/doc/install/Production-Ubuntu.md b/doc/install/Production-Ubuntu.md
index 098707ada..482c2a0ba 100644
--- a/doc/install/Production-Ubuntu.md
+++ b/doc/install/Production-Ubuntu.md
@@ -38,7 +38,7 @@
## Set up Mattermost Server
1. For the purposes of this guide we will assume this server has an IP address of 10.10.10.2
1. Download the latest Mattermost Server by typing:
- * ``` wget https://github.com/mattermost/platform/releases/download/v1.1.0/mattermost.tar.gz```
+ * ``` wget https://github.com/mattermost/platform/releases/download/v1.2.1/mattermost.tar.gz```
1. Unzip the Mattermost Server by typing:
* ``` tar -xvzf mattermost.tar.gz```
1. For the sake of making this guide simple we located the files at `/home/ubuntu/mattermost`. In the future we will give guidance for storing under `/opt`.
diff --git a/doc/install/Upgrade-Guide.md b/doc/install/Upgrade-Guide.md
index 7f4eeaeb9..aec9aa8ef 100644
--- a/doc/install/Upgrade-Guide.md
+++ b/doc/install/Upgrade-Guide.md
@@ -4,6 +4,8 @@
Each release of Mattermost contains logic to upgrade it from the previously major build version. For example, version 1.2 upgrades the database and configuration data schema for a Mattermost version 1.1 server. The following procedure outlines how to upgrade Mattermost to the next major release version.
+If you're upgrading across multiple major releases, from 1.0.x to 1.2.x for example, please run the following procedure once for each incremental upgrade, in sequential order.
+
1. Download the **next major build release** of your server
1. Determine the current version of your Mattermost server
1. Go to any team site, opening the main menu at the top right of the left-hand sidebar and selecting **About Mattermost**
@@ -11,7 +13,7 @@ Each release of Mattermost contains logic to upgrade it from the previously majo
1. For example, if your current version is 1.1.0, you want to select version 1.2.0.
1. In some cases there will be **minor build releases**, such as 1.2.1 and 1.2.2. The minor build number indicates a bug fix or security issue release. Testing on minor build versions is less extensive than on major build versions and it is recommended that you use the minor build only if you need the specific additions included.
3. Review Release Notes
- 1. Check the release notes for the version of Mattermost you are able to install, and note any setting changes in the **Compatibility** section that apply to your deployment
+ 1. Check the release notes for the version of Mattermost you are able to install, and note any setting changes in the **Compatibility** section that apply to your deployment (Release notes across versions are available from the [CHANGELOG](https://github.com/mattermost/platform/blob/master/CHANGELOG.md)).
4. Download the `mattermost.tar.gz` file with the correct version for your upgrade
1. You can use `wget` to retrieve a specific version. For example, to download v1.1.0 run `wget https://github.com/mattermost/platform/releases/download/v1.1.0/mattermost.tar.gz`
2. Stop the Mattermost Server
@@ -26,8 +28,8 @@ Each release of Mattermost contains logic to upgrade it from the previously majo
5. Restore the state of your server by copying the backed up version of `config.json` in place of the default `config.json`
6. Start your server and address any setting changes relevant in the latest version of Mattermost
1. Run `sudo start mattermost`
- 2. The server will upgrade your database schema to be compatibile with the new release, as well as upgrade your `config.json` file to the latest format, using default values for new settings added
- 3. Go to the System Console to update any settings that have been added or modified based on the **Compatibility** documentation in the release notes
+ 2. Go to the **System Console** to update any settings that have been added or modified based on the **Compatibility** section in the release notes of the version you are installing (Release notes across versions are available from the [CHANGELOG](https://github.com/mattermost/platform/blob/master/CHANGELOG.md)).
+ 1. Opening the System Console and saving a change will upgrade your `config.json` schema to the latest version using default values for new settings added
7. Test the system is working by going to the URL of an existing team
### Upgrading from Mattermost Beta (Version 0.7)
diff --git a/web/react/components/docs.jsx b/web/react/components/docs.jsx
new file mode 100644
index 000000000..68baa6dad
--- /dev/null
+++ b/web/react/components/docs.jsx
@@ -0,0 +1,41 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const TextFormatting = require('../utils/text_formatting.jsx');
+const UserStore = require('../stores/user_store.jsx');
+
+export default class Docs extends React.Component {
+ constructor(props) {
+ super(props);
+ UserStore.setCurrentUser(global.window.mm_user || {});
+
+ this.state = {text: ''};
+ const errorState = {text: '## 404'};
+
+ if (props.site) {
+ $.get('/static/help/' + props.site + '.md').then((response) => {
+ this.setState({text: response});
+ }, () => {
+ this.setState(errorState);
+ });
+ } else {
+ this.setState(errorState);
+ }
+ }
+
+ render() {
+ return (
+ <div
+ dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.text)}}
+ >
+ </div>
+ );
+ }
+}
+
+Docs.defaultProps = {
+ site: ''
+};
+Docs.propTypes = {
+ site: React.PropTypes.string
+};
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index 087ca1df2..ec8223203 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -83,9 +83,14 @@ export default class PostsView extends React.Component {
let hideProfilePic = false;
if (prevPost) {
- sameUser = prevPost.user_id === post.user_id && post.create_at - prevPost.create_at <= 1000 * 60 * 5;
+ const postIsComment = Utils.isComment(post);
+ const prevPostIsComment = Utils.isComment(prevPost);
+ const postFromWebhook = Boolean(post.props && post.props.from_webhook);
+ const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
- sameRoot = Utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
+ sameUser = prevPost.user_id === post.user_id && postFromWebhook === prevPostFromWebhook &&
+ post.create_at - prevPost.create_at <= 1000 * 60 * 5;
+ sameRoot = (postIsComment && (prevPost.id === post.root_id || prevPost.root_id === post.root_id)) || (!postIsComment && !prevPostIsComment && sameUser);
// hide the profile pic if:
// the previous post was made by the same user as the current post,
@@ -94,10 +99,10 @@ export default class PostsView extends React.Component {
// the current post is not from a webhook
// and the previous post is not from a webhook
if ((prevPost.user_id === post.user_id) &&
- !Utils.isComment(prevPost) &&
- !Utils.isComment(post) &&
- (!post.props || !post.props.from_webhook) &&
- (!prevPost.props || !prevPost.props.from_webhook)) {
+ !prevPostIsComment &&
+ !postIsComment &&
+ !postFromWebhook &&
+ !prevPostFromWebhook) {
hideProfilePic = true;
}
}
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index e6530b941..1a5269baa 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -295,6 +295,13 @@ export default class Textbox extends React.Component {
this.resize();
}
+ showHelp(e) {
+ e.preventDefault();
+ e.target.blur();
+
+ global.window.open('/docs/Messaging');
+ }
+
render() {
const previewLinkVisible = this.props.messageText.length > 0;
@@ -336,11 +343,17 @@ export default class Textbox extends React.Component {
>
</div>
<a
+ onClick={this.showHelp}
+ className='textbox-help-link'
+ >
+ {'Help'}
+ </a>
+ <a
style={{visibility: previewLinkVisible ? 'visible' : 'hidden'}}
onClick={this.showPreview}
className='textbox-preview-link'
>
- {this.state.preview ? 'Edit message' : 'Preview'}
+ {this.state.preview ? 'Edit' : 'Preview'}
</a>
</div>
);
diff --git a/web/react/pages/docs.jsx b/web/react/pages/docs.jsx
new file mode 100644
index 000000000..ed2b6d0c9
--- /dev/null
+++ b/web/react/pages/docs.jsx
@@ -0,0 +1,16 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Docs = require('../components/docs.jsx');
+
+function setupDocumentationPage(props) {
+ ReactDOM.render(
+ <Docs
+ site={props.Site}
+ />,
+ document.getElementById('docs')
+ );
+}
+
+global.window.mm_user = global.window.mm_user || {};
+global.window.setup_documentation_page = setupDocumentationPage;
diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx
index 8e86ce32f..2e3a26cff 100644
--- a/web/react/stores/browser_store.jsx
+++ b/web/react/stores/browser_store.jsx
@@ -72,7 +72,7 @@ class BrowserStoreClass {
console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
localStorage.clear();
sessionStorage.clear();
- window.location.href = window.location.href;
+ window.location.reload(true);
}
}
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index 205c7461c..b39648bf0 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -63,7 +63,7 @@ export function getChannels(force, updateLastViewed, checkVersion) {
if (serverVersion !== BrowserStore.getLastServerVersion()) {
BrowserStore.setLastServerVersion(serverVersion);
- window.location.href = window.location.href;
+ window.location.reload(true);
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
}
}
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index e2981e914..fad6f5074 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -47,20 +47,25 @@ body.ios {
.textarea-wrapper {
position:relative;
- .textbox-preview-area {
- position: absolute;
- z-index: 2;
- top: 0;
- left: 0;
- box-shadow: none;
- }
- .textbox-preview-link {
- position: absolute;
- z-index: 3;
- bottom: -23px;
- right: 0;
+ .textbox-preview-area {
+ position: absolute;
+ z-index: 2;
+ top: 0;
+ left: 0;
+ box-shadow: none;
+ }
+ .textbox-preview-link, .textbox-help-link {
+ position: absolute;
+ z-index: 3;
+ bottom: -23px;
font-size: 13px;
- cursor: pointer;
+ cursor: pointer;
+ }
+ .textbox-preview-link {
+ right: 45px;
+ }
+ .textbox-help-link {
+ right: 0;
}
min-height:36px;
}
@@ -393,6 +398,15 @@ body.ios {
.post-body {
@include border-radius(0 4px 4px 0);
}
+ .post-body {
+ border-left: 4px solid #EEE;
+ width: 570px;
+ margin-left: 30px;
+ padding-left: 10px;
+ .post-link {
+ display: none;
+ }
+ }
}
}
&.same--root {
@@ -408,13 +422,12 @@ body.ios {
.post__content {
padding: 0;
}
- .post-body {
- border-left: 4px solid #EEE;
- width: 570px;
- margin-left: 30px;
- padding-left: 10px;
- .post-link {
- display: none;
+ &.same--user {
+ .post__content {
+ padding-left: 46px;
+ }
+ .post-header-post {
+ visibility: hidden;
}
}
}
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index a4bdc3e92..aad991035 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -9,25 +9,26 @@
}
}
.post {
- &.same--root {
- margin-left: 60px;
- padding-left: 10px;
- border-left: 4px solid #EEE;
- div.post-profile-img__container {
- .post-profile-img {
- display: none;
- }
- }
- .post__content {
- width: 825px;
- }
- .post-body {
- width: 736px;
- border: none;
- margin: 3px 0 0;
- }
- }
&.post--comment {
+ &.same--root {
+ margin-left: 104px;
+ padding-left: 10px;
+ border-left: 4px solid #EEE;
+ div.post-profile-img__container {
+ .post-profile-img {
+ display: none;
+ }
+ }
+ .post__content {
+ width: 825px;
+ margin-left: 0;
+ }
+ .post-body {
+ width: 736px;
+ border: none;
+ margin: 3px 0 0;
+ }
+ }
&.other--root {
.post-comment {
margin-left: 0;
@@ -105,34 +106,61 @@
}
}
.post {
- &.same--root {
- margin-left: 60px;
- padding-left: 10px;
- border-left: 4px solid #EEE;
- div.post-profile-img__container {
- .post-profile-img {
- display: none;
- }
- }
- .post__content {
- width: 825px;
- }
- .post-body {
- width: 736px;
- border: none;
- margin: 3px 0 0;
- }
- }
+ &.same--root.same--user {
+ .post-header-post {
+ visibility: hidden;
+ width: 100%;
+ position: relative;
+ top: -5px;
+ .post-header-col.post-header__name {
+ display: none;
+ }
+ }
+ .post-body {
+ top: -15px;
+ margin-bottom: -10px;
+ }
+ &:hover .post-header-post {
+ visibility: visible;
+ }
+ }
+
&.post--comment {
&.other--root {
.post-comment {
margin-left: 0;
}
}
- &.same--root {
- margin-top: 5px;
- margin-bottom: 5px;
- }
+ &.same--root {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ margin-left: 104px;
+ padding-left: 10px;
+ border-left: 4px solid #EEE;
+ div.post-profile-img__container {
+ .post-profile-img {
+ display: none;
+ }
+ }
+ .post-body {
+ margin-left: 0;
+ border-left: 0;
+ }
+ &.same--user {
+ .post__content {
+ margin-left: 0;
+ padding-left: 0;
+ }
+ }
+ }
+ .post__content {
+ width: 810px;
+ }
+ .post-body {
+ width: 736px;
+ border: none;
+ margin: 3px 0 0;
+ }
}
.post__content {
width: 880px;
@@ -358,9 +386,6 @@
}
}
.post {
- &.same--root {
- margin-left: 25px;
- }
&:hover {
background: none;
.post-header .post-header-col.post-header__reply {
@@ -370,7 +395,11 @@
}
}
&.post--comment {
+ &.same--root {
+ margin-left: 25px;
+ }
&.other--root {
+ margin-left: 0;
&:hover {
background: none;
}
@@ -381,6 +410,9 @@
content: '...';
}
}
+ &.same--root.same--user .post__content{
+ padding-left: 0;
+ }
}
.signup-team__container {
padding: 30px 0;
diff --git a/web/static/help/Messaging.md b/web/static/help/Messaging.md
new file mode 120000
index 000000000..f74c0b879
--- /dev/null
+++ b/web/static/help/Messaging.md
@@ -0,0 +1 @@
+../../../doc/help/Messaging.md \ No newline at end of file
diff --git a/web/templates/docs.html b/web/templates/docs.html
new file mode 100644
index 000000000..21659e810
--- /dev/null
+++ b/web/templates/docs.html
@@ -0,0 +1,24 @@
+{{define "docs"}}
+<!DOCTYPE html>
+<html>
+{{template "head" . }}
+<body class="white">
+<div class="container-fluid">
+ <div class="inner__wrap">
+ <div class="row content">
+ <div class="col-sm-12">
+ <div id="docs"></div>
+ </div>
+ <div class="footer-push"></div>
+ </div>
+ <div class="row footer">
+ {{template "footer" . }}
+ </div>
+ </div>
+</div>
+<script>
+ window.setup_documentation_page({{ .Props }});
+</script>
+</body>
+</html>
+{{end}}
diff --git a/web/web.go b/web/web.go
index 02ceb69ba..477bd8b27 100644
--- a/web/web.go
+++ b/web/web.go
@@ -80,6 +80,8 @@ func InitWeb() {
mainrouter.Handle("/hooks/{id:[A-Za-z0-9]+}", api.ApiAppHandler(incomingWebhook)).Methods("POST")
+ mainrouter.Handle("/docs/{doc:[A-Za-z0-9]+}", api.AppHandlerIndependent(docs)).Methods("GET")
+
// ----------------------------------------------------------------------------------------------
// *ANYTHING* team specific should go below this line
// ----------------------------------------------------------------------------------------------
@@ -494,6 +496,15 @@ func findTeam(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Render(c, w)
}
+func docs(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+ doc := params["doc"]
+
+ page := NewHtmlTemplatePage("docs", "Documentation")
+ page.Props["Site"] = doc
+ page.Render(c, w)
+}
+
func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
isResetLink := true
hash := r.URL.Query().Get("h")