summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/.eslintrc.json226
-rw-r--r--web/react/action_creators/global_actions.jsx252
-rw-r--r--web/react/components/about_build_modal.jsx135
-rw-r--r--web/react/components/access_history_modal.jsx110
-rw-r--r--web/react/components/activity_log_modal.jsx296
-rw-r--r--web/react/components/admin_console/admin_controller.jsx215
-rw-r--r--web/react/components/admin_console/admin_navbar_dropdown.jsx110
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx492
-rw-r--r--web/react/components/admin_console/admin_sidebar_header.jsx67
-rw-r--r--web/react/components/admin_console/audits.jsx95
-rw-r--r--web/react/components/admin_console/email_settings.jsx957
-rw-r--r--web/react/components/admin_console/gitlab_settings.jsx379
-rw-r--r--web/react/components/admin_console/image_settings.jsx688
-rw-r--r--web/react/components/admin_console/ldap_settings.jsx584
-rw-r--r--web/react/components/admin_console/legal_and_support_settings.jsx290
-rw-r--r--web/react/components/admin_console/license_settings.jsx291
-rw-r--r--web/react/components/admin_console/log_settings.jsx414
-rw-r--r--web/react/components/admin_console/logs.jsx100
-rw-r--r--web/react/components/admin_console/privacy_settings.jsx211
-rw-r--r--web/react/components/admin_console/rate_settings.jsx367
-rw-r--r--web/react/components/admin_console/reset_password_modal.jsx159
-rw-r--r--web/react/components/admin_console/select_team_modal.jsx112
-rw-r--r--web/react/components/admin_console/service_settings.jsx980
-rw-r--r--web/react/components/admin_console/sql_settings.jsx386
-rw-r--r--web/react/components/admin_console/team_settings.jsx416
-rw-r--r--web/react/components/admin_console/team_users.jsx186
-rw-r--r--web/react/components/admin_console/user_item.jsx421
-rw-r--r--web/react/components/analytics/doughnut_chart.jsx77
-rw-r--r--web/react/components/analytics/line_chart.jsx90
-rw-r--r--web/react/components/analytics/statistic_count.jsx33
-rw-r--r--web/react/components/analytics/system_analytics.jsx346
-rw-r--r--web/react/components/analytics/table_chart.jsx60
-rw-r--r--web/react/components/analytics/team_analytics.jsx235
-rw-r--r--web/react/components/audio_video_preview.jsx116
-rw-r--r--web/react/components/audit_table.jsx624
-rw-r--r--web/react/components/authorize.jsx115
-rw-r--r--web/react/components/center_panel.jsx139
-rw-r--r--web/react/components/change_url_modal.jsx219
-rw-r--r--web/react/components/channel_header.jsx517
-rw-r--r--web/react/components/channel_info_modal.jsx102
-rw-r--r--web/react/components/channel_invite_modal.jsx214
-rw-r--r--web/react/components/channel_members_modal.jsx223
-rw-r--r--web/react/components/channel_notifications_modal.jsx415
-rw-r--r--web/react/components/channel_view.jsx18
-rw-r--r--web/react/components/claim/claim_account.jsx113
-rw-r--r--web/react/components/claim/email_to_sso.jsx151
-rw-r--r--web/react/components/claim/sso_to_email.jsx165
-rw-r--r--web/react/components/confirm_modal.jsx67
-rw-r--r--web/react/components/create_comment.jsx448
-rw-r--r--web/react/components/create_post.jsx507
-rw-r--r--web/react/components/delete_channel_modal.jsx109
-rw-r--r--web/react/components/delete_post_modal.jsx174
-rw-r--r--web/react/components/do_verify_email.jsx82
-rw-r--r--web/react/components/edit_channel_header_modal.jsx173
-rw-r--r--web/react/components/edit_channel_purpose_modal.jsx184
-rw-r--r--web/react/components/edit_post_modal.jsx220
-rw-r--r--web/react/components/error_bar.jsx98
-rw-r--r--web/react/components/file_attachment.jsx225
-rw-r--r--web/react/components/file_attachment_list.jsx62
-rw-r--r--web/react/components/file_info_preview.jsx56
-rw-r--r--web/react/components/file_preview.jsx116
-rw-r--r--web/react/components/file_upload.jsx335
-rw-r--r--web/react/components/file_upload_overlay.jsx45
-rw-r--r--web/react/components/filtered_user_list.jsx132
-rw-r--r--web/react/components/get_link_modal.jsx135
-rw-r--r--web/react/components/get_post_link_modal.jsx76
-rw-r--r--web/react/components/get_team_invite_link_modal.jsx76
-rw-r--r--web/react/components/invite_member_modal.jsx515
-rw-r--r--web/react/components/loading_screen.jsx38
-rw-r--r--web/react/components/logged_in.jsx224
-rw-r--r--web/react/components/login.jsx302
-rw-r--r--web/react/components/login_email.jsx165
-rw-r--r--web/react/components/login_ldap.jsx142
-rw-r--r--web/react/components/login_username.jsx181
-rw-r--r--web/react/components/member_list_team.jsx60
-rw-r--r--web/react/components/message_wrapper.jsx26
-rw-r--r--web/react/components/more_channels.jsx227
-rw-r--r--web/react/components/more_direct_channels.jsx153
-rw-r--r--web/react/components/msg_typing.jsx135
-rw-r--r--web/react/components/navbar.jsx570
-rw-r--r--web/react/components/navbar_dropdown.jsx277
-rw-r--r--web/react/components/needs_team.jsx20
-rw-r--r--web/react/components/new_channel_flow.jsx248
-rw-r--r--web/react/components/new_channel_modal.jsx284
-rw-r--r--web/react/components/not_logged_in.jsx70
-rw-r--r--web/react/components/notify_counts.jsx52
-rw-r--r--web/react/components/password_reset_form.jsx129
-rw-r--r--web/react/components/password_reset_send_link.jsx149
-rw-r--r--web/react/components/popover_list_members.jsx200
-rw-r--r--web/react/components/post.jsx252
-rw-r--r--web/react/components/post_attachment.jsx312
-rw-r--r--web/react/components/post_attachment_list.jsx28
-rw-r--r--web/react/components/post_attachment_oembed.jsx104
-rw-r--r--web/react/components/post_body.jsx219
-rw-r--r--web/react/components/post_body_additional_content.jsx147
-rw-r--r--web/react/components/post_deleted_modal.jsx89
-rw-r--r--web/react/components/post_focus_view.jsx133
-rw-r--r--web/react/components/post_header.jsx78
-rw-r--r--web/react/components/post_image.jsx81
-rw-r--r--web/react/components/post_info.jsx249
-rw-r--r--web/react/components/posts_view.jsx604
-rw-r--r--web/react/components/posts_view_container.jsx223
-rw-r--r--web/react/components/providers.json376
-rw-r--r--web/react/components/register_app_modal.jsx409
-rw-r--r--web/react/components/removed_from_channel_modal.jsx132
-rw-r--r--web/react/components/rename_channel_modal.jsx321
-rw-r--r--web/react/components/rhs_comment.jsx306
-rw-r--r--web/react/components/rhs_header_post.jsx91
-rw-r--r--web/react/components/rhs_root_post.jsx287
-rw-r--r--web/react/components/rhs_thread.jsx226
-rw-r--r--web/react/components/root.jsx90
-rw-r--r--web/react/components/search_bar.jsx206
-rw-r--r--web/react/components/search_results.jsx193
-rw-r--r--web/react/components/search_results_header.jsx75
-rw-r--r--web/react/components/search_results_item.jsx143
-rw-r--r--web/react/components/setting_item_max.jsx95
-rw-r--r--web/react/components/setting_item_min.jsx45
-rw-r--r--web/react/components/setting_picture.jsx156
-rw-r--r--web/react/components/setting_upload.jsx123
-rw-r--r--web/react/components/settings_sidebar.jsx64
-rw-r--r--web/react/components/should_verify_email.jsx111
-rw-r--r--web/react/components/sidebar.jsx714
-rw-r--r--web/react/components/sidebar_header.jsx141
-rw-r--r--web/react/components/sidebar_right.jsx140
-rw-r--r--web/react/components/sidebar_right_menu.jsx220
-rw-r--r--web/react/components/signup_team.jsx205
-rw-r--r--web/react/components/signup_team_complete/components/signup_team_complete.jsx79
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx136
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_email_item.jsx86
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_finished.jsx15
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_password_page.jsx215
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx210
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_url_page.jsx205
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_username_page.jsx164
-rw-r--r--web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx234
-rw-r--r--web/react/components/signup_team_confirm.jsx46
-rw-r--r--web/react/components/signup_user_complete.jsx491
-rw-r--r--web/react/components/suggestion/at_mention_provider.jsx118
-rw-r--r--web/react/components/suggestion/command_provider.jsx43
-rw-r--r--web/react/components/suggestion/emoticon_provider.jsx91
-rw-r--r--web/react/components/suggestion/search_channel_provider.jsx69
-rw-r--r--web/react/components/suggestion/search_suggestion_list.jsx97
-rw-r--r--web/react/components/suggestion/search_user_provider.jsx62
-rw-r--r--web/react/components/suggestion/suggestion_box.jsx163
-rw-r--r--web/react/components/suggestion/suggestion_list.jsx125
-rw-r--r--web/react/components/team_export_tab.jsx124
-rw-r--r--web/react/components/team_general_tab.jsx655
-rw-r--r--web/react/components/team_import_tab.jsx168
-rw-r--r--web/react/components/team_members_dropdown.jsx332
-rw-r--r--web/react/components/team_members_modal.jsx87
-rw-r--r--web/react/components/team_settings.jsx84
-rw-r--r--web/react/components/team_settings_modal.jsx123
-rw-r--r--web/react/components/team_signup_choose_auth.jsx138
-rw-r--r--web/react/components/team_signup_with_email.jsx121
-rw-r--r--web/react/components/team_signup_with_ldap.jsx234
-rw-r--r--web/react/components/team_signup_with_sso.jsx178
-rw-r--r--web/react/components/textbox.jsx263
-rw-r--r--web/react/components/time_since.jsx66
-rw-r--r--web/react/components/toggle_modal_button.jsx73
-rw-r--r--web/react/components/tutorial/tutorial_intro_screens.jsx242
-rw-r--r--web/react/components/tutorial/tutorial_tip.jsx158
-rw-r--r--web/react/components/unread_channel_indicator.jsx32
-rw-r--r--web/react/components/user_list.jsx49
-rw-r--r--web/react/components/user_list_row.jsx64
-rw-r--r--web/react/components/user_profile.jsx126
-rw-r--r--web/react/components/user_settings/custom_theme_chooser.jsx387
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx215
-rw-r--r--web/react/components/user_settings/manage_command_hooks.jsx679
-rw-r--r--web/react/components/user_settings/manage_incoming_hooks.jsx223
-rw-r--r--web/react/components/user_settings/manage_languages.jsx118
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx395
-rw-r--r--web/react/components/user_settings/premade_theme_chooser.jsx58
-rw-r--r--web/react/components/user_settings/user_settings.jsx158
-rw-r--r--web/react/components/user_settings/user_settings_advanced.jsx343
-rw-r--r--web/react/components/user_settings/user_settings_developer.jsx136
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx496
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx815
-rw-r--r--web/react/components/user_settings/user_settings_integrations.jsx208
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx338
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx831
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx472
-rw-r--r--web/react/components/user_settings/user_settings_theme.jsx299
-rw-r--r--web/react/components/view_image.jsx422
-rw-r--r--web/react/components/view_image_popover_bar.jsx81
-rw-r--r--web/react/components/youtube_video.jsx176
-rw-r--r--web/react/dispatcher/app_dispatcher.jsx27
-rw-r--r--web/react/package.json55
-rw-r--r--web/react/pages/authorize.jsx71
-rw-r--r--web/react/pages/root.jsx290
-rw-r--r--web/react/stores/admin_store.jsx162
-rw-r--r--web/react/stores/analytics_store.jsx85
-rw-r--r--web/react/stores/browser_store.jsx225
-rw-r--r--web/react/stores/channel_store.jsx352
-rw-r--r--web/react/stores/error_store.jsx80
-rw-r--r--web/react/stores/file_store.jsx60
-rw-r--r--web/react/stores/localization_store.jsx60
-rw-r--r--web/react/stores/modal_store.jsx47
-rw-r--r--web/react/stores/post_store.jsx610
-rw-r--r--web/react/stores/preference_store.jsx178
-rw-r--r--web/react/stores/search_store.jsx137
-rw-r--r--web/react/stores/socket_store.jsx342
-rw-r--r--web/react/stores/suggestion_store.jsx261
-rw-r--r--web/react/stores/team_store.jsx142
-rw-r--r--web/react/stores/user_store.jsx305
-rw-r--r--web/react/utils/async_client.jsx1085
-rw-r--r--web/react/utils/channel_intro_messages.jsx253
-rw-r--r--web/react/utils/client.jsx1650
-rw-r--r--web/react/utils/constants.jsx520
-rw-r--r--web/react/utils/delayed_action.jsx27
-rw-r--r--web/react/utils/emoticons.jsx161
-rw-r--r--web/react/utils/markdown.jsx575
-rw-r--r--web/react/utils/text_formatting.jsx402
-rw-r--r--web/react/utils/utils.jsx1416
213 files changed, 0 insertions, 50180 deletions
diff --git a/web/react/.eslintrc.json b/web/react/.eslintrc.json
deleted file mode 100644
index bc06643c1..000000000
--- a/web/react/.eslintrc.json
+++ /dev/null
@@ -1,226 +0,0 @@
-{
- "extends": "eslint:recommended",
- "parserOptions": {
- "ecmaVersion": 6,
- "sourceType": "module",
- "ecmaFeatures": {
- "jsx": true,
- "impliedStrict": true,
- "modules": true
- }
- },
- "parser": "babel-eslint",
- "plugins": [
- "react"
- ],
- "env": {
- "browser": true,
- "node": true,
- "jquery": true,
- "es6": true
- },
- "globals": {
- "React": false,
- "ReactDOM": false,
- "ReactBootstrap": false,
- "ReactIntl": false,
- "ReactIntlLocaleData": false,
- "Chart": false,
- "katex": false
- },
- "rules": {
- "comma-dangle": [2, "never"],
- "array-callback-return": 2,
- "prefer-rest-params": 2,
- "no-unmodified-loop-condition": 2,
- "sort-imports": 0,
- "no-new-symbol": 2,
- "no-empty-function": 2,
- "no-whitespace-before-property": 2,
- "no-useless-constructor": 2,
- "id-blacklist": 0,
- "one-var-declaration-per-line": 0,
- "no-extra-label": 2,
- "template-curly-spacing": [2, "never"],
- "no-self-assign": 2,
- "newline-per-chained-call": 0,
- "no-confusing-arrow": 2,
- "no-case-declarations": 2,
- "no-cond-assign": [2, "except-parens"],
- "no-console": 2,
- "no-constant-condition": 2,
- "no-debugger": 2,
- "no-dupe-args": 2,
- "no-dupe-keys": 2,
- "no-duplicate-case": 2,
- "no-empty": 2,
- "no-empty-pattern": 2,
- "no-ex-assign": 2,
- "no-extra-semi": 2,
- "no-fallthrough": 2,
- "no-func-assign": 2,
- "no-inner-declarations": 0,
- "no-invalid-regexp": 2,
- "no-irregular-whitespace": 2,
- "no-unexpected-multiline": 2,
- "no-unreachable": 2,
- "no-magic-numbers": [1, { "ignore": [-1, 0, 1, 2], "enforceConst": true, "detectObjects": true } ],
- "valid-typeof": 2,
-
- "block-scoped-var": 2,
- "complexity": [0, 8],
- "consistent-return": 2,
- "curly": [2, "all"],
- "dot-location": [2, "object"],
- "dot-notation": 2,
- "eqeqeq": [2, "smart"],
- "global-require": 2,
- "guard-for-in": 2,
- "no-alert": 2,
- "no-array-constructor": 2,
- "no-caller": 2,
- "no-div-regex": 2,
- "no-else-return": 2,
- "no-eval": 2,
- "no-extend-native": 2,
- "no-extra-bind": 2,
- "no-floating-decimal": 2,
- "no-implied-eval": 2,
- "no-implicit-globals": 0,
- "no-iterator": 2,
- "no-labels": 2,
- "no-lone-blocks": 2,
- "no-loop-func": 2,
- "no-multi-spaces": [2, { "exceptions": { "Property": false } }],
- "no-multi-str": 0,
- "no-native-reassign": 2,
- "no-new": 2,
- "no-new-func": 2,
- "no-new-object": 2,
- "no-new-wrappers": 2,
- "no-octal-escape": 2,
- "no-param-reassign": 2,
- "no-process-env": 2,
- "no-process-exit": 2,
- "no-proto": 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-undef-init": 2,
- "no-unused-expressions": 2,
- "no-useless-concat": 1,
- "no-void": 2,
- "no-warning-comments": 1,
- "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-shadow-restricted-names": 2,
- "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
- "no-use-before-define": [2, "nofunc"],
-
- // Style
- "array-bracket-spacing": [2, "never"],
- "brace-style": [2, "1tbs", { "allowSingleLine": false }],
- "camelcase": [2, {"properties": "never"}],
- "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, {"SwitchCase": 0}],
- "jsx-quotes": [2, "prefer-single"],
- "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
- "linebreak-style": 2,
- "lines-around-comment": [2, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, "allowBlockEnd": true }],
- "new-cap": 2,
- "new-parens": 2,
- "no-lonely-if": 2,
- "no-mixed-spaces-and-tabs": 2,
- "no-multiple-empty-lines": [2, {"max": 1}],
- "no-negated-condition": 2,
- "no-nested-ternary": 2,
- "no-spaced-func": 2,
- "no-ternary": 0,
- "no-trailing-spaces": [2, { "skipBlankLines": false }],
- "no-underscore-dangle": 2,
- "no-unneeded-ternary": [2, {"defaultAssignment": false}],
- "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": [2, "always"],
- "semi-spacing": [2, {"before": false, "after": true}],
- "keyword-spacing": [2, {"before": true, "after": true, "overrides": {}}],
- "space-before-blocks": [2, "always"],
- "space-before-function-paren": [2, "never"],
- "space-in-parens": [2, "never"],
- "space-infix-ops": 2,
- "space-unary-ops": [2, { "words": true, "nonwords": false }],
- "wrap-regex": 2,
-
- // ES6 stuff
- "arrow-parens": [2, "always"],
- "arrow-body-style": 0,
- "arrow-spacing": [2, { "before": true, "after": true }],
- "constructor-super": 2,
- "generator-star-spacing": [2, {"before": false, "after": true}],
- "no-class-assign": 2,
- "no-const-assign": 2,
- "no-dupe-class-members": 2,
- "no-this-before-super": 2,
- "no-var": 0,
- "object-shorthand": [1, "always"],
- "prefer-arrow-callback": 1,
- "prefer-const": 1,
- "prefer-spread": 2,
- "prefer-reflect": 1,
- "prefer-template": 0,
- "require-yield": 2,
-
- // React Specific
- "react/display-name": [2, { "ignoreTranspilerName": false }],
- "react/no-deprecated": 2,
- "react/no-is-mounted": 2,
- "react/no-string-refs": 0,
- "react/jsx-pascal-case": 2,
- "react/jsx-indent": [1, 4],
- "react/jsx-equals-spacing": [2, "never"],
- "react/jsx-handler-names": 0,
- "react/jsx-boolean-value": [2, "always"],
- "react/jsx-closing-bracket-location": [2, { "location": "tag-aligned" }],
- "react/jsx-curly-spacing": [2, "never"],
- "react/jsx-indent-props": [2, 4],
- "react/jsx-key": 2,
- "react/jsx-max-props-per-line": [2, { "maximum": 1 }],
- "react/jsx-no-bind": 1,
- "react/jsx-space-before-closing": [2, "never"],
- "react/jsx-no-duplicate-props": [2, { "ignoreCase": false }],
- "react/jsx-no-literals": 1,
- "react/jsx-no-undef": 2,
- "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-direct-mutation-state": 2,
- "react/no-multi-comp": [2, { "ignoreStateless": true }],
- "react/no-set-state": 0,
- "react/no-unknown-property": 2,
- "react/prefer-es6-class": 2,
- "react/prop-types": 2,
- "react/self-closing-comp": 2,
- "react/sort-comp": 0,
- "react/wrap-multilines": 2
- }
-}
diff --git a/web/react/action_creators/global_actions.jsx b/web/react/action_creators/global_actions.jsx
deleted file mode 100644
index 4375d6c87..000000000
--- a/web/react/action_creators/global_actions.jsx
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export function emitChannelClickEvent(channel) {
- AsyncClient.getChannels(true);
- AsyncClient.getChannelExtraInfo(channel.id);
- AsyncClient.updateLastViewedAt(channel.id);
- AsyncClient.getPosts(channel.id);
-
- AppDispatcher.handleViewAction({
- type: ActionTypes.CLICK_CHANNEL,
- name: channel.name,
- id: channel.id,
- prev: ChannelStore.getCurrentId()
- });
-}
-
-export function emitPostFocusEvent(postId) {
- Client.getPostById(
- postId,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_FOCUSED_POST,
- postId,
- post_list: data
- });
-
- AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
- AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
- }
- );
-}
-
-export function emitPostFocusRightHandSideFromSearch(post, isMentionSearch) {
- Client.getPost(
- post.channel_id,
- post.id,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POSTS,
- id: post.channel_id,
- numRequested: 0,
- post_list: data
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: Utils.getRootId(post),
- from_search: SearchStore.getSearchTerm()
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null,
- is_mention_search: isMentionSearch
- });
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getPost');
- }
- );
-}
-
-export function emitLoadMorePostsEvent() {
- const id = ChannelStore.getCurrentId();
- loadMorePostsTop(id);
-}
-
-export function emitLoadMorePostsFocusedTopEvent() {
- const id = PostStore.getFocusedPostId();
- loadMorePostsTop(id);
-}
-
-export function loadMorePostsTop(id) {
- const earliestPostId = PostStore.getEarliestPost(id).id;
- if (PostStore.requestVisibilityIncrease(id, Constants.POST_CHUNK_SIZE)) {
- AsyncClient.getPostsBefore(earliestPostId, 0, Constants.POST_CHUNK_SIZE);
- }
-}
-
-export function emitLoadMorePostsFocusedBottomEvent() {
- const id = PostStore.getFocusedPostId();
- const latestPostId = PostStore.getLatestPost(id).id;
- AsyncClient.getPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE);
-}
-
-export function emitPostRecievedEvent(post) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post
- });
-}
-
-export function emitUserPostedEvent(post) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.CREATE_POST,
- post
- });
-}
-
-export function emitPostDeletedEvent(post) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.POST_DELETED,
- post
- });
-}
-
-export function showDeletePostModal(post, commentCount = 0) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_DELETE_POST_MODAL,
- value: true,
- post,
- commentCount
- });
-}
-
-export function showGetPostLinkModal(post) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_GET_POST_LINK_MODAL,
- value: true,
- post
- });
-}
-
-export function showGetTeamInviteLinkModal() {
- AppDispatcher.handleViewAction({
- type: Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL,
- value: true
- });
-}
-
-export function showInviteMemberModal() {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_INVITE_MEMBER_MODAL,
- value: true
- });
-}
-
-export function showRegisterAppModal() {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_REGISTER_APP_MODAL,
- value: true
- });
-}
-
-export function emitSuggestionPretextChanged(suggestionId, pretext) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.SUGGESTION_PRETEXT_CHANGED,
- id: suggestionId,
- pretext
- });
-}
-
-export function emitSelectNextSuggestion(suggestionId) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.SUGGESTION_SELECT_NEXT,
- id: suggestionId
- });
-}
-
-export function emitSelectPreviousSuggestion(suggestionId) {
- AppDispatcher.handleViewAction({
- type: ActionTypes.SUGGESTION_SELECT_PREVIOUS,
- id: suggestionId
- });
-}
-
-export function emitCompleteWordSuggestion(suggestionId, term = '') {
- AppDispatcher.handleViewAction({
- type: Constants.ActionTypes.SUGGESTION_COMPLETE_WORD,
- id: suggestionId,
- term
- });
-}
-
-export function emitClearSuggestions(suggestionId) {
- AppDispatcher.handleViewAction({
- type: Constants.ActionTypes.SUGGESTION_CLEAR_SUGGESTIONS,
- id: suggestionId
- });
-}
-
-export function emitPreferenceChangedEvent(preference) {
- AppDispatcher.handleServerAction({
- type: Constants.ActionTypes.RECEIVED_PREFERENCE,
- preference
- });
-}
-
-export function emitRemovePost(post) {
- AppDispatcher.handleViewAction({
- type: Constants.ActionTypes.REMOVE_POST,
- post
- });
-}
-
-export function sendEphemeralPost(message, channelId) {
- const timestamp = Utils.getTimestamp();
- const post = {
- id: Utils.generateId(),
- user_id: '0',
- channel_id: channelId || ChannelStore.getCurrentId(),
- message,
- type: Constants.POST_TYPE_EPHEMERAL,
- create_at: timestamp,
- update_at: timestamp,
- filenames: [],
- props: {}
- };
-
- emitPostRecievedEvent(post);
-}
-
-export function loadTeamRequiredPage() {
- AsyncClient.getAllTeams();
-}
-
-export function newLocalizationSelected(locale) {
- Client.getTranslations(
- locale,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_LOCALE,
- locale,
- translations: data
- });
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getTranslations');
- }
- );
-}
-
-export function viewLoggedIn() {
- AsyncClient.getChannels();
- AsyncClient.getChannelExtraInfo();
- AsyncClient.getMyTeam();
- AsyncClient.getMe();
-
- // Clear pending posts (shouldn't have pending posts if we are loading)
- PostStore.clearPendingPosts();
-}
diff --git a/web/react/components/about_build_modal.jsx b/web/react/components/about_build_modal.jsx
deleted file mode 100644
index 34b1fdccf..000000000
--- a/web/react/components/about_build_modal.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class AboutBuildModal extends React.Component {
- constructor(props) {
- super(props);
- this.doHide = this.doHide.bind(this);
- }
-
- doHide() {
- this.props.onModalDismissed();
- }
-
- render() {
- const config = global.window.mm_config;
- const license = global.window.mm_license;
-
- let title = (
- <FormattedMessage
- id='about.teamEditiont0'
- defaultMessage='Team Edition T0'
- />
- );
-
- let licensee;
- if (config.BuildEnterpriseReady === 'true') {
- title = (
- <FormattedMessage
- id='about.teamEditiont1'
- defaultMessage='Team Edition T1'
- />
- );
- if (license.IsLicensed === 'true') {
- title = (
- <FormattedMessage
- id='about.enterpriseEditione1'
- defaultMessage='Enterprise Edition E1'
- />
- );
- 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>
- );
- }
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='about.title'
- defaultMessage='About Mattermost'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <h4>{'Mattermost'} {title}</h4>
- {licensee}
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.version'
- defaultMessage='Version:'
- />
- </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>
- <div className='col-sm-9'>{config.BuildNumber}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.date'
- defaultMessage='Build Date:'
- />
- </div>
- <div className='col-sm-9'>{config.BuildDate}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='about.hash'
- defaultMessage='Build Hash:'
- />
- </div>
- <div className='col-sm-9'>{config.BuildHash}</div>
- </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>
- );
- }
-}
-
-AboutBuildModal.defaultProps = {
- show: false
-};
-
-AboutBuildModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
deleted file mode 100644
index cd1db32ec..000000000
--- a/web/react/components/access_history_modal.jsx
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import LoadingScreen from './loading_screen.jsx';
-import AuditTable from './audit_table.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, FormattedMessage} from 'mm-intl';
-
-class AccessHistoryModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onAuditChange = this.onAuditChange.bind(this);
- this.onShow = this.onShow.bind(this);
- this.onHide = this.onHide.bind(this);
-
- const state = this.getStateFromStoresForAudits();
- state.moreInfo = [];
-
- this.state = state;
- }
- getStateFromStoresForAudits() {
- return {
- audits: UserStore.getAudits()
- };
- }
- onShow() {
- AsyncClient.getAudits();
-
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
- }
- }
- onHide() {
- this.setState({moreInfo: []});
- this.props.onHide();
- }
- componentDidMount() {
- UserStore.addAuditsChangeListener(this.onAuditChange);
-
- if (this.props.show) {
- this.onShow();
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
- componentWillUnmount() {
- UserStore.removeAuditsChangeListener(this.onAuditChange);
- }
- onAuditChange() {
- var newState = this.getStateFromStoresForAudits();
- if (!Utils.areObjectsEqual(newState.audits, this.state.audits)) {
- this.setState(newState);
- }
- }
- render() {
- var content;
- if (this.state.audits.loading) {
- content = (<LoadingScreen/>);
- } else {
- content = (
- <AuditTable
- audits={this.state.audits}
- showIp={true}
- showSession={true}
- />
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- bsSize='large'
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='access_history.title'
- defaultMessage='Access History'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- {content}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-AccessHistoryModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(AccessHistoryModal);
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
deleted file mode 100644
index db366f8ed..000000000
--- a/web/react/components/activity_log_modal.jsx
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-const Modal = ReactBootstrap.Modal;
-import LoadingScreen from './loading_screen.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage, FormattedTime, FormattedDate} from 'mm-intl';
-
-export default class ActivityLogModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitRevoke = this.submitRevoke.bind(this);
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleMoreInfo = this.handleMoreInfo.bind(this);
- this.onHide = this.onHide.bind(this);
- this.onShow = this.onShow.bind(this);
-
- let state = this.getStateFromStores();
- state.moreInfo = [];
-
- this.state = state;
- }
- getStateFromStores() {
- return {
- sessions: UserStore.getSessions(),
- serverError: null,
- clientError: null
- };
- }
- submitRevoke(altId, e) {
- e.preventDefault();
- var modalContent = $(e.target).closest('.modal-content');
- modalContent.addClass('animation--highlight');
- setTimeout(() => {
- modalContent.removeClass('animation--highlight');
- }, 1500);
- Client.revokeSession(altId,
- function handleRevokeSuccess() {
- AsyncClient.getSessions();
- },
- function handleRevokeError(err) {
- let state = this.getStateFromStores();
- state.serverError = err;
- this.setState(state);
- }.bind(this)
- );
- }
- onShow() {
- AsyncClient.getSessions();
-
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
- }
- }
- onHide() {
- this.setState({moreInfo: []});
- this.props.onHide();
- }
- componentDidMount() {
- UserStore.addSessionsChangeListener(this.onListenerChange);
-
- if (this.props.show) {
- this.onShow();
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
- componentWillUnmount() {
- UserStore.removeSessionsChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) {
- this.setState(newState);
- }
- }
- handleMoreInfo(index) {
- let newMoreInfo = this.state.moreInfo;
- newMoreInfo[index] = true;
- this.setState({moreInfo: newMoreInfo});
- }
- render() {
- let activityList = [];
-
- for (let i = 0; i < this.state.sessions.length; i++) {
- const currentSession = this.state.sessions[i];
- const lastAccessTime = new Date(currentSession.last_activity_at);
- const firstAccessTime = new Date(currentSession.create_at);
- let devicePlatform = currentSession.props.platform;
- let devicePicture = '';
-
- if (currentSession.props.platform === 'Windows') {
- devicePicture = 'fa fa-windows';
- } else if (currentSession.device_id && currentSession.device_id.indexOf('apple:') === 0) {
- devicePicture = 'fa fa-apple';
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.iphoneNativeApp'
- defaultMessage='iPhone Native App'
- />
- );
- } else if (currentSession.device_id && currentSession.device_id.indexOf('android:') === 0) {
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.androidNativeApp'
- defaultMessage='Android Native App'
- />
- );
- devicePicture = 'fa fa-android';
- } else if (currentSession.props.platform === 'Macintosh' ||
- currentSession.props.platform === 'iPhone') {
- devicePicture = 'fa fa-apple';
- } else if (currentSession.props.platform === 'Linux') {
- if (currentSession.props.os.indexOf('Android') >= 0) {
- devicePlatform = (
- <FormattedMessage
- id='activity_log_modal.android'
- defaultMessage='Android'
- />
- );
- devicePicture = 'fa fa-android';
- } else {
- devicePicture = 'fa fa-linux';
- }
- }
-
- let moreInfo;
- if (this.state.moreInfo[i]) {
- moreInfo = (
- <div>
- <div>
- <FormattedMessage
- id='activity_log.firstTime'
- defaultMessage='First time active: {date}, {time}'
- values={{
- date: (
- <FormattedDate
- value={firstAccessTime}
- day='2-digit'
- month='long'
- year='numeric'
- />
- ),
- time: (
- <FormattedTime
- value={firstAccessTime}
- hour='2-digit'
- minute='2-digit'
- />
- )
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.os'
- defaultMessage='OS: {os}'
- values={{
- os: currentSession.props.os
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.browser'
- defaultMessage='Browser: {browser}'
- values={{
- browser: currentSession.props.browser
- }}
- />
- </div>
- <div>
- <FormattedMessage
- id='activity_log.sessionId'
- defaultMessage='Session ID: {id}'
- values={{
- id: currentSession.id
- }}
- />
- </div>
- </div>
- );
- } else {
- moreInfo = (
- <a
- className='theme'
- href='#'
- onClick={this.handleMoreInfo.bind(this, i)}
- >
- <FormattedMessage
- id='activity_log.moreInfo'
- defaultMessage='More info'
- />
- </a>
- );
- }
-
- activityList[i] = (
- <div
- key={'activityLogEntryKey' + i}
- className='activity-log__table'
- >
- <div className='activity-log__report'>
- <div className='report__platform'><i className={devicePicture}/>{devicePlatform}</div>
- <div className='report__info'>
- <div>
- <FormattedMessage
- id='activity_log.lastActivity'
- defaultMessage='Last activity: {date}, {time}'
- values={{
- date: (
- <FormattedDate
- value={lastAccessTime}
- day='2-digit'
- month='long'
- year='numeric'
- />
- ),
- time: (
- <FormattedTime
- value={lastAccessTime}
- hour='2-digit'
- minute='2-digit'
- />
- )
- }}
- />
- </div>
- {moreInfo}
- </div>
- </div>
- <div className='activity-log__action'>
- <button
- onClick={this.submitRevoke.bind(this, currentSession.id)}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='activity_log.logout'
- defaultMessage='Logout'
- />
- </button>
- </div>
- </div>
- );
- }
-
- let content;
- if (this.state.sessions.loading) {
- content = <LoadingScreen/>;
- } else {
- content = <form role='form'>{activityList}</form>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- bsSize='large'
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='activity_log.activeSessions'
- defaultMessage='Active Sessions'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <p className='session-help-text'>
- <FormattedMessage
- id='activity_log.sessionsDescription'
- defaultMessage="Sessions are created when you log in to a new browser on a device. Sessions let you use Mattermost without having to log in again for a time period specified by the System Admin. If you want to log out sooner, use the 'Logout' button below to end a session."
- />
- </p>
- {content}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ActivityLogModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
deleted file mode 100644
index 6dca391d0..000000000
--- a/web/react/components/admin_console/admin_controller.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminSidebar from './admin_sidebar.jsx';
-import AdminStore from '../../stores/admin_store.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-
-import EmailSettingsTab from './email_settings.jsx';
-import LogSettingsTab from './log_settings.jsx';
-import LogsTab from './logs.jsx';
-import AuditsTab from './audits.jsx';
-import FileSettingsTab from './image_settings.jsx';
-import PrivacySettingsTab from './privacy_settings.jsx';
-import RateSettingsTab from './rate_settings.jsx';
-import GitLabSettingsTab from './gitlab_settings.jsx';
-import SqlSettingsTab from './sql_settings.jsx';
-import TeamSettingsTab from './team_settings.jsx';
-import ServiceSettingsTab from './service_settings.jsx';
-import LegalAndSupportSettingsTab from './legal_and_support_settings.jsx';
-import TeamUsersTab from './team_users.jsx';
-import TeamAnalyticsTab from '../analytics/team_analytics.jsx';
-import LdapSettingsTab from './ldap_settings.jsx';
-import LicenseSettingsTab from './license_settings.jsx';
-import SystemAnalyticsTab from '../analytics/system_analytics.jsx';
-
-export default class AdminController extends React.Component {
- constructor(props) {
- super(props);
-
- this.selectTab = this.selectTab.bind(this);
- this.removeSelectedTeam = this.removeSelectedTeam.bind(this);
- this.addSelectedTeam = this.addSelectedTeam.bind(this);
- this.onConfigListenerChange = this.onConfigListenerChange.bind(this);
- this.onAllTeamsListenerChange = this.onAllTeamsListenerChange.bind(this);
-
- var selectedTeams = AdminStore.getSelectedTeams();
- if (selectedTeams == null) {
- selectedTeams = {};
- selectedTeams[TeamStore.getCurrentId()] = 'true';
- AdminStore.saveSelectedTeams(selectedTeams);
- }
-
- this.state = {
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams,
- selected: props.tab || 'system_analytics',
- selectedTeam: props.teamId || null
- };
- }
-
- componentDidMount() {
- AdminStore.addConfigChangeListener(this.onConfigListenerChange);
- AsyncClient.getConfig();
-
- AdminStore.addAllTeamsChangeListener(this.onAllTeamsListenerChange);
- AsyncClient.getAllTeams();
-
- $('[data-toggle="tooltip"]').tooltip();
- $('[data-toggle="popover"]').popover();
- }
-
- componentWillUnmount() {
- AdminStore.removeConfigChangeListener(this.onConfigListenerChange);
- AdminStore.removeAllTeamsChangeListener(this.onAllTeamsListenerChange);
- }
-
- onConfigListenerChange() {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- onAllTeamsListenerChange() {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
-
- });
- }
-
- selectTab(tab, teamId) {
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: tab,
- selectedTeam: teamId
- });
- }
-
- removeSelectedTeam(teamId) {
- var selectedTeams = AdminStore.getSelectedTeams();
- Reflect.deleteProperty(selectedTeams, teamId);
- AdminStore.saveSelectedTeams(selectedTeams);
-
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- addSelectedTeam(teamId) {
- var selectedTeams = AdminStore.getSelectedTeams();
- selectedTeams[teamId] = 'true';
- AdminStore.saveSelectedTeams(selectedTeams);
-
- this.setState({
- config: AdminStore.getConfig(),
- teams: AdminStore.getAllTeams(),
- selectedTeams: AdminStore.getSelectedTeams(),
- selected: this.state.selected,
- selectedTeam: this.state.selectedTeam
- });
- }
-
- render() {
- var tab = <LoadingScreen/>;
-
- if (this.state.config != null) {
- if (this.state.selected === 'email_settings') {
- tab = <EmailSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'log_settings') {
- tab = <LogSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'logs') {
- tab = <LogsTab/>;
- } else if (this.state.selected === 'audits') {
- tab = <AuditsTab/>;
- } else if (this.state.selected === 'image_settings') {
- tab = <FileSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'privacy_settings') {
- tab = <PrivacySettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'rate_settings') {
- tab = <RateSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'gitlab_settings') {
- tab = <GitLabSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'sql_settings') {
- tab = <SqlSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'team_settings') {
- tab = <TeamSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'service_settings') {
- tab = <ServiceSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'legal_and_support_settings') {
- tab = <LegalAndSupportSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'ldap_settings') {
- tab = <LdapSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'license') {
- tab = <LicenseSettingsTab config={this.state.config}/>;
- } else if (this.state.selected === 'team_users') {
- if (this.state.teams) {
- tab = <TeamUsersTab team={this.state.teams[this.state.selectedTeam]}/>;
- }
- } else if (this.state.selected === 'team_analytics') {
- if (this.state.teams) {
- tab = <TeamAnalyticsTab team={this.state.teams[this.state.selectedTeam]}/>;
- }
- } else if (this.state.selected === 'system_analytics') {
- tab = <SystemAnalyticsTab/>;
- }
- }
-
- return (
- <div
- id='admin_controller'
- className='admin-controller'
- >
- <div
- className='sidebar--menu'
- id='sidebar-menu'
- />
- <AdminSidebar
- selected={this.state.selected}
- selectedTeam={this.state.selectedTeam}
- selectTab={this.selectTab}
- teams={this.state.teams}
- selectedTeams={this.state.selectedTeams}
- removeSelectedTeam={this.removeSelectedTeam}
- addSelectedTeam={this.addSelectedTeam}
- />
- <div className='inner-wrap channel__wrap'>
- <div className='row header'>
- </div>
- <div className='row main'>
- <div
- id='app-content'
- className='app__content admin'
- >
- {tab}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-AdminController.defaultProps = {
-};
-
-AdminController.propTypes = {
- tab: React.PropTypes.string,
- teamId: React.PropTypes.string
-};
diff --git a/web/react/components/admin_console/admin_navbar_dropdown.jsx b/web/react/components/admin_console/admin_navbar_dropdown.jsx
deleted file mode 100644
index ae95f5a3a..000000000
--- a/web/react/components/admin_console/admin_navbar_dropdown.jsx
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {Link} from 'react-router';
-
-function getStateFromStores() {
- return {currentTeam: TeamStore.getCurrent()};
-}
-
-export default class AdminNavbarDropdown extends React.Component {
- constructor(props) {
- super(props);
- this.blockToggle = false;
-
- this.state = getStateFromStores();
- }
-
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
- this.blockToggle = true;
- setTimeout(() => {
- this.blockToggle = false;
- }, 100);
- });
- }
-
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
- }
-
- render() {
- return (
- <ul className='nav navbar-nav navbar-right'>
- <li
- ref='dropdown'
- className='dropdown'
- >
- <a
- href='#'
- className='dropdown-toggle'
- data-toggle='dropdown'
- role='button'
- aria-expanded='false'
- >
- <span
- className='dropdown__icon'
- dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}
- />
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- <li>
- <a
- href={Utils.getWindowLocationOrigin() + '/' + this.state.currentTeam.name}
- >
- <FormattedMessage
- id='admin.nav.switch'
- defaultMessage='Switch to {display_name}'
- values={{
- display_name: this.state.currentTeam.display_name
- }}
- />
- </a>
- </li>
- <li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
- <FormattedMessage
- id='admin.nav.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- <li className='divider'></li>
- <li>
- <a
- target='_blank'
- href='/static/help/help.html'
- >
- <FormattedMessage
- id='admin.nav.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- <li>
- <a
- target='_blank'
- href='/static/help/report_problem.html'
- >
- <FormattedMessage
- id='admin.nav.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
deleted file mode 100644
index c2f31f569..000000000
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminSidebarHeader from './admin_sidebar_header.jsx';
-import SelectTeamModal from './select_team_modal.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class AdminSidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.isSelected = this.isSelected.bind(this);
- this.handleClick = this.handleClick.bind(this);
- this.removeTeam = this.removeTeam.bind(this);
-
- this.showTeamSelect = this.showTeamSelect.bind(this);
- this.teamSelectedModal = this.teamSelectedModal.bind(this);
- this.teamSelectedModalDismissed = this.teamSelectedModalDismissed.bind(this);
-
- this.state = {
- showSelectModal: false
- };
- }
-
- handleClick(name, teamId, e) {
- e.preventDefault();
- this.props.selectTab(name, teamId);
- }
-
- isSelected(name, teamId) {
- if (this.props.selected === name) {
- if (name === 'team_users' || name === 'team_analytics') {
- if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
- return 'active';
- }
- } else {
- return 'active';
- }
- }
-
- return '';
- }
-
- removeTeam(teamId, e) {
- e.preventDefault();
- e.stopPropagation();
- Reflect.deleteProperty(this.props.selectedTeams, teamId);
- this.props.removeSelectedTeam(teamId);
-
- if (this.props.selected === 'team_users') {
- if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
- this.props.selectTab('service_settings', null);
- }
- }
- }
-
- componentDidMount() {
- if ($(window).width() > 768) {
- $('.nav-pills__container').perfectScrollbar();
- }
- }
-
- showTeamSelect(e) {
- e.preventDefault();
- this.setState({showSelectModal: true});
- }
-
- teamSelectedModal(teamId) {
- this.setState({showSelectModal: false});
- this.props.addSelectedTeam(teamId);
- this.forceUpdate();
- }
-
- teamSelectedModalDismissed() {
- this.setState({showSelectModal: false});
- }
-
- render() {
- var count = '*';
- var teams = (
- <FormattedMessage
- id='admin.sidebar.loading'
- defaultMessage='Loading'
- />
- );
- const removeTooltip = (
- <Tooltip id='remove-team-tooltip'>
- <FormattedMessage
- id='admin.sidebar.rmTeamSidebar'
- defaultMessage='Remove team from sidebar menu'
- />
- </Tooltip>
- );
- const addTeamTooltip = (
- <Tooltip id='add-team-tooltip'>
- <FormattedMessage
- id='admin.sidebar.addTeamSidebar'
- defaultMessage='Add team from sidebar menu'
- />
- </Tooltip>
- );
-
- if (this.props.teams != null) {
- count = '' + Object.keys(this.props.teams).length;
-
- teams = [];
- for (var key in this.props.selectedTeams) {
- if (this.props.selectedTeams.hasOwnProperty(key)) {
- var team = this.props.teams[key];
-
- if (team != null) {
- teams.push(
- <ul
- key={'team_' + team.id}
- className='nav nav__sub-menu'
- >
- <li>
- <a
- href='#'
- onClick={this.handleClick.bind(this, 'team_users', team.id)}
- className={'nav__sub-menu-item ' + this.isSelected('team_users', team.id) + ' ' + this.isSelected('team_analytics', team.id)}
- >
- {team.name}
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={removeTooltip}
- >
- <span
- className='menu-icon--right menu__close'
- onClick={this.removeTeam.bind(this, team.id)}
- style={{cursor: 'pointer'}}
- >
- {'Ɨ'}
- </span>
- </OverlayTrigger>
- </a>
- </li>
- <li>
- <ul className='nav nav__inner-menu'>
- <li>
- <a
- href='#'
- className={this.isSelected('team_users', team.id)}
- onClick={this.handleClick.bind(this, 'team_users', team.id)}
- >
- <FormattedMessage
- id='admin.sidebar.users'
- defaultMessage='- Users'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('team_analytics', team.id)}
- onClick={this.handleClick.bind(this, 'team_analytics', team.id)}
- >
- <FormattedMessage
- id='admin.sidebar.statistics'
- defaultMessage='- Statistics'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
- }
- }
- }
-
- let ldapSettings;
- let licenseSettings;
- if (global.window.mm_config.BuildEnterpriseReady === 'true') {
- if (global.window.mm_license.IsLicensed === 'true') {
- ldapSettings = (
- <li>
- <a
- href='#'
- className={this.isSelected('ldap_settings')}
- onClick={this.handleClick.bind(this, 'ldap_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.ldap'
- defaultMessage='LDAP Settings'
- />
- </a>
- </li>
- );
- }
-
- licenseSettings = (
- <li>
- <a
- href='#'
- className={this.isSelected('license')}
- onClick={this.handleClick.bind(this, 'license', null)}
- >
- <FormattedMessage
- id='admin.sidebar.license'
- defaultMessage='Edition and License'
- />
- </a>
- </li>
- );
- }
-
- let audits;
- if (global.window.mm_license.IsLicensed === 'true') {
- audits = (
- <li>
- <a
- href='#'
- className={this.isSelected('audits')}
- onClick={this.handleClick.bind(this, 'audits', null)}
- >
- <FormattedMessage
- id='admin.sidebar.audits'
- defaultMessage='Compliance and Auditing'
- />
- </a>
- </li>
- );
- }
-
- return (
- <div className='sidebar--left sidebar--collapsable'>
- <div>
- <AdminSidebarHeader/>
- <div className='nav-pills__container'>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.reports'
- defaultMessage='SITE REPORTS'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- <a
- href='#'
- className={this.isSelected('system_analytics')}
- onClick={this.handleClick.bind(this, 'system_analytics', null)}
- >
- <FormattedMessage
- id='admin.sidebar.view_statistics'
- defaultMessage='View Statistics'
- />
- </a>
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.settings'
- defaultMessage='SETTINGS'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- <a
- href='#'
- className={this.isSelected('service_settings')}
- onClick={this.handleClick.bind(this, 'service_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.service'
- defaultMessage='Service Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('team_settings')}
- onClick={this.handleClick.bind(this, 'team_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.team'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('sql_settings')}
- onClick={this.handleClick.bind(this, 'sql_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.sql'
- defaultMessage='SQL Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('email_settings')}
- onClick={this.handleClick.bind(this, 'email_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.email'
- defaultMessage='Email Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('image_settings')}
- onClick={this.handleClick.bind(this, 'image_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.file'
- defaultMessage='File Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('log_settings')}
- onClick={this.handleClick.bind(this, 'log_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.log'
- defaultMessage='Log Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('rate_settings')}
- onClick={this.handleClick.bind(this, 'rate_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.rate_limit'
- defaultMessage='Rate Limit Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('privacy_settings')}
- onClick={this.handleClick.bind(this, 'privacy_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.privacy'
- defaultMessage='Privacy Settings'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className={this.isSelected('gitlab_settings')}
- onClick={this.handleClick.bind(this, 'gitlab_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.gitlab'
- defaultMessage='GitLab Settings'
- />
- </a>
- </li>
- {ldapSettings}
- <li>
- <a
- href='#'
- className={this.isSelected('legal_and_support_settings')}
- onClick={this.handleClick.bind(this, 'legal_and_support_settings', null)}
- >
- <FormattedMessage
- id='admin.sidebar.support'
- defaultMessage='Legal and Support Settings'
- />
- </a>
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.teams'
- defaultMessage='TEAMS ({count})'
- values={{
- count: count
- }}
- />
- </span>
- <span className='menu-icon--right'>
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={addTeamTooltip}
- >
- <a
- href='#'
- onClick={this.showTeamSelect}
- >
- <i
- className='fa fa-plus'
- ></i>
- </a>
- </OverlayTrigger>
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- <li>
- {teams}
- </li>
- </ul>
- <ul className='nav nav__sub-menu'>
- <li>
- <h4>
- <span className='icon fa fa-gear'></span>
- <span>
- <FormattedMessage
- id='admin.sidebar.other'
- defaultMessage='OTHER'
- />
- </span>
- </h4>
- </li>
- </ul>
- <ul className='nav nav__sub-menu padded'>
- {licenseSettings}
- {audits}
- <li>
- <a
- href='#'
- className={this.isSelected('logs')}
- onClick={this.handleClick.bind(this, 'logs', null)}
- >
- <FormattedMessage
- id='admin.sidebar.logs'
- defaultMessage='Logs'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- </div>
- </div>
-
- <SelectTeamModal
- teams={this.props.teams}
- show={this.state.showSelectModal}
- onModalSubmit={this.teamSelectedModal}
- onModalDismissed={this.teamSelectedModalDismissed}
- />
- </div>
- );
- }
-}
-
-AdminSidebar.propTypes = {
- teams: React.PropTypes.object,
- selectedTeams: React.PropTypes.object,
- removeSelectedTeam: React.PropTypes.func,
- addSelectedTeam: React.PropTypes.func,
- selected: React.PropTypes.string,
- selectedTeam: React.PropTypes.string,
- selectTab: React.PropTypes.func
-};
diff --git a/web/react/components/admin_console/admin_sidebar_header.jsx b/web/react/components/admin_console/admin_sidebar_header.jsx
deleted file mode 100644
index f1281c6ee..000000000
--- a/web/react/components/admin_console/admin_sidebar_header.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
-import UserStore from '../../stores/user_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SidebarHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.toggleDropdown = this.toggleDropdown.bind(this);
-
- this.state = {};
- }
-
- toggleDropdown(e) {
- e.preventDefault();
-
- if (this.refs.dropdown.blockToggle) {
- this.refs.dropdown.blockToggle = false;
- return;
- }
-
- $('.team__header').find('.dropdown-toggle').dropdown('toggle');
- }
-
- render() {
- var me = UserStore.getCurrentUser();
- var profilePicture = null;
-
- if (!me) {
- return null;
- }
-
- if (me.last_picture_update) {
- profilePicture = (
- <img
- className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
- />
- );
- }
-
- return (
- <div className='team__header theme'>
- <a
- href='#'
- onClick={this.toggleDropdown}
- >
- {profilePicture}
- <div className='header__info'>
- <div className='user__name'>{'@' + me.username}</div>
- <div className='team__name'>
- <FormattedMessage
- id='admin.sidebarHeader.systemConsole'
- defaultMessage='System Console'
- />
- </div>
- </div>
- </a>
- <AdminNavbarDropdown ref='dropdown'/>
- </div>
- );
- }
-}
diff --git a/web/react/components/admin_console/audits.jsx b/web/react/components/admin_console/audits.jsx
deleted file mode 100644
index c519e0a23..000000000
--- a/web/react/components/admin_console/audits.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoadingScreen from '../loading_screen.jsx';
-import AuditTable from '../audit_table.jsx';
-
-import AdminStore from '../../stores/admin_store.jsx';
-
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class Audits extends React.Component {
- constructor(props) {
- super(props);
-
- this.onAuditListenerChange = this.onAuditListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
- this.state = {
- audits: AdminStore.getAudits()
- };
- }
-
- componentDidMount() {
- AdminStore.addAuditChangeListener(this.onAuditListenerChange);
- AsyncClient.getServerAudits();
- }
-
- componentWillUnmount() {
- AdminStore.removeAuditChangeListener(this.onAuditListenerChange);
- }
-
- onAuditListenerChange() {
- this.setState({
- audits: AdminStore.getAudits()
- });
- }
-
- reload() {
- AdminStore.saveAudits(null);
- this.setState({
- audits: null
- });
-
- AsyncClient.getServerAudits();
- }
-
- render() {
- var content = null;
-
- if (global.window.mm_license.IsLicensed !== 'true') {
- return <div/>;
- }
-
- if (this.state.audits === null) {
- content = <LoadingScreen/>;
- } else {
- content = (
- <div style={{margin: '10px'}}>
- <AuditTable
- audits={this.state.audits}
- showUserId={true}
- showIp={true}
- showSession={true}
- />
- </div>
- );
- }
-
- return (
- <div className='panel'>
- <h3>
- <FormattedMessage
- id='admin.audits.title'
- defaultMessage='User Activity'
- />
- </h3>
- <button
- type='submit'
- className='btn btn-primary'
- onClick={this.reload}
- >
- <FormattedMessage
- id='admin.audits.reload'
- defaultMessage='Reload'
- />
- </button>
- <div className='log__panel'>
- {content}
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
deleted file mode 100644
index 1d8f9c1dc..000000000
--- a/web/react/components/admin_console/email_settings.jsx
+++ /dev/null
@@ -1,957 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-var holders = defineMessages({
- notificationDisplayExample: {
- id: 'admin.email.notificationDisplayExample',
- defaultMessage: 'Ex: "Mattermost Notification", "System", "No-Reply"'
- },
- notificationEmailExample: {
- id: 'admin.email.notificationEmailExample',
- defaultMessage: 'Ex: "mattermost@yourcompany.com", "admin@yourcompany.com"'
- },
- smtpUsernameExample: {
- id: 'admin.email.smtpUsernameExample',
- defaultMessage: 'Ex: "admin@yourcompany.com", "AKIADTOVBGERKLCBV"'
- },
- smtpPasswordExample: {
- id: 'admin.email.smtpPasswordExample',
- defaultMessage: 'Ex: "yourpassword", "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- smtpServerExample: {
- id: 'admin.email.smtpServerExample',
- defaultMessage: 'Ex: "smtp.yourcompany.com", "email-smtp.us-east-1.amazonaws.com"'
- },
- smtpPortExample: {
- id: 'admin.email.smtpPortExample',
- defaultMessage: 'Ex: "25", "465"'
- },
- connectionSecurityNone: {
- id: 'admin.email.connectionSecurityNone',
- defaultMessage: 'None'
- },
- connectionSecurityTls: {
- id: 'admin.email.connectionSecurityTls',
- defaultMessage: 'TLS (Recommended)'
- },
- connectionSecurityStart: {
- id: 'admin.email.connectionSecurityStart',
- defaultMessage: 'STARTTLS'
- },
- inviteSaltExample: {
- id: 'admin.email.inviteSaltExample',
- defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
- },
- passwordSaltExample: {
- id: 'admin.email.passwordSaltExample',
- defaultMessage: 'Ex "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo"'
- },
- pushServerEx: {
- id: 'admin.email.pushServerEx',
- defaultMessage: 'E.g.: "http://push-test.mattermost.com"'
- },
- testing: {
- id: 'admin.email.testing',
- defaultMessage: 'Testing...'
- },
- saving: {
- id: 'admin.email.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class EmailSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleTestConnection = this.handleTestConnection.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.buildConfig = this.buildConfig.bind(this);
- this.handleGenerateInvite = this.handleGenerateInvite.bind(this);
- this.handleGenerateReset = this.handleGenerateReset.bind(this);
-
- this.state = {
- sendEmailNotifications: this.props.config.EmailSettings.SendEmailNotifications,
- sendPushNotifications: this.props.config.EmailSettings.SendPushNotifications,
- saveNeeded: false,
- serverError: null,
- emailSuccess: null,
- emailFail: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'sendEmailNotifications_true') {
- s.sendEmailNotifications = true;
- }
-
- if (action === 'sendEmailNotifications_false') {
- s.sendEmailNotifications = false;
- }
-
- if (action === 'sendPushNotifications_true') {
- s.sendPushNotifications = true;
- }
-
- if (action === 'sendPushNotifications_false') {
- s.sendPushNotifications = false;
- }
-
- this.setState(s);
- }
-
- buildConfig() {
- var config = this.props.config;
- config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked;
- config.EmailSettings.EnableSignInWithEmail = ReactDOM.findDOMNode(this.refs.allowSignInWithEmail).checked;
- config.EmailSettings.EnableSignInWithUsername = ReactDOM.findDOMNode(this.refs.allowSignInWithUsername).checked;
- config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked;
- config.EmailSettings.SendPushNotifications = ReactDOM.findDOMNode(this.refs.sendPushNotifications).checked;
- config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked;
- config.EmailSettings.FeedbackName = ReactDOM.findDOMNode(this.refs.feedbackName).value.trim();
- config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim();
- config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim();
- config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).value.trim();
- config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim();
- config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim();
- config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim();
- config.EmailSettings.ConnectionSecurity = ReactDOM.findDOMNode(this.refs.ConnectionSecurity).value.trim();
-
- config.EmailSettings.InviteSalt = ReactDOM.findDOMNode(this.refs.InviteSalt).value.trim();
- if (config.EmailSettings.InviteSalt === '') {
- config.EmailSettings.InviteSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.InviteSalt).value = config.EmailSettings.InviteSalt;
- }
-
- config.EmailSettings.PasswordResetSalt = ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value.trim();
- if (config.EmailSettings.PasswordResetSalt === '') {
- config.EmailSettings.PasswordResetSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = config.EmailSettings.PasswordResetSalt;
- }
-
- return config;
- }
-
- handleGenerateInvite(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.InviteSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleGenerateReset(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleTestConnection(e) {
- e.preventDefault();
- $('#connection-button').button('loading');
-
- var config = this.buildConfig();
-
- Client.testEmail(
- config,
- () => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: true,
- emailSuccess: true,
- emailFail: null
- });
- $('#connection-button').button('reset');
- },
- (err) => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: true,
- emailSuccess: null,
- emailFail: err.message + ' - ' + err.detailed_error
- });
- $('#connection-button').button('reset');
- }
- );
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.buildConfig();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: null,
- saveNeeded: false,
- emailSuccess: null,
- emailFail: null
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- sendEmailNotifications: config.EmailSettings.SendEmailNotifications,
- serverError: err.message,
- saveNeeded: true,
- emailSuccess: null,
- emailFail: null
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var emailSuccess = '';
- if (this.state.emailSuccess) {
- emailSuccess = (
- <div className='alert alert-success'>
- <i className='fa fa-check'></i>
- <FormattedMessage
- id='admin.email.emailSuccess'
- defaultMessage='No errors were reported while sending an email. Please check your inbox to make sure.'
- />
- </div>
- );
- }
-
- var emailFail = '';
- if (this.state.emailFail) {
- emailSuccess = (
- <div className='alert alert-warning'>
- <i className='fa fa-warning'></i>
- <FormattedMessage
- id='admin.email.emailFail'
- defaultMessage='Connection unsuccessful: {error}'
- values={{
- error: this.state.emailFail
- }}
- />
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.email.emailSettings'
- defaultMessage='Email Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignUpWithEmail'
- >
- <FormattedMessage
- id='admin.email.allowSignupTitle'
- defaultMessage='Allow Sign Up With Email: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignUpWithEmail'
- value='true'
- ref='allowSignUpWithEmail'
- defaultChecked={this.props.config.EmailSettings.EnableSignUpWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignUpWithEmail'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignUpWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignUpWithEmail_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowSignupDescription'
- defaultMessage='When true, Mattermost allows team creation and account signup using email and password. This value should be false only when you want to limit signup to a single-sign-on service like OAuth or LDAP.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignInWithEmail'
- >
- <FormattedMessage
- id='admin.email.allowEmailSignInTitle'
- defaultMessage='Allow Sign In With Email: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithEmail'
- value='true'
- ref='allowSignInWithEmail'
- defaultChecked={this.props.config.EmailSettings.EnableSignInWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignInWithEmail_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithEmail'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignInWithEmail}
- onChange={this.handleChange.bind(this, 'allowSignInWithEmail_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowEmailSignInDescription'
- defaultMessage='When true, Mattermost allows users to sign in using their email and password.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='allowSignInWithUsername'
- >
- <FormattedMessage
- id='admin.email.allowUsernameSignInTitle'
- defaultMessage='Allow Sign In With Username: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithUsername'
- value='true'
- ref='allowSignInWithUsername'
- defaultChecked={this.props.config.EmailSettings.EnableSignInWithUsername}
- onChange={this.handleChange.bind(this, 'allowSignInWithUsername_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='allowSignInWithUsername'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.EnableSignInWithUsername}
- onChange={this.handleChange.bind(this, 'allowSignInWithUsername_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.allowUsernameSignInDescription'
- defaultMessage='When true, Mattermost allows users to sign in using their username and password. This setting is typically only used when email verification is disabled.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='sendEmailNotifications'
- >
- <FormattedMessage
- id='admin.email.notificationsTitle'
- defaultMessage='Send Email Notifications: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendEmailNotifications'
- value='true'
- ref='sendEmailNotifications'
- defaultChecked={this.props.config.EmailSettings.SendEmailNotifications}
- onChange={this.handleChange.bind(this, 'sendEmailNotifications_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendEmailNotifications'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.SendEmailNotifications}
- onChange={this.handleChange.bind(this, 'sendEmailNotifications_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.email.notificationsDescription'
- defaultMessage='Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.<br />Setting this to true removes the Preview Mode banner (requires logging out and logging back in after setting is changed).'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='requireEmailVerification'
- >
- <FormattedMessage
- id='admin.email.requireVerificationTitle'
- defaultMessage='Require Email Verification: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='requireEmailVerification'
- value='true'
- ref='requireEmailVerification'
- defaultChecked={this.props.config.EmailSettings.RequireEmailVerification}
- onChange={this.handleChange.bind(this, 'requireEmailVerification_true')}
- disabled={!this.state.sendEmailNotifications}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='requireEmailVerification'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.RequireEmailVerification}
- onChange={this.handleChange.bind(this, 'requireEmailVerification_false')}
- disabled={!this.state.sendEmailNotifications}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.requireVerificationDescription'
- defaultMessage='Typically set to true in production. When true, Mattermost requires email verification after account creation prior to allowing login. Developers may set this field to false so skip sending verification emails for faster development.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='feedbackName'
- >
- <FormattedMessage
- id='admin.email.notificationDisplayTitle'
- defaultMessage='Notification Display Name:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='feedbackName'
- ref='feedbackName'
- placeholder={formatMessage(holders.notificationDisplayExample)}
- defaultValue={this.props.config.EmailSettings.FeedbackName}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.notificationDisplayDescription'
- defaultMessage='Display name on email account used when sending notification emails from Mattermost.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='feedbackEmail'
- >
- <FormattedMessage
- id='admin.email.notificationEmailTitle'
- defaultMessage='Notification Email Address:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='email'
- className='form-control'
- id='feedbackEmail'
- ref='feedbackEmail'
- placeholder={formatMessage(holders.notificationEmailExample)}
- defaultValue={this.props.config.EmailSettings.FeedbackEmail}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.notificationEmailDescription'
- defaultMessage='Email address displayed on email account used when sending notification emails from Mattermost.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPUsername'
- >
- <FormattedMessage
- id='admin.email.smtpUsernameTitle'
- defaultMessage='SMTP Username:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPUsername'
- ref='SMTPUsername'
- placeholder={formatMessage(holders.smtpUsernameExample)}
- defaultValue={this.props.config.EmailSettings.SMTPUsername}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpUsernameDescription'
- defaultMessage=' Obtain this credential from administrator setting up your email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPPassword'
- >
- <FormattedMessage
- id='admin.email.smtpPasswordTitle'
- defaultMessage='SMTP Password:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPPassword'
- ref='SMTPPassword'
- placeholder={formatMessage(holders.smtpPasswordExample)}
- defaultValue={this.props.config.EmailSettings.SMTPPassword}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpPasswordDescription'
- defaultMessage=' Obtain this credential from administrator setting up your email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPServer'
- >
- <FormattedMessage
- id='admin.email.smtpServerTitle'
- defaultMessage='SMTP Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPServer'
- ref='SMTPServer'
- placeholder={formatMessage(holders.smtpServerExample)}
- defaultValue={this.props.config.EmailSettings.SMTPServer}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpServerDescription'
- defaultMessage='Location of SMTP email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SMTPPort'
- >
- <FormattedMessage
- id='admin.email.smtpPortTitle'
- defaultMessage='SMTP Port:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SMTPPort'
- ref='SMTPPort'
- placeholder={formatMessage(holders.smtpPortExample)}
- defaultValue={this.props.config.EmailSettings.SMTPPort}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.smtpPortDescription'
- defaultMessage='Port of SMTP email server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ConnectionSecurity'
- >
- <FormattedMessage
- id='admin.email.connectionSecurityTitle'
- defaultMessage='Connection Security:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='ConnectionSecurity'
- ref='ConnectionSecurity'
- defaultValue={this.props.config.EmailSettings.ConnectionSecurity}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- >
- <option value=''>{formatMessage(holders.connectionSecurityNone)}</option>
- <option value='TLS'>{formatMessage(holders.connectionSecurityTls)}</option>
- <option value='STARTTLS'>{formatMessage(holders.connectionSecurityStart)}</option>
- </select>
- <div className='help-text'>
- <table
- className='table table-bordered'
- cellPadding='5'
- >
- <tbody>
- <tr><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityNone'
- defaultMessage='None'
- />
- </td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityNoneDescription'
- defaultMessage='Mattermost will send email over an unsecure connection.'
- />
- </td></tr>
- <tr><td className='help-text'>{'TLS'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityTlsDescription'
- defaultMessage='Encrypts the communication between Mattermost and your email server.'
- />
- </td></tr>
- <tr><td className='help-text'>{'STARTTLS'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.email.connectionSecurityStartDescription'
- defaultMessage='Takes an existing insecure connection and attempts to upgrade it to a secure connection using TLS.'
- />
- </td></tr>
- </tbody>
- </table>
- </div>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleTestConnection}
- disabled={!this.state.sendEmailNotifications}
- id='connection-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.testing)}
- >
- <FormattedMessage
- id='admin.email.connectionSecurityTest'
- defaultMessage='Test Connection'
- />
- </button>
- {emailSuccess}
- {emailFail}
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='InviteSalt'
- >
- <FormattedMessage
- id='admin.email.inviteSaltTitle'
- defaultMessage='Invite Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='InviteSalt'
- ref='InviteSalt'
- placeholder={formatMessage(holders.inviteSaltExample)}
- defaultValue={this.props.config.EmailSettings.InviteSalt}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.inviteSaltDescription'
- defaultMessage='32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateInvite}
- disabled={!this.state.sendEmailNotifications}
- >
- <FormattedMessage
- id='admin.email.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PasswordResetSalt'
- >
- <FormattedMessage
- id='admin.email.passwordSaltTitle'
- defaultMessage='Password Reset Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PasswordResetSalt'
- ref='PasswordResetSalt'
- placeholder={formatMessage(holders.passwordSaltExample)}
- defaultValue={this.props.config.EmailSettings.PasswordResetSalt}
- onChange={this.handleChange}
- disabled={!this.state.sendEmailNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.passwordSaltDescription'
- defaultMessage='32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateReset}
- disabled={!this.state.sendEmailNotifications}
- >
- <FormattedMessage
- id='admin.email.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='sendPushNotifications'
- >
- <FormattedMessage
- id='admin.email.pushTitle'
- defaultMessage='Send Push Notifications: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='true'
- ref='sendPushNotifications'
- defaultChecked={this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_true')}
- />
- <FormattedMessage
- id='admin.email.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='sendPushNotifications'
- value='false'
- defaultChecked={!this.props.config.EmailSettings.SendPushNotifications}
- onChange={this.handleChange.bind(this, 'sendPushNotifications_false')}
- />
- <FormattedMessage
- id='admin.email.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushDesc'
- defaultMessage='Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PushNotificationServer'
- >
- <FormattedMessage
- id='admin.email.pushServerTitle'
- defaultMessage='Push Notification Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PushNotificationServer'
- ref='PushNotificationServer'
- placeholder={formatMessage(holders.pushServerEx)}
- defaultValue={this.props.config.EmailSettings.PushNotificationServer}
- onChange={this.handleChange}
- disabled={!this.state.sendPushNotifications}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.email.pushServerDesc'
- defaultMessage='Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use http://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.email.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-EmailSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(EmailSettings);
diff --git a/web/react/components/admin_console/gitlab_settings.jsx b/web/react/components/admin_console/gitlab_settings.jsx
deleted file mode 100644
index ad9950eea..000000000
--- a/web/react/components/admin_console/gitlab_settings.jsx
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- clientIdExample: {
- id: 'admin.gitlab.clientIdExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- clientSecretExample: {
- id: 'admin.gitlab.clientSecretExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- authExample: {
- id: 'admin.gitlab.authExample',
- defaultMessage: 'Ex ""'
- },
- tokenExample: {
- id: 'admin.gitlab.tokenExample',
- defaultMessage: 'Ex ""'
- },
- userExample: {
- id: 'admin.gitlab.userExample',
- defaultMessage: 'Ex ""'
- },
- saving: {
- id: 'admin.gitlab.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class GitLabSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- Enable: this.props.config.GitLabSettings.Enable,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'EnableTrue') {
- s.Enable = true;
- }
-
- if (action === 'EnableFalse') {
- s.Enable = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.GitLabSettings.Enable = ReactDOM.findDOMNode(this.refs.Enable).checked;
- config.GitLabSettings.Secret = ReactDOM.findDOMNode(this.refs.Secret).value.trim();
- config.GitLabSettings.Id = ReactDOM.findDOMNode(this.refs.Id).value.trim();
- config.GitLabSettings.AuthEndpoint = ReactDOM.findDOMNode(this.refs.AuthEndpoint).value.trim();
- config.GitLabSettings.TokenEndpoint = ReactDOM.findDOMNode(this.refs.TokenEndpoint).value.trim();
- config.GitLabSettings.UserApiEndpoint = ReactDOM.findDOMNode(this.refs.UserApiEndpoint).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.gitlab.settingsTitle'
- defaultMessage='GitLab Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Enable'
- >
- <FormattedMessage
- id='admin.gitlab.enableTitle'
- defaultMessage='Enable Sign Up With GitLab: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='true'
- ref='Enable'
- defaultChecked={this.props.config.GitLabSettings.Enable}
- onChange={this.handleChange.bind(this, 'EnableTrue')}
- />
- <FormattedMessage
- id='admin.gitlab.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='false'
- defaultChecked={!this.props.config.GitLabSettings.Enable}
- onChange={this.handleChange.bind(this, 'EnableFalse')}
- />
- <FormattedMessage
- id='admin.gitlab.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.enableDescription'
- defaultMessage='When true, Mattermost allows team creation and account signup using GitLab OAuth.'
- />
- <br/>
- </p>
- <div className='help-text'>
- <FormattedHTMLMessage
- id='admin.gitlab.EnableHtmlDesc'
- defaultMessage='<ol><li>Log in to your GitLab account and go to Profile Settings -> Applications.</li><li>Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". </li><li>Then use "Secret" and "Id" fields from GitLab to complete the options below.</li><li>Complete the Endpoint URLs below. </li></ol>'
- />
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Id'
- >
- <FormattedMessage
- id='admin.gitlab.clientIdTitle'
- defaultMessage='Id:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Id'
- ref='Id'
- placeholder={formatMessage(holders.clientIdExample)}
- defaultValue={this.props.config.GitLabSettings.Id}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.clientIdDescription'
- defaultMessage='Obtain this value via the instructions above for logging into GitLab'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Secret'
- >
- <FormattedMessage
- id='admin.gitlab.clientSecretTitle'
- defaultMessage='Secret:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Secret'
- ref='Secret'
- placeholder={formatMessage(holders.clientSecretExample)}
- defaultValue={this.props.config.GitLabSettings.Secret}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitab.clientSecretDescription'
- defaultMessage='Obtain this value via the instructions above for logging into GitLab.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AuthEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.authTitle'
- defaultMessage='Auth Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AuthEndpoint'
- ref='AuthEndpoint'
- placeholder={formatMessage(holders.authExample)}
- defaultValue={this.props.config.GitLabSettings.AuthEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.authDescription'
- defaultMessage='Enter https://<your-gitlab-url>/oauth/authorize (example https://example.com:3000/oauth/authorize). Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='TokenEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.tokenTitle'
- defaultMessage='Token Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='TokenEndpoint'
- ref='TokenEndpoint'
- placeholder={formatMessage(holders.tokenExample)}
- defaultValue={this.props.config.GitLabSettings.TokenEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.tokenDescription'
- defaultMessage='Enter https://<your-gitlab-url>/oauth/token. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='UserApiEndpoint'
- >
- <FormattedMessage
- id='admin.gitlab.userTitle'
- defaultMessage='User API Endpoint:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='UserApiEndpoint'
- ref='UserApiEndpoint'
- placeholder={formatMessage(holders.userExample)}
- defaultValue={this.props.config.GitLabSettings.UserApiEndpoint}
- onChange={this.handleChange}
- disabled={!this.state.Enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.gitlab.userDescription'
- defaultMessage='Enter https://<your-gitlab-url>/api/v3/user. Make sure you use HTTP or HTTPS in your URL depending on your server configuration.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.gitlab.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-//config.GitLabSettings.Scope = ReactDOM.findDOMNode(this.refs.Scope).value.trim();
-// <div className='form-group'>
-// <label
-// className='control-label col-sm-4'
-// htmlFor='Scope'
-// >
-// {'Scope:'}
-// </label>
-// <div className='col-sm-8'>
-// <input
-// type='text'
-// className='form-control'
-// id='Scope'
-// ref='Scope'
-// placeholder='Not currently used by GitLab. Please leave blank'
-// defaultValue={this.props.config.GitLabSettings.Scope}
-// onChange={this.handleChange}
-// disabled={!this.state.Allow}
-// />
-// <p className='help-text'>{'This field is not yet used by GitLab OAuth. Other OAuth providers may use this field to specify the scope of account data from OAuth provider that is sent to Mattermost.'}</p>
-// </div>
-// </div>
-
-GitLabSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(GitLabSettings);
diff --git a/web/react/components/admin_console/image_settings.jsx b/web/react/components/admin_console/image_settings.jsx
deleted file mode 100644
index 86f78e093..000000000
--- a/web/react/components/admin_console/image_settings.jsx
+++ /dev/null
@@ -1,688 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- storeLocal: {
- id: 'admin.image.storeLocal',
- defaultMessage: 'Local File System'
- },
- storeAmazonS3: {
- id: 'admin.image.storeAmazonS3',
- defaultMessage: 'Amazon S3'
- },
- localExample: {
- id: 'admin.image.localExample',
- defaultMessage: 'Ex "./data/"'
- },
- amazonS3IdExample: {
- id: 'admin.image.amazonS3IdExample',
- defaultMessage: 'Ex "AKIADTOVBGERKLCBV"'
- },
- amazonS3SecretExample: {
- id: 'admin.image.amazonS3SecretExample',
- defaultMessage: 'Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
- },
- amazonS3BucketExample: {
- id: 'admin.image.amazonS3BucketExample',
- defaultMessage: 'Ex "mattermost-media"'
- },
- amazonS3RegionExample: {
- id: 'admin.image.amazonS3RegionExample',
- defaultMessage: 'Ex "us-east-1"'
- },
- thumbWidthExample: {
- id: 'admin.image.thumbWidthExample',
- defaultMessage: 'Ex "120"'
- },
- thumbHeightExample: {
- id: 'admin.image.thumbHeightExample',
- defaultMessage: 'Ex "100"'
- },
- previewWidthExample: {
- id: 'admin.image.previewWidthExample',
- defaultMessage: 'Ex "1024"'
- },
- previewHeightExample: {
- id: 'admin.image.previewHeightExample',
- defaultMessage: 'Ex "0"'
- },
- profileWidthExample: {
- id: 'admin.image.profileWidthExample',
- defaultMessage: 'Ex "1024"'
- },
- profileHeightExample: {
- id: 'admin.image.profileHeightExample',
- defaultMessage: 'Ex "0"'
- },
- publicLinkExample: {
- id: 'admin.image.publicLinkExample',
- defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
- },
- saving: {
- id: 'admin.image.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class FileSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleGenerate = this.handleGenerate.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null,
- DriverName: this.props.config.FileSettings.DriverName
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'DriverName') {
- s.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
- }
-
- this.setState(s);
- }
-
- handleGenerate(e) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.FileSettings.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
- config.FileSettings.Directory = ReactDOM.findDOMNode(this.refs.Directory).value;
- config.FileSettings.AmazonS3AccessKeyId = ReactDOM.findDOMNode(this.refs.AmazonS3AccessKeyId).value;
- config.FileSettings.AmazonS3SecretAccessKey = ReactDOM.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
- config.FileSettings.AmazonS3Bucket = ReactDOM.findDOMNode(this.refs.AmazonS3Bucket).value;
- config.FileSettings.AmazonS3Region = ReactDOM.findDOMNode(this.refs.AmazonS3Region).value;
- config.FileSettings.EnablePublicLink = ReactDOM.findDOMNode(this.refs.EnablePublicLink).checked;
-
- config.FileSettings.PublicLinkSalt = ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value.trim();
-
- if (config.FileSettings.PublicLinkSalt === '') {
- config.FileSettings.PublicLinkSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = config.FileSettings.PublicLinkSalt;
- }
-
- var thumbnailWidth = 120;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
- thumbnailWidth = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10);
- }
- config.FileSettings.ThumbnailWidth = thumbnailWidth;
- ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value = thumbnailWidth;
-
- var thumbnailHeight = 100;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10))) {
- thumbnailHeight = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10);
- }
- config.FileSettings.ThumbnailHeight = thumbnailHeight;
- ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value = thumbnailHeight;
-
- var previewWidth = 1024;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10))) {
- previewWidth = parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10);
- }
- config.FileSettings.PreviewWidth = previewWidth;
- ReactDOM.findDOMNode(this.refs.PreviewWidth).value = previewWidth;
-
- var previewHeight = 0;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10))) {
- previewHeight = parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10);
- }
- config.FileSettings.PreviewHeight = previewHeight;
- ReactDOM.findDOMNode(this.refs.PreviewHeight).value = previewHeight;
-
- var profileWidth = 128;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10))) {
- profileWidth = parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10);
- }
- config.FileSettings.ProfileWidth = profileWidth;
- ReactDOM.findDOMNode(this.refs.ProfileWidth).value = profileWidth;
-
- var profileHeight = 128;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10))) {
- profileHeight = parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10);
- }
- config.FileSettings.ProfileHeight = profileHeight;
- ReactDOM.findDOMNode(this.refs.ProfileHeight).value = profileHeight;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var enableFile = false;
- var enableS3 = false;
-
- if (this.state.DriverName === 'local') {
- enableFile = true;
- }
-
- if (this.state.DriverName === 'amazons3') {
- enableS3 = true;
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.image.fileSettings'
- defaultMessage='File Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DriverName'
- >
- <FormattedMessage
- id='admin.image.storeTitle'
- defaultMessage='Store Files In:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='DriverName'
- ref='DriverName'
- defaultValue={this.props.config.FileSettings.DriverName}
- onChange={this.handleChange.bind(this, 'DriverName')}
- >
- <option value='local'>{formatMessage(holders.storeLocal)}</option>
- <option value='amazons3'>{formatMessage(holders.storeAmazonS3)}</option>
- </select>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Directory'
- >
- <FormattedMessage
- id='admin.image.localTitle'
- defaultMessage='Local Directory Location:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='Directory'
- ref='Directory'
- placeholder={formatMessage(holders.localExample)}
- defaultValue={this.props.config.FileSettings.Directory}
- onChange={this.handleChange}
- disabled={!enableFile}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.localDescription'
- defaultMessage='Directory to which image files are written. If blank, will be set to ./data/.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3AccessKeyId'
- >
- <FormattedMessage
- id='admin.image.amazonS3IdTitle'
- defaultMessage='Amazon S3 Access Key Id:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3AccessKeyId'
- ref='AmazonS3AccessKeyId'
- placeholder={formatMessage(holders.amazonS3IdExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3AccessKeyId}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3IdDescription'
- defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3SecretAccessKey'
- >
- <FormattedMessage
- id='admin.image.amazonS3SecretTitle'
- defaultMessage='Amazon S3 Secret Access Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3SecretAccessKey'
- ref='AmazonS3SecretAccessKey'
- placeholder={formatMessage(holders.amazonS3SecretExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3SecretAccessKey}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3SecretDescription'
- defaultMessage='Obtain this credential from your Amazon EC2 administrator.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3Bucket'
- >
- <FormattedMessage
- id='admin.image.amazonS3BucketTitle'
- defaultMessage='Amazon S3 Bucket:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3Bucket'
- ref='AmazonS3Bucket'
- placeholder={formatMessage(holders.amazonS3BucketExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3Bucket}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3BucketDescription'
- defaultMessage='Name you selected for your S3 bucket in AWS.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AmazonS3Region'
- >
- <FormattedMessage
- id='admin.image.amazonS3RegionTitle'
- defaultMessage='Amazon S3 Region:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AmazonS3Region'
- ref='AmazonS3Region'
- placeholder={formatMessage(holders.amazonS3RegionExample)}
- defaultValue={this.props.config.FileSettings.AmazonS3Region}
- onChange={this.handleChange}
- disabled={!enableS3}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.amazonS3RegionDescription'
- defaultMessage='AWS region you selected for creating your S3 bucket.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ThumbnailWidth'
- >
- <FormattedMessage
- id='admin.image.thumbWidthTitle'
- defaultMessage='Thumbnail Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ThumbnailWidth'
- ref='ThumbnailWidth'
- placeholder={formatMessage(holders.thumbWidthExample)}
- defaultValue={this.props.config.FileSettings.ThumbnailWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.thumbWidthDescription'
- defaultMessage='Width of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ThumbnailHeight'
- >
- <FormattedMessage
- id='admin.image.thumbHeightTitle'
- defaultMessage='Thumbnail Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ThumbnailHeight'
- ref='ThumbnailHeight'
- placeholder={formatMessage(holders.thumbHeightExample)}
- defaultValue={this.props.config.FileSettings.ThumbnailHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.thumbHeightDescription'
- defaultMessage='Height of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PreviewWidth'
- >
- <FormattedMessage
- id='admin.image.previewWidthTitle'
- defaultMessage='Preview Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PreviewWidth'
- ref='PreviewWidth'
- placeholder={formatMessage(holders.previewWidthExample)}
- defaultValue={this.props.config.FileSettings.PreviewWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.previewWidthDescription'
- defaultMessage='Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PreviewHeight'
- >
- <FormattedMessage
- id='admin.image.previewHeightTitle'
- defaultMessage='Preview Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PreviewHeight'
- ref='PreviewHeight'
- placeholder={formatMessage(holders.previewHeightExample)}
- defaultValue={this.props.config.FileSettings.PreviewHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.previewHeightDescription'
- defaultMessage='Maximum height of preview image ("0": Sets to auto-size). Updating this value changes how preview images render in future, but does not change images created in the past.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ProfileWidth'
- >
- <FormattedMessage
- id='admin.image.profileWidthTitle'
- defaultMessage='Profile Width:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ProfileWidth'
- ref='ProfileWidth'
- placeholder={formatMessage(holders.profileWidthExample)}
- defaultValue={this.props.config.FileSettings.ProfileWidth}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.profileWidthDescription'
- defaultMessage='Width of profile picture.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ProfileHeight'
- >
- <FormattedMessage
- id='admin.image.profileHeightTitle'
- defaultMessage='Profile Height:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ProfileHeight'
- ref='ProfileHeight'
- placeholder={formatMessage(holders.profileHeightExample)}
- defaultValue={this.props.config.FileSettings.ProfileHeight}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.profileHeightDescription'
- defaultMessage='Height of profile picture.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePublicLink'
- >
- <FormattedMessage
- id='admin.image.shareTitle'
- defaultMessage='Share Public File Link: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePublicLink'
- value='true'
- ref='EnablePublicLink'
- defaultChecked={this.props.config.FileSettings.EnablePublicLink}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.image.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePublicLink'
- value='false'
- defaultChecked={!this.props.config.FileSettings.EnablePublicLink}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.image.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.shareDescription'
- defaultMessage='Allow users to share public links to files and images.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PublicLinkSalt'
- >
- <FormattedMessage
- id='admin.image.publicLinkTitle'
- defaultMessage='Public Link Salt:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PublicLinkSalt'
- ref='PublicLinkSalt'
- placeholder={formatMessage(holders.publicLinkExample)}
- defaultValue={this.props.config.FileSettings.PublicLinkSalt}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.image.publicLinkDescription'
- defaultMessage='32-character salt added to signing of public image links. Randomly generated on install. Click "Re-Generate" to create new salt.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerate}
- >
- <FormattedMessage
- id='admin.image.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.image.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-FileSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(FileSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/ldap_settings.jsx b/web/react/components/admin_console/ldap_settings.jsx
deleted file mode 100644
index 4cd19c886..000000000
--- a/web/react/components/admin_console/ldap_settings.jsx
+++ /dev/null
@@ -1,584 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const DEFAULT_LDAP_PORT = 389;
-const DEFAULT_QUERY_TIMEOUT = 60;
-
-var holders = defineMessages({
- serverEx: {
- id: 'admin.ldap.serverEx',
- defaultMessage: 'Ex "10.0.0.23"'
- },
- portEx: {
- id: 'admin.ldap.portEx',
- defaultMessage: 'Ex "389"'
- },
- baseEx: {
- id: 'admin.ldap.baseEx',
- defaultMessage: 'Ex "ou=Unit Name,dc=corp,dc=example,dc=com"'
- },
- firstnameAttrEx: {
- id: 'admin.ldap.firstnameAttrEx',
- defaultMessage: 'Ex "givenName"'
- },
- lastnameAttrEx: {
- id: 'admin.ldap.lastnameAttrEx',
- defaultMessage: 'Ex "sn"'
- },
- emailAttrEx: {
- id: 'admin.ldap.emailAttrEx',
- defaultMessage: 'Ex "mail" or "userPrincipalName"'
- },
- usernameAttrEx: {
- id: 'admin.ldap.usernameAttrEx',
- defaultMessage: 'Ex "sAMAccountName"'
- },
- idAttrEx: {
- id: 'admin.ldap.idAttrEx',
- defaultMessage: 'Ex "sAMAccountName"'
- },
- queryEx: {
- id: 'admin.ldap.queryEx',
- defaultMessage: 'Ex "60"'
- },
- saving: {
- id: 'admin.ldap.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LdapSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleChange = this.handleChange.bind(this);
- this.handleEnable = this.handleEnable.bind(this);
- this.handleDisable = this.handleDisable.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null,
- enable: this.props.config.LdapSettings.Enable
- };
- }
- handleChange() {
- this.setState({saveNeeded: true});
- }
- handleEnable() {
- this.setState({saveNeeded: true, enable: true});
- }
- handleDisable() {
- this.setState({saveNeeded: true, enable: false});
- }
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- const config = this.props.config;
- config.LdapSettings.Enable = this.refs.Enable.checked;
- config.LdapSettings.LdapServer = this.refs.LdapServer.value.trim();
-
- let LdapPort = DEFAULT_LDAP_PORT;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.LdapPort).value, 10))) {
- LdapPort = parseInt(ReactDOM.findDOMNode(this.refs.LdapPort).value, 10);
- }
- config.LdapSettings.LdapPort = LdapPort;
-
- config.LdapSettings.BaseDN = this.refs.BaseDN.value.trim();
- config.LdapSettings.BindUsername = this.refs.BindUsername.value.trim();
- config.LdapSettings.BindPassword = this.refs.BindPassword.value.trim();
- config.LdapSettings.FirstNameAttribute = this.refs.FirstNameAttribute.value.trim();
- config.LdapSettings.LastNameAttribute = this.refs.LastNameAttribute.value.trim();
- config.LdapSettings.EmailAttribute = this.refs.EmailAttribute.value.trim();
- config.LdapSettings.UsernameAttribute = this.refs.UsernameAttribute.value.trim();
- config.LdapSettings.IdAttribute = this.refs.IdAttribute.value.trim();
-
- let QueryTimeout = DEFAULT_QUERY_TIMEOUT;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.QueryTimeout).value, 10))) {
- QueryTimeout = parseInt(ReactDOM.findDOMNode(this.refs.QueryTimeout).value, 10);
- }
- config.LdapSettings.QueryTimeout = QueryTimeout;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
- render() {
- const {formatMessage} = this.props.intl;
- let serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- let saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- const licenseEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.LDAP === 'true';
-
- let bannerContent;
- if (licenseEnabled) {
- bannerContent = (
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.ldap.bannerHeading'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.ldap.bannerDesc'
- defaultMessage='If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.'
- />
- </p>
- </div>
- </div>
- );
- } else {
- bannerContent = (
- <div className='banner warning'>
- <div className='banner__content'>
- <FormattedHTMLMessage
- id='admin.ldap.noLicense'
- defaultMessage='<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>'
- />
- </div>
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- {bannerContent}
- <h3>
- <FormattedMessage
- id='admin.ldap.title'
- defaultMessage='LDAP Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Enable'
- >
- <FormattedMessage
- id='admin.ldap.enableTitle'
- defaultMessage='Enable Login With LDAP:'
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='true'
- ref='Enable'
- defaultChecked={this.props.config.LdapSettings.Enable}
- onChange={this.handleEnable}
- disabled={!licenseEnabled}
- />
- <FormattedMessage
- id='admin.ldap.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Enable'
- value='false'
- defaultChecked={!this.props.config.LdapSettings.Enable}
- onChange={this.handleDisable}
- />
- <FormattedMessage
- id='admin.ldap.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.enableDesc'
- defaultMessage='When true, Mattermost allows login using LDAP'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LdapServer'
- >
- <FormattedMessage
- id='admin.ldap.serverTitle'
- defaultMessage='LDAP Server:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='LdapServer'
- ref='LdapServer'
- placeholder={formatMessage(holders.serverEx)}
- defaultValue={this.props.config.LdapSettings.LdapServer}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.serverDesc'
- defaultMessage='The domain or IP address of LDAP server.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LdapPort'
- >
- <FormattedMessage
- id='admin.ldap.portTitle'
- defaultMessage='LDAP Port:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='number'
- className='form-control'
- id='LdapPort'
- ref='LdapPort'
- placeholder={formatMessage(holders.portEx)}
- defaultValue={this.props.config.LdapSettings.LdapPort}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.portDesc'
- defaultMessage='The port Mattermost will use to connect to the LDAP server. Default is 389.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BaseDN'
- >
- <FormattedMessage
- id='admin.ldap.baseTitle'
- defaultMessage='BaseDN:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='BaseDN'
- ref='BaseDN'
- placeholder={formatMessage(holders.baseEx)}
- defaultValue={this.props.config.LdapSettings.BaseDN}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.baseDesc'
- defaultMessage='The Base DN is the Distinguished Name of the location where Mattermost should start its search for users in the LDAP tree.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BindUsername'
- >
- <FormattedMessage
- id='admin.ldap.bindUserTitle'
- defaultMessage='Bind Username:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='BindUsername'
- ref='BindUsername'
- placeholder=''
- defaultValue={this.props.config.LdapSettings.BindUsername}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.bindUserDesc'
- defaultMessage='The username used to perform the LDAP search. This should typically be an account created specifically for use with Mattermost. It should have access limited to read the portion of the LDAP tree specified in the BaseDN field.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='BindPassword'
- >
- <FormattedMessage
- id='admin.ldap.bindPwdTitle'
- defaultMessage='Bind Password:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='password'
- className='form-control'
- id='BindPassword'
- ref='BindPassword'
- placeholder=''
- defaultValue={this.props.config.LdapSettings.BindPassword}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.bindPwdDesc'
- defaultMessage='Password of the user given in "Bind Username".'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='FirstNameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.firstnameAttrTitle'
- defaultMessage='First Name Attrubute'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='FirstNameAttribute'
- ref='FirstNameAttribute'
- placeholder={formatMessage(holders.firstnameAttrEx)}
- defaultValue={this.props.config.LdapSettings.FirstNameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.firstnameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the first name of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='LastNameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.lastnameAttrTitle'
- defaultMessage='Last Name Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='LastNameAttribute'
- ref='LastNameAttribute'
- placeholder={formatMessage(holders.lastnameAttrEx)}
- defaultValue={this.props.config.LdapSettings.LastNameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.lastnameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EmailAttribute'
- >
- <FormattedMessage
- id='admin.ldap.emailAttrTitle'
- defaultMessage='Email Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='EmailAttribute'
- ref='EmailAttribute'
- placeholder={formatMessage(holders.emailAttrEx)}
- defaultValue={this.props.config.LdapSettings.EmailAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.emailAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the email addresses of users in Mattermost.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='UsernameAttribute'
- >
- <FormattedMessage
- id='admin.ldap.usernameAttrTitle'
- defaultMessage='Username Attribute:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='UsernameAttribute'
- ref='UsernameAttribute'
- placeholder={formatMessage(holders.usernameAttrEx)}
- defaultValue={this.props.config.LdapSettings.UsernameAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.uernameAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='IdAttribute'
- >
- <FormattedMessage
- id='admin.ldap.idAttrTitle'
- defaultMessage='Id Attribute: '
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='IdAttribute'
- ref='IdAttribute'
- placeholder={formatMessage(holders.idAttrEx)}
- defaultValue={this.props.config.LdapSettings.IdAttribute}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.idAttrDesc'
- defaultMessage='The attribute in the LDAP server that will be used as a unique identifier in Mattermost. It should be an LDAP attribute with a value that does not change, such as username or uid. If a userā€™s Id Attribute changes, it will create a new Mattermost account unassociated with their old one. This is the value used to log in to Mattermost in the "LDAP Username" field on the sign in page. Normally this attribute is the same as the ā€œUsername Attributeā€ field above. If your team typically uses domain\\username to sign in to other services with LDAP, you may choose to put domain\\username in this field to maintain consistency between sites.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='QueryTimeout'
- >
- <FormattedMessage
- id='admin.ldap.queryTitle'
- defaultMessage='Query Timeout (seconds):'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='number'
- className='form-control'
- id='QueryTimeout'
- ref='QueryTimeout'
- placeholder={formatMessage(holders.queryEx)}
- defaultValue={this.props.config.LdapSettings.QueryTimeout}
- onChange={this.handleChange}
- disabled={!this.state.enable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.ldap.queryDesc'
- defaultMessage='The timeout value for queries to the LDAP server. Increase if you are getting timeout errors caused by a slow LDAP server.'
- />
- </p>
- </div>
- </div>
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.ldap.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
- </form>
- </div>
- );
- }
-}
-LdapSettings.defaultProps = {
-};
-
-LdapSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LdapSettings);
diff --git a/web/react/components/admin_console/legal_and_support_settings.jsx b/web/react/components/admin_console/legal_and_support_settings.jsx
deleted file mode 100644
index a6c6a0626..000000000
--- a/web/react/components/admin_console/legal_and_support_settings.jsx
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- saving: {
- id: 'admin.support.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LegalAndSupportSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
-
- config.SupportSettings.TermsOfServiceLink = ReactDOM.findDOMNode(this.refs.TermsOfServiceLink).value.trim();
- config.SupportSettings.PrivacyPolicyLink = ReactDOM.findDOMNode(this.refs.PrivacyPolicyLink).value.trim();
- config.SupportSettings.AboutLink = ReactDOM.findDOMNode(this.refs.AboutLink).value.trim();
- config.SupportSettings.HelpLink = ReactDOM.findDOMNode(this.refs.HelpLink).value.trim();
- config.SupportSettings.ReportAProblemLink = ReactDOM.findDOMNode(this.refs.ReportAProblemLink).value.trim();
- config.SupportSettings.SupportEmail = ReactDOM.findDOMNode(this.refs.SupportEmail).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.support.title'
- defaultMessage='Legal and Support Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='TermsOfServiceLink'
- >
- <FormattedMessage
- id='admin.support.termsTitle'
- defaultMessage='Terms of Service link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='TermsOfServiceLink'
- ref='TermsOfServiceLink'
- defaultValue={this.props.config.SupportSettings.TermsOfServiceLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.termsDesc'
- defaultMessage='Link to Terms of Service available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PrivacyPolicyLink'
- >
- <FormattedMessage
- id='admin.support.privacyTitle'
- defaultMessage='Privacy Policy link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PrivacyPolicyLink'
- ref='PrivacyPolicyLink'
- defaultValue={this.props.config.SupportSettings.PrivacyPolicyLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.privacyDesc'
- defaultMessage='Link to Privacy Policy available to users on desktop and on mobile. Leaving this blank will hide the option to display a notice.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AboutLink'
- >
- <FormattedMessage
- id='admin.support.aboutTitle'
- defaultMessage='About link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AboutLink'
- ref='AboutLink'
- defaultValue={this.props.config.SupportSettings.AboutLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.aboutDesc'
- defaultMessage='Link to About page for more information on your Mattermost deployment, for example its purpose and audience within your organization. Defaults to Mattermost information page.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='HelpLink'
- >
- <FormattedMessage
- id='admin.support.helpTitle'
- defaultMessage='Help link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='HelpLink'
- ref='HelpLink'
- defaultValue={this.props.config.SupportSettings.HelpLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.helpDesc'
- defaultMessage='Link to help documentation from team site main menu. Typically not changed unless your organization chooses to create custom documentation.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ReportAProblemLink'
- >
- <FormattedMessage
- id='admin.support.problemTitle'
- defaultMessage='Report a Problem link:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ReportAProblemLink'
- ref='ReportAProblemLink'
- defaultValue={this.props.config.SupportSettings.ReportAProblemLink}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.problemDesc'
- defaultMessage='Link to help documentation from team site main menu. By default this points to the peer-to-peer troubleshooting forum where users can search for, find and request help with technical issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SupportEmail'
- >
- <FormattedMessage
- id='admin.support.emailTitle'
- defaultMessage='Support email:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SupportEmail'
- ref='SupportEmail'
- defaultValue={this.props.config.SupportSettings.SupportEmail}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.support.emailHelp'
- defaultMessage='Email shown during tutorial for end users to ask support questions.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.support.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-LegalAndSupportSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LegalAndSupportSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/license_settings.jsx b/web/react/components/admin_console/license_settings.jsx
deleted file mode 100644
index 9d2ec8030..000000000
--- a/web/react/components/admin_console/license_settings.jsx
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- removing: {
- id: 'admin.license.removing',
- defaultMessage: 'Removing License...'
- },
- uploading: {
- id: 'admin.license.uploading',
- defaultMessage: 'Uploading License...'
- }
-});
-
-class LicenseSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleRemove = this.handleRemove.bind(this);
-
- this.state = {
- fileSelected: false,
- fileName: null,
- serverError: null
- };
- }
-
- handleChange() {
- const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
- if (element.prop('files').length > 0) {
- this.setState({fileSelected: true, fileName: element.prop('files')[0].name});
- }
- }
-
- handleSubmit(e) {
- e.preventDefault();
-
- const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
- if (element.prop('files').length === 0) {
- return;
- }
- const file = element.prop('files')[0];
-
- $('#upload-button').button('loading');
-
- const formData = new FormData();
- formData.append('license', file, file.name);
-
- Client.uploadLicenseFile(formData,
- () => {
- Utils.clearFileInput(element[0]);
- $('#upload-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: null});
- window.location.reload(true);
- },
- (error) => {
- Utils.clearFileInput(element[0]);
- $('#upload-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: error.message});
- }
- );
- }
-
- handleRemove(e) {
- e.preventDefault();
-
- $('#remove-button').button('loading');
-
- Client.removeLicenseFile(
- () => {
- $('#remove-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: null});
- window.location.reload(true);
- },
- (error) => {
- $('#remove-button').button('reset');
- this.setState({fileSelected: false, fileName: null, serverError: error.message});
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var btnClass = 'btn';
- if (this.state.fileSelected) {
- btnClass = 'btn btn-primary';
- }
-
- let edition;
- let licenseType;
- let licenseKey;
-
- if (global.window.mm_license.IsLicensed === 'true') {
- edition = (
- <FormattedMessage
- id='admin.license.enterpriseEdition'
- defaultMessage='Mattermost Enterprise Edition. Designed for enterprise-scale communication.'
- />
- );
- licenseType = (
- <FormattedHTMLMessage
- id='admin.license.enterpriseType'
- values={{
- terms: global.window.mm_config.TermsOfServiceLink,
- name: global.window.mm_license.Name,
- company: global.window.mm_license.Company,
- users: global.window.mm_license.Users,
- issued: Utils.displayDate(parseInt(global.window.mm_license.IssuedAt, 10)) + ' ' + Utils.displayTime(parseInt(global.window.mm_license.IssuedAt, 10), true),
- start: Utils.displayDate(parseInt(global.window.mm_license.StartsAt, 10)),
- expires: Utils.displayDate(parseInt(global.window.mm_license.ExpiresAt, 10)),
- ldap: global.window.mm_license.LDAP
- }}
- defaultMessage='<div><p>This compiled release of Mattermost platform is provided under a <a href="http://mattermost.com" target="_blank">commercial license</a> from Mattermost, Inc. based on your subscription level and is subject to the <a href="{terms}" target="_blank">Terms of Service.</a></p>
- <p>Your subscription details are as follows:</p>
- Name: {name}<br />
- Company or organization name: {company}<br/>
- Number of users: {users}<br/>
- License issued: {issued}<br/>
- Start date of license: {start}<br/>
- Expiry date of license: {expires}<br/>
- LDAP: {ldap}<br/></div>'
- />
- );
-
- licenseKey = (
- <div className='col-sm-8'>
- <button
- disabled={this.props.config.LdapSettings.Enable}
- className='btn btn-danger'
- onClick={this.handleRemove}
- id='remove-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.removing)}
- >
- <FormattedMessage
- id='admin.license.keyRemove'
- defaultMessage='Remove Enterprise License and Downgrade Server'
- />
- </button>
- <br/>
- <br/>
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.licence.keyMigration'
- defaultMessage='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.'
- />
- </p>
- </div>
- );
- } else {
- edition = (
- <FormattedMessage
- id='admin.license.teamEdition'
- defaultMessage='Mattermost Team Edition. Designed for teams from 5 to 50 users.'
- />
- );
-
- licenseType = (
- <FormattedHTMLMessage
- id='admin.license.teamType'
- defaultMessage='<span><p>This compiled release of Mattermost platform is offered under an MIT license.</p>
- <p>See MIT-COMPILED-LICENSE.txt in your root install directory for details. See NOTICES.txt for information about open source software used in this system.</p></span>'
- />
- );
-
- let fileName;
- if (this.state.fileName) {
- fileName = this.state.fileName;
- } else {
- fileName = (
- <FormattedMessage
- id='admin.license.noFile'
- defaultMessage='No file uploaded'
- />
- );
- }
-
- licenseKey = (
- <div className='col-sm-8'>
- <div className='file__upload'>
- <button className='btn btn-default'>
- <FormattedMessage
- id='admin.license.choose'
- defaultMessage='Choose File'
- />
- </button>
- <input
- ref='fileInput'
- type='file'
- accept='.mattermost-license'
- onChange={this.handleChange}
- />
- </div>
- <button
- className={btnClass}
- disabled={!this.state.fileSelected}
- onClick={this.handleSubmit}
- id='upload-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.uploading)}
- >
- <FormattedMessage
- id='admin.license.upload'
- defaultMessage='Upload'
- />
- </button>
- <div className='help-text no-margin'>
- {fileName}
- </div>
- <br/>
- {serverError}
- <p className='help-text no-margin'>
- <FormattedHTMLMessage
- id='admin.license.uploadDesc'
- defaultMessage='Upload a license key for Mattermost Enterprise Edition to upgrade this server. <a href="http://mattermost.com" target="_blank">Visit us online</a> to learn more about the benefits of Enterprise Edition or to purchase a key.'
- />
- </p>
- </div>
- );
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.license.title'
- defaultMessage='Edition and License'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.edition'
- defaultMessage='Edition: '
- />
- </label>
- <div className='col-sm-8'>
- {edition}
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.type'
- defaultMessage='License: '
- />
- </label>
- <div className='col-sm-8'>
- {licenseType}
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.license.key'
- defaultMessage='License Key: '
- />
- </label>
- {licenseKey}
- </div>
- </form>
- </div>
- );
- }
-}
-
-LicenseSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LicenseSettings);
diff --git a/web/react/components/admin_console/log_settings.jsx b/web/react/components/admin_console/log_settings.jsx
deleted file mode 100644
index cefe6afba..000000000
--- a/web/react/components/admin_console/log_settings.jsx
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- locationPlaceholder: {
- id: 'admin.log.locationPlaceholder',
- defaultMessage: 'Enter your file location'
- },
- formatPlaceholder: {
- id: 'admin.log.formatPlaceholder',
- defaultMessage: 'Enter your file format'
- },
- saving: {
- id: 'admin.log.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class LogSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- consoleEnable: this.props.config.LogSettings.EnableConsole,
- fileEnable: this.props.config.LogSettings.EnableFile,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'console_true') {
- s.consoleEnable = true;
- }
-
- if (action === 'console_false') {
- s.consoleEnable = false;
- }
-
- if (action === 'file_true') {
- s.fileEnable = true;
- }
-
- if (action === 'file_false') {
- s.fileEnable = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.LogSettings.EnableConsole = ReactDOM.findDOMNode(this.refs.consoleEnable).checked;
- config.LogSettings.ConsoleLevel = ReactDOM.findDOMNode(this.refs.consoleLevel).value;
- config.LogSettings.EnableFile = ReactDOM.findDOMNode(this.refs.fileEnable).checked;
- config.LogSettings.FileLevel = ReactDOM.findDOMNode(this.refs.fileLevel).value;
- config.LogSettings.FileLocation = ReactDOM.findDOMNode(this.refs.fileLocation).value.trim();
- config.LogSettings.FileFormat = ReactDOM.findDOMNode(this.refs.fileFormat).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- consoleEnable: config.LogSettings.EnableConsole,
- fileEnable: config.LogSettings.EnableFile,
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- consoleEnable: config.LogSettings.EnableConsole,
- fileEnable: config.LogSettings.EnableFile,
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.log.logSettings'
- defaultMessage='Log Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='consoleEnable'
- >
- <FormattedMessage
- id='admin.log.consoleTitle'
- defaultMessage='Log To The Console: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='consoleEnable'
- value='true'
- ref='consoleEnable'
- defaultChecked={this.props.config.LogSettings.EnableConsole}
- onChange={this.handleChange.bind(this, 'console_true')}
- />
- <FormattedMessage
- id='admin.log.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='consoleEnable'
- value='false'
- defaultChecked={!this.props.config.LogSettings.EnableConsole}
- onChange={this.handleChange.bind(this, 'console_false')}
- />
- <FormattedMessage
- id='admin.log.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.consoleDescription'
- defaultMessage='Typically set to false in production. Developers may set this field to true to output log messages to console based on the console level option. If true, server writes messages to the standard output stream (stdout).'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='consoleLevel'
- >
- <FormattedMessage
- id='admin.log.levelTitle'
- defaultMessage='Console Log Level:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='consoleLevel'
- ref='consoleLevel'
- defaultValue={this.props.config.LogSettings.consoleLevel}
- onChange={this.handleChange}
- disabled={!this.state.consoleEnable}
- >
- <option value='DEBUG'>{'DEBUG'}</option>
- <option value='INFO'>{'INFO'}</option>
- <option value='ERROR'>{'ERROR'}</option>
- </select>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.levelDescription'
- defaultMessage='This setting determines the level of detail at which log events are written to the console. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- >
- <FormattedMessage
- id='admin.log.fileTitle'
- defaultMessage='Log To File: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='fileEnable'
- ref='fileEnable'
- value='true'
- defaultChecked={this.props.config.LogSettings.EnableFile}
- onChange={this.handleChange.bind(this, 'file_true')}
- />
- <FormattedMessage
- id='admin.log.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='fileEnable'
- value='false'
- defaultChecked={!this.props.config.LogSettings.EnableFile}
- onChange={this.handleChange.bind(this, 'file_false')}
- />
- <FormattedMessage
- id='admin.log.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.fileDescription'
- defaultMessage='Typically set to true in production. When true, log files are written to the log file specified in file location field below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileLevel'
- >
- <FormattedMessage
- id='admin.log.fileLevelTitle'
- defaultMessage='File Log Level:'
- />
- </label>
- <div className='col-sm-8'>
- <select
- className='form-control'
- id='fileLevel'
- ref='fileLevel'
- defaultValue={this.props.config.LogSettings.FileLevel}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- >
- <option value='DEBUG'>{'DEBUG'}</option>
- <option value='INFO'>{'INFO'}</option>
- <option value='ERROR'>{'ERROR'}</option>
- </select>
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.fileLevelDescription'
- defaultMessage='This setting determines the level of detail at which log events are written to the log file. ERROR: Outputs only error messages. INFO: Outputs error messages and information around startup and initialization. DEBUG: Prints high detail for developers working on debugging issues.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileLocation'
- >
- <FormattedMessage
- id='admin.log.locationTitle'
- defaultMessage='File Location:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='fileLocation'
- ref='fileLocation'
- placeholder={formatMessage(holders.locationPlaceholder)}
- defaultValue={this.props.config.LogSettings.FileLocation}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.log.locationDescription'
- defaultMessage='File to which log files are written. If blank, will be set to ./logs/mattermost, which writes logs to mattermost.log. Log rotation is enabled and every 10,000 lines of log information is written to new files stored in the same directory, for example mattermost.2015-09-23.001, mattermost.2015-09-23.002, and so forth.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='fileFormat'
- >
- <FormattedMessage
- id='admin.log.formatTitle'
- defaultMessage='File Format:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='fileFormat'
- ref='fileFormat'
- placeholder={formatMessage(holders.formatPlaceholder)}
- defaultValue={this.props.config.LogSettings.FileFormat}
- onChange={this.handleChange}
- disabled={!this.state.fileEnable}
- />
- <div className='help-text'>
- <FormattedMessage
- id='admin.log.formatDescription'
- defaultMessage='Format of log message output. If blank will be set to "[%D %T] [%L] %M", where:'
- />
- <div className='help-text'>
- <table
- className='table table-bordered'
- cellPadding='5'
- >
- <tbody>
- <tr><td className='help-text'>{'%T'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatTime'
- defaultMessage='Time (15:04:05 MST)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%D'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatDateLong'
- defaultMessage='Date (2006/01/02)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%d'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatDateShort'
- defaultMessage='Date (01/02/06)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%L'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatLevel'
- defaultMessage='Level (DEBG, INFO, EROR)'
- />
- </td></tr>
- <tr><td className='help-text'>{'%S'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatSource'
- defaultMessage='Source'
- />
- </td></tr>
- <tr><td className='help-text'>{'%M'}</td><td className='help-text'>
- <FormattedMessage
- id='admin.log.formatMessage'
- defaultMessage='Message'
- />
- </td></tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.log.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-LogSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(LogSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/logs.jsx b/web/react/components/admin_console/logs.jsx
deleted file mode 100644
index 8457999f5..000000000
--- a/web/react/components/admin_console/logs.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AdminStore from '../../stores/admin_store.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class Logs extends React.Component {
- constructor(props) {
- super(props);
-
- this.onLogListenerChange = this.onLogListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
- this.state = {
- logs: AdminStore.getLogs()
- };
- }
-
- componentDidMount() {
- AdminStore.addLogChangeListener(this.onLogListenerChange);
- AsyncClient.getLogs();
- }
-
- componentWillUnmount() {
- AdminStore.removeLogChangeListener(this.onLogListenerChange);
- }
-
- onLogListenerChange() {
- this.setState({
- logs: AdminStore.getLogs()
- });
- }
-
- reload() {
- AdminStore.saveLogs(null);
- this.setState({
- logs: null
- });
-
- AsyncClient.getLogs();
- }
-
- render() {
- var content = null;
-
- if (this.state.logs === null) {
- content = <LoadingScreen/>;
- } else {
- content = [];
-
- for (var i = 0; i < this.state.logs.length; i++) {
- var style = {
- whiteSpace: 'nowrap',
- fontFamily: 'monospace'
- };
-
- if (this.state.logs[i].indexOf('[EROR]') > 0) {
- style.color = 'red';
- }
-
- content.push(<br key={'br_' + i}/>);
- content.push(
- <span
- key={'log_' + i}
- style={style}
- >
- {this.state.logs[i]}
- </span>
- );
- }
- }
-
- return (
- <div className='panel'>
- <h3>
- <FormattedMessage
- id='admin.logs.title'
- defaultMessage='Server Logs'
- />
- </h3>
- <button
- type='submit'
- className='btn btn-primary'
- onClick={this.reload}
- >
- <FormattedMessage
- id='admin.logs.reload'
- defaultMessage='Reload'
- />
- </button>
- <div className='log__panel'>
- {content}
- </div>
- </div>
- );
- }
-} \ No newline at end of file
diff --git a/web/react/components/admin_console/privacy_settings.jsx b/web/react/components/admin_console/privacy_settings.jsx
deleted file mode 100644
index 1ab625049..000000000
--- a/web/react/components/admin_console/privacy_settings.jsx
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- saving: {
- id: 'admin.privacy.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class PrivacySettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.PrivacySettings.ShowEmailAddress = ReactDOM.findDOMNode(this.refs.ShowEmailAddress).checked;
- config.PrivacySettings.ShowFullName = ReactDOM.findDOMNode(this.refs.ShowFullName).checked;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.privacy.title'
- defaultMessage='Privacy Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ShowEmailAddress'
- >
- <FormattedMessage
- id='admin.privacy.showEmailTitle'
- defaultMessage='Show Email Address: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowEmailAddress'
- value='true'
- ref='ShowEmailAddress'
- defaultChecked={this.props.config.PrivacySettings.ShowEmailAddress}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowEmailAddress'
- value='false'
- defaultChecked={!this.props.config.PrivacySettings.ShowEmailAddress}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.privacy.showEmailDescription'
- defaultMessage='When false, hides email address of users from other users in the user interface, including team owners and team administrators. Used when system is set up for managing teams where some users choose to keep their contact information private.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ShowFullName'
- >
- <FormattedMessage
- id='admin.privacy.showFullNameTitle'
- defaultMessage='Show Full Name: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowFullName'
- value='true'
- ref='ShowFullName'
- defaultChecked={this.props.config.PrivacySettings.ShowFullName}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='ShowFullName'
- value='false'
- defaultChecked={!this.props.config.PrivacySettings.ShowFullName}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.privacy.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.privacy.showFullNameDescription'
- defaultMessage='When false, hides full name of users from other users, including team owners and team administrators. Username is shown in place of full name.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.privacy.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-PrivacySettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(PrivacySettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/rate_settings.jsx b/web/react/components/admin_console/rate_settings.jsx
deleted file mode 100644
index d3c1bffa2..000000000
--- a/web/react/components/admin_console/rate_settings.jsx
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- queriesExample: {
- id: 'admin.rate.queriesExample',
- defaultMessage: 'Ex "10"'
- },
- memoryExample: {
- id: 'admin.rate.memoryExample',
- defaultMessage: 'Ex "10000"'
- },
- httpHeaderExample: {
- id: 'admin.rate.httpHeaderExample',
- defaultMessage: 'Ex "X-Real-IP", "X-Forwarded-For"'
- },
- saving: {
- id: 'admin.rate.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class RateSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- EnableRateLimiter: this.props.config.RateLimitSettings.EnableRateLimiter,
- VaryByRemoteAddr: this.props.config.RateLimitSettings.VaryByRemoteAddr,
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange(action) {
- var s = {saveNeeded: true, serverError: this.state.serverError};
-
- if (action === 'EnableRateLimiterTrue') {
- s.EnableRateLimiter = true;
- }
-
- if (action === 'EnableRateLimiterFalse') {
- s.EnableRateLimiter = false;
- }
-
- if (action === 'VaryByRemoteAddrTrue') {
- s.VaryByRemoteAddr = true;
- }
-
- if (action === 'VaryByRemoteAddrFalse') {
- s.VaryByRemoteAddr = false;
- }
-
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.RateLimitSettings.EnableRateLimiter = ReactDOM.findDOMNode(this.refs.EnableRateLimiter).checked;
- config.RateLimitSettings.VaryByRemoteAddr = ReactDOM.findDOMNode(this.refs.VaryByRemoteAddr).checked;
- config.RateLimitSettings.VaryByHeader = ReactDOM.findDOMNode(this.refs.VaryByHeader).value.trim();
-
- var PerSec = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10))) {
- PerSec = parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10);
- }
- config.RateLimitSettings.PerSec = PerSec;
- ReactDOM.findDOMNode(this.refs.PerSec).value = PerSec;
-
- var MemoryStoreSize = 10000;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10))) {
- MemoryStoreSize = parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10);
- }
- config.RateLimitSettings.MemoryStoreSize = MemoryStoreSize;
- ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value = MemoryStoreSize;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.rate.noteTitle'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.rate.noteDescription'
- defaultMessage='Changing properties in this section will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <h3>
- <FormattedMessage
- id='admin.rate.title'
- defaultMessage='Rate Limit Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableRateLimiter'
- >
- <FormattedMessage
- id='admin.rate.enableLimiterTitle'
- defaultMessage='Enable Rate Limiter: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableRateLimiter'
- value='true'
- ref='EnableRateLimiter'
- defaultChecked={this.props.config.RateLimitSettings.EnableRateLimiter}
- onChange={this.handleChange.bind(this, 'EnableRateLimiterTrue')}
- />
- <FormattedMessage
- id='admin.rate.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableRateLimiter'
- value='false'
- defaultChecked={!this.props.config.RateLimitSettings.EnableRateLimiter}
- onChange={this.handleChange.bind(this, 'EnableRateLimiterFalse')}
- />
- <FormattedMessage
- id='admin.rate.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.enableLimiterDescription'
- defaultMessage='When true, APIs are throttled at rates specified below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='PerSec'
- >
- <FormattedMessage
- id='admin.rate.queriesTitle'
- defaultMessage='Number Of Queries Per Second:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='PerSec'
- ref='PerSec'
- placeholder={formatMessage(holders.queriesExample)}
- defaultValue={this.props.config.RateLimitSettings.PerSec}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.queriesDescription'
- defaultMessage='Throttles API at this number of requests per second.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MemoryStoreSize'
- >
- <FormattedMessage
- id='admin.rate.memoryTitle'
- defaultMessage='Memory Store Size:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MemoryStoreSize'
- ref='MemoryStoreSize'
- placeholder={formatMessage(holders.memoryExample)}
- defaultValue={this.props.config.RateLimitSettings.MemoryStoreSize}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.memoryDescription'
- defaultMessage='Maximum number of users sessions connected to the system as determined by "Vary By Remote Address" and "Vary By Header" settings below.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='VaryByRemoteAddr'
- >
- <FormattedMessage
- id='admin.rate.remoteTitle'
- defaultMessage='Vary By Remote Address: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='VaryByRemoteAddr'
- value='true'
- ref='VaryByRemoteAddr'
- defaultChecked={this.props.config.RateLimitSettings.VaryByRemoteAddr}
- onChange={this.handleChange.bind(this, 'VaryByRemoteAddrTrue')}
- disabled={!this.state.EnableRateLimiter}
- />
- <FormattedMessage
- id='admin.rate.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='VaryByRemoteAddr'
- value='false'
- defaultChecked={!this.props.config.RateLimitSettings.VaryByRemoteAddr}
- onChange={this.handleChange.bind(this, 'VaryByRemoteAddrFalse')}
- disabled={!this.state.EnableRateLimiter}
- />
- <FormattedMessage
- id='admin.rate.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.remoteDescription'
- defaultMessage='When true, rate limit API access by IP address.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='VaryByHeader'
- >
- <FormattedMessage
- id='admin.rate.httpHeaderTitle'
- defaultMessage='Vary By HTTP Header:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='VaryByHeader'
- ref='VaryByHeader'
- placeholder={formatMessage(holders.httpHeaderExample)}
- defaultValue={this.props.config.RateLimitSettings.VaryByHeader}
- onChange={this.handleChange}
- disabled={!this.state.EnableRateLimiter || this.state.VaryByRemoteAddr}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.rate.httpHeaderDescription'
- defaultMessage='When filled in, vary rate limiting by HTTP header field specified (e.g. when configuring NGINX set to "X-Real-IP", when configuring AmazonELB set to "X-Forwarded-For").'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.rate.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-RateSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(RateSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/reset_password_modal.jsx b/web/react/components/admin_console/reset_password_modal.jsx
deleted file mode 100644
index 8ed519ffb..000000000
--- a/web/react/components/admin_console/reset_password_modal.jsx
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import Constants from '../../utils/constants.jsx';
-var Modal = ReactBootstrap.Modal;
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- submit: {
- id: 'admin.reset_password.submit',
- defaultMessage: 'Please enter at least {chars} characters.'
- }
-});
-
-class ResetPasswordModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
-
- this.state = {
- serverError: null
- };
- }
-
- doSubmit(e) {
- e.preventDefault();
- var password = ReactDOM.findDOMNode(this.refs.password).value;
-
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({serverError: this.props.intl.formatMessage(holders.submit, {chars: Constants.MIN_PASSWORD_LENGTH})});
- return;
- }
-
- this.setState({serverError: null});
-
- var data = {};
- data.new_password = password;
- data.name = this.props.team.name;
- data.user_id = this.props.user.id;
-
- Client.resetPassword(data,
- () => {
- this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.password).value);
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- doCancel() {
- this.setState({serverError: null});
- this.props.onModalDismissed();
- }
-
- render() {
- if (this.props.user == null) {
- return <div/>;
- }
-
- let urlClass = 'input-group input-group--limit';
- let serverError = null;
-
- if (this.state.serverError) {
- urlClass += ' has-error';
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.state.serverError}</p></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='admin.reset_password.title'
- defaultMessage='Reset Password'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='form-group'>
- <div className='col-sm-10'>
- <div className={urlClass}>
- <span
- data-toggle='tooltip'
- title='New Password'
- className='input-group-addon'
- >
- <FormattedMessage
- id='admin.reset_password.newPassword'
- defaultMessage='New Password'
- />
- </span>
- <input
- type='password'
- ref='password'
- className='form-control'
- maxLength='22'
- autoFocus={true}
- tabIndex='1'
- />
- </div>
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='admin.reset_password.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- <FormattedMessage
- id='admin.reset_password.select'
- defaultMessage='Select'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-ResetPasswordModal.defaultProps = {
- show: false
-};
-
-ResetPasswordModal.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- team: React.PropTypes.object,
- show: React.PropTypes.bool.isRequired,
- onModalSubmit: React.PropTypes.func,
- onModalDismissed: React.PropTypes.func
-};
-
-export default injectIntl(ResetPasswordModal); \ No newline at end of file
diff --git a/web/react/components/admin_console/select_team_modal.jsx b/web/react/components/admin_console/select_team_modal.jsx
deleted file mode 100644
index e0d070b28..000000000
--- a/web/react/components/admin_console/select_team_modal.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-var Modal = ReactBootstrap.Modal;
-
-export default class SelectTeamModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
- }
-
- doSubmit(e) {
- e.preventDefault();
- this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.team).value);
- }
- doCancel() {
- this.props.onModalDismissed();
- }
- render() {
- if (this.props.teams == null) {
- return <div/>;
- }
-
- var options = [];
-
- for (var key in this.props.teams) {
- if (this.props.teams.hasOwnProperty(key)) {
- var team = this.props.teams[key];
- options.push(
- <option
- key={'opt_' + team.id}
- value={team.id}
- >
- {team.name}
- </option>
- );
- }
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='admin.select_team.selectTeam'
- defaultMessage='Select Team'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='form-group'>
- <div className='col-sm-12'>
- <select
- ref='team'
- size='10'
- className='form-control'
- >
- {options}
- </select>
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='admin.select_team.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- <FormattedMessage
- id='admin.select_team.select'
- defaultMessage='Select'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-SelectTeamModal.defaultProps = {
- show: false
-};
-
-SelectTeamModal.propTypes = {
- teams: React.PropTypes.object,
- show: React.PropTypes.bool.isRequired,
- onModalSubmit: React.PropTypes.func,
- onModalDismissed: React.PropTypes.func
-}; \ No newline at end of file
diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx
deleted file mode 100644
index 9ed81b6a3..000000000
--- a/web/react/components/admin_console/service_settings.jsx
+++ /dev/null
@@ -1,980 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const DefaultSessionLength = 30;
-const DefaultMaximumLoginAttempts = 10;
-const DefaultSessionCacheInMinutes = 10;
-
-var holders = defineMessages({
- listenExample: {
- id: 'admin.service.listenExample',
- defaultMessage: 'Ex ":8065"'
- },
- attemptExample: {
- id: 'admin.service.attemptExample',
- defaultMessage: 'Ex "10"'
- },
- segmentExample: {
- id: 'admin.service.segmentExample',
- defaultMessage: 'Ex "g3fgGOXJAQ43QV7rAh6iwQCkV4cA1Gs"'
- },
- googleExample: {
- id: 'admin.service.googleExample',
- defaultMessage: 'Ex "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV"'
- },
- sessionDaysEx: {
- id: 'admin.service.sessionDaysEx',
- defaultMessage: 'Ex "30"'
- },
- corsExample: {
- id: 'admin.service.corsEx',
- defaultMessage: 'http://example.com'
- },
- saving: {
- id: 'admin.service.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class ServiceSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.ServiceSettings.ListenAddress = ReactDOM.findDOMNode(this.refs.ListenAddress).value.trim();
- if (config.ServiceSettings.ListenAddress === '') {
- config.ServiceSettings.ListenAddress = ':8065';
- ReactDOM.findDOMNode(this.refs.ListenAddress).value = config.ServiceSettings.ListenAddress;
- }
-
- config.ServiceSettings.SegmentDeveloperKey = ReactDOM.findDOMNode(this.refs.SegmentDeveloperKey).value.trim();
- config.ServiceSettings.GoogleDeveloperKey = ReactDOM.findDOMNode(this.refs.GoogleDeveloperKey).value.trim();
- config.ServiceSettings.EnableIncomingWebhooks = ReactDOM.findDOMNode(this.refs.EnableIncomingWebhooks).checked;
- config.ServiceSettings.EnableOutgoingWebhooks = ReactDOM.findDOMNode(this.refs.EnableOutgoingWebhooks).checked;
- config.ServiceSettings.EnablePostUsernameOverride = ReactDOM.findDOMNode(this.refs.EnablePostUsernameOverride).checked;
- config.ServiceSettings.EnablePostIconOverride = ReactDOM.findDOMNode(this.refs.EnablePostIconOverride).checked;
- config.ServiceSettings.EnableTesting = ReactDOM.findDOMNode(this.refs.EnableTesting).checked;
- config.ServiceSettings.EnableDeveloper = ReactDOM.findDOMNode(this.refs.EnableDeveloper).checked;
- config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
- config.ServiceSettings.EnableInsecureOutgoingConnections = ReactDOM.findDOMNode(this.refs.EnableInsecureOutgoingConnections).checked;
- config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
- config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
-
- //config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
-
- var MaximumLoginAttempts = DefaultMaximumLoginAttempts;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) {
- MaximumLoginAttempts = parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10);
- }
- if (MaximumLoginAttempts < 1) {
- MaximumLoginAttempts = 1;
- }
- config.ServiceSettings.MaximumLoginAttempts = MaximumLoginAttempts;
- ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value = MaximumLoginAttempts;
-
- var SessionLengthWebInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10))) {
- SessionLengthWebInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value, 10);
- }
- if (SessionLengthWebInDays < 1) {
- SessionLengthWebInDays = 1;
- }
- config.ServiceSettings.SessionLengthWebInDays = SessionLengthWebInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthWebInDays).value = SessionLengthWebInDays;
-
- var SessionLengthMobileInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10))) {
- SessionLengthMobileInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value, 10);
- }
- if (SessionLengthMobileInDays < 1) {
- SessionLengthMobileInDays = 1;
- }
- config.ServiceSettings.SessionLengthMobileInDays = SessionLengthMobileInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthMobileInDays).value = SessionLengthMobileInDays;
-
- var SessionLengthSSOInDays = DefaultSessionLength;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10))) {
- SessionLengthSSOInDays = parseInt(ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value, 10);
- }
- if (SessionLengthSSOInDays < 1) {
- SessionLengthSSOInDays = 1;
- }
- config.ServiceSettings.SessionLengthSSOInDays = SessionLengthSSOInDays;
- ReactDOM.findDOMNode(this.refs.SessionLengthSSOInDays).value = SessionLengthSSOInDays;
-
- var SessionCacheInMinutes = DefaultSessionCacheInMinutes;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10))) {
- SessionCacheInMinutes = parseInt(ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value, 10);
- }
- if (SessionCacheInMinutes < -1) {
- SessionCacheInMinutes = -1;
- }
- config.ServiceSettings.SessionCacheInMinutes = SessionCacheInMinutes;
- ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value = SessionCacheInMinutes;
-
- config.ServiceSettings.AllowCorsFrom = ReactDOM.findDOMNode(this.refs.AllowCorsFrom).value.trim();
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.service.title'
- defaultMessage='Service Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='ListenAddress'
- >
- <FormattedMessage
- id='admin.service.listenAddress'
- defaultMessage='Listen Address:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='ListenAddress'
- ref='ListenAddress'
- placeholder={formatMessage(holders.listenExample)}
- defaultValue={this.props.config.ServiceSettings.ListenAddress}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.listenDescription'
- defaultMessage='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.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaximumLoginAttempts'
- >
- <FormattedMessage
- id='admin.service.attemptTitle'
- defaultMessage='Maximum Login Attempts:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaximumLoginAttempts'
- ref='MaximumLoginAttempts'
- placeholder={formatMessage(holders.attemptExample)}
- defaultValue={this.props.config.ServiceSettings.MaximumLoginAttempts}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.attemptDescription'
- defaultMessage='Login attempts allowed before user is locked out and required to reset password via email.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SegmentDeveloperKey'
- >
- <FormattedMessage
- id='admin.service.segmentTitle'
- defaultMessage='Segment Developer Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SegmentDeveloperKey'
- ref='SegmentDeveloperKey'
- placeholder={formatMessage(holders.segmentExample)}
- defaultValue={this.props.config.ServiceSettings.SegmentDeveloperKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.segmentDescription'
- defaultMessage='For users running a SaaS services, sign up for a key at Segment.com to track metrics.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='GoogleDeveloperKey'
- >
- <FormattedMessage
- id='admin.service.googleTitle'
- defaultMessage='Google Developer Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='GoogleDeveloperKey'
- ref='GoogleDeveloperKey'
- placeholder={formatMessage(holders.googleExample)}
- defaultValue={this.props.config.ServiceSettings.GoogleDeveloperKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedHTMLMessage
- id='admin.service.googleDescription'
- defaultMessage='Set this key to enable embedding of YouTube video previews based on hyperlinks appearing in messages or comments. Instructions to obtain a key available at <a href="https://www.youtube.com/watch?v=Im69kzhpR3I" target="_blank">https://www.youtube.com/watch?v=Im69kzhpR3I</a>. Leaving the field blank disables the automatic generation of YouTube video previews from links.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableIncomingWebhooks'
- >
- <FormattedMessage
- id='admin.service.webhooksTitle'
- defaultMessage='Enable Incoming Webhooks: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableIncomingWebhooks'
- value='true'
- ref='EnableIncomingWebhooks'
- defaultChecked={this.props.config.ServiceSettings.EnableIncomingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableIncomingWebhooks'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableIncomingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.webhooksDescription'
- defaultMessage='When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableOutgoingWebhooks'
- >
- <FormattedMessage
- id='admin.service.outWebhooksTitle'
- defaultMessage='Enable Outgoing Webhooks: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOutgoingWebhooks'
- value='true'
- ref='EnableOutgoingWebhooks'
- defaultChecked={this.props.config.ServiceSettings.EnableOutgoingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOutgoingWebhooks'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableOutgoingWebhooks}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.outWebhooksDesc'
- defaultMessage='When true, outgoing webhooks will be allowed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableCommands'
- >
- <FormattedMessage
- id='admin.service.cmdsTitle'
- defaultMessage='Enable Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableCommands'
- value='true'
- ref='EnableCommands'
- defaultChecked={this.props.config.ServiceSettings.EnableCommands}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableCommands'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableCommands}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.cmdsDesc'
- defaultMessage='When true, user created slash commands will be allowed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableOnlyAdminIntegrations'
- >
- <FormattedMessage
- id='admin.service.integrationAdmin'
- defaultMessage='Enable Integrations for Admin Only: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOnlyAdminIntegrations'
- value='true'
- ref='EnableOnlyAdminIntegrations'
- defaultChecked={this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableOnlyAdminIntegrations'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.integrationAdminDesc'
- defaultMessage='When true, user created integrations can only be created by admins.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePostUsernameOverride'
- >
- <FormattedMessage
- id='admin.service.overrideTitle'
- defaultMessage='Enable Overriding Usernames from Webhooks and Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostUsernameOverride'
- value='true'
- ref='EnablePostUsernameOverride'
- defaultChecked={this.props.config.ServiceSettings.EnablePostUsernameOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostUsernameOverride'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnablePostUsernameOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.overrideDescription'
- defaultMessage='When true, webhooks and slash commands will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnablePostIconOverride'
- >
- <FormattedMessage
- id='admin.service.iconTitle'
- defaultMessage='Enable Overriding Icon from Webhooks and Slash Commands: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostIconOverride'
- value='true'
- ref='EnablePostIconOverride'
- defaultChecked={this.props.config.ServiceSettings.EnablePostIconOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnablePostIconOverride'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnablePostIconOverride}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.iconDescription'
- defaultMessage='When true, webhooks and slash commands will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTesting'
- >
- <FormattedMessage
- id='admin.service.testingTitle'
- defaultMessage='Enable Testing: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTesting'
- value='true'
- ref='EnableTesting'
- defaultChecked={this.props.config.ServiceSettings.EnableTesting}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTesting'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableTesting}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.testingDescription'
- defaultMessage='(Developer Option) When true, /loadtest slash command is enabled to load test accounts and test data. Changing this will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableDeveloper'
- >
- <FormattedMessage
- id='admin.service.developerTitle'
- defaultMessage='Enable Developer Mode: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableDeveloper'
- value='true'
- ref='EnableDeveloper'
- defaultChecked={this.props.config.ServiceSettings.EnableDeveloper}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableDeveloper'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableDeveloper}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.developerDesc'
- defaultMessage='(Developer Option) When true, extra information around errors will be displayed in the UI.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableSecurityFixAlert'
- >
- <FormattedMessage
- id='admin.service.securityTitle'
- defaultMessage='Enable Security Alerts: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableSecurityFixAlert'
- value='true'
- ref='EnableSecurityFixAlert'
- defaultChecked={this.props.config.ServiceSettings.EnableSecurityFixAlert}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableSecurityFixAlert'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableSecurityFixAlert}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.securityDesc'
- defaultMessage='When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableInsecureOutgoingConnections'
- >
- <FormattedMessage
- id='admin.service.insecureTlsTitle'
- defaultMessage='Enable Insecure Outgoing Connections: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableInsecureOutgoingConnections'
- value='true'
- ref='EnableInsecureOutgoingConnections'
- defaultChecked={this.props.config.ServiceSettings.EnableInsecureOutgoingConnections}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableInsecureOutgoingConnections'
- value='false'
- defaultChecked={!this.props.config.ServiceSettings.EnableInsecureOutgoingConnections}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.service.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.insecureTlsDesc'
- defaultMessage='When true, any outgoing HTTPS requests will accept unverified, self-signed certificates. For example, outgoing webhooks to a server with a self-signed TLS certificate, using any domain, will be allowed. Note that this makes these connections susceptible to man-in-the-middle attacks.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AllowCorsFrom'
- >
- <FormattedMessage
- id='admin.service.corsTitle'
- defaultMessage='Allow Cross-origin Requests from:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AllowCorsFrom'
- ref='AllowCorsFrom'
- placeholder={formatMessage(holders.corsExample)}
- defaultValue={this.props.config.ServiceSettings.AllowCorsFrom}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.corsDescription'
- defaultMessage='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.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthWebInDays'
- >
- <FormattedMessage
- id='admin.service.webSessionDays'
- defaultMessage='Session Length for Web in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthWebInDays'
- ref='SessionLengthWebInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthWebInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.webSessionDaysDesc'
- defaultMessage='The web session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthMobileInDays'
- >
- <FormattedMessage
- id='admin.service.mobileSessionDays'
- defaultMessage='Session Length for Mobile Device in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthMobileInDays'
- ref='SessionLengthMobileInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthMobileInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.mobileSessionDaysDesc'
- defaultMessage='The native mobile session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionLengthSSOInDays'
- >
- <FormattedMessage
- id='admin.service.ssoSessionDays'
- defaultMessage='Session Length for SSO in Days:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionLengthSSOInDays'
- ref='SessionLengthSSOInDays'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionLengthSSOInDays}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.ssoSessionDaysDesc'
- defaultMessage='The SSO session will expire after the number of days specified and will require a user to login again.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SessionCacheInMinutes'
- >
- <FormattedMessage
- id='admin.service.sessionCache'
- defaultMessage='Session Cache in Minutes:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SessionCacheInMinutes'
- ref='SessionCacheInMinutes'
- placeholder={formatMessage(holders.sessionDaysEx)}
- defaultValue={this.props.config.ServiceSettings.SessionCacheInMinutes}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.service.sessionCacheDesc'
- defaultMessage='The number of minutes to cache a session in memory.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.service.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-// <div className='form-group'>
-// <label
-// className='control-label col-sm-4'
-// htmlFor='EnableOAuthServiceProvider'
-// >
-// {'Enable OAuth Service Provider: '}
-// </label>
-// <div className='col-sm-8'>
-// <label className='radio-inline'>
-// <input
-// type='radio'
-// name='EnableOAuthServiceProvider'
-// value='true'
-// ref='EnableOAuthServiceProvider'
-// defaultChecked={this.props.config.ServiceSettings.EnableOAuthServiceProvider}
-// onChange={this.handleChange}
-// />
-// {'true'}
-// </label>
-// <label className='radio-inline'>
-// <input
-// type='radio'
-// name='EnableOAuthServiceProvider'
-// value='false'
-// defaultChecked={!this.props.config.ServiceSettings.EnableOAuthServiceProvider}
-// onChange={this.handleChange}
-// />
-// {'false'}
-// </label>
-// <p className='help-text'>{'When enabled Mattermost will act as an OAuth2 Provider. Changing this will require a server restart before taking effect.'}</p>
-// </div>
-// </div>
-
-ServiceSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(ServiceSettings);
diff --git a/web/react/components/admin_console/sql_settings.jsx b/web/react/components/admin_console/sql_settings.jsx
deleted file mode 100644
index 69ae808f6..000000000
--- a/web/react/components/admin_console/sql_settings.jsx
+++ /dev/null
@@ -1,386 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import crypto from 'crypto';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- warning: {
- id: 'admin.sql.warning',
- defaultMessage: 'Warning: re-generating this salt may cause some columns in the database to return empty results.'
- },
- maxConnectionsExample: {
- id: 'admin.sql.maxConnectionsExample',
- defaultMessage: 'Ex "10"'
- },
- maxOpenExample: {
- id: 'admin.sql.maxOpenExample',
- defaultMessage: 'Ex "10"'
- },
- keyExample: {
- id: 'admin.sql.keyExample',
- defaultMessage: 'Ex "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"'
- },
- saving: {
- id: 'admin.sql.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class SqlSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleGenerate = this.handleGenerate.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.SqlSettings.Trace = ReactDOM.findDOMNode(this.refs.Trace).checked;
- config.SqlSettings.AtRestEncryptKey = ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value.trim();
-
- if (config.SqlSettings.AtRestEncryptKey === '') {
- config.SqlSettings.AtRestEncryptKey = crypto.randomBytes(256).toString('base64').substring(0, 32);
- ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = config.SqlSettings.AtRestEncryptKey;
- }
-
- var MaxOpenConns = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10))) {
- MaxOpenConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10);
- }
- config.SqlSettings.MaxOpenConns = MaxOpenConns;
- ReactDOM.findDOMNode(this.refs.MaxOpenConns).value = MaxOpenConns;
-
- var MaxIdleConns = 10;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10))) {
- MaxIdleConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10);
- }
- config.SqlSettings.MaxIdleConns = MaxIdleConns;
- ReactDOM.findDOMNode(this.refs.MaxIdleConns).value = MaxIdleConns;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- handleGenerate(e) {
- e.preventDefault();
-
- var cfm = global.window.confirm(this.props.intl.formatMessage(holders.warning));
- if (cfm === false) {
- return;
- }
-
- ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- var dataSource = '**********' + this.props.config.SqlSettings.DataSource.substring(this.props.config.SqlSettings.DataSource.indexOf('@'));
-
- var dataSourceReplicas = '';
- this.props.config.SqlSettings.DataSourceReplicas.forEach((replica) => {
- dataSourceReplicas += '[**********' + replica.substring(replica.indexOf('@')) + '] ';
- });
-
- if (this.props.config.SqlSettings.DataSourceReplicas.length === 0) {
- dataSourceReplicas = 'none';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <div className='banner'>
- <div className='banner__content'>
- <h4 className='banner__heading'>
- <FormattedMessage
- id='admin.sql.noteTitle'
- defaultMessage='Note:'
- />
- </h4>
- <p>
- <FormattedMessage
- id='admin.sql.noteDescription'
- defaultMessage='Changing properties in this section will require a server restart before taking effect.'
- />
- </p>
- </div>
- </div>
-
- <h3>
- <FormattedMessage
- id='admin.sql.title'
- defaultMessage='SQL Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DriverName'
- >
- <FormattedMessage
- id='admin.sql.driverName'
- defaultMessage='Driver Name:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{this.props.config.SqlSettings.DriverName}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DataSource'
- >
- <FormattedMessage
- id='admin.sql.dataSource'
- defaultMessage='Data Source:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{dataSource}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='DataSourceReplicas'
- >
- <FormattedMessage
- id='admin.sql.replicas'
- defaultMessage='Data Source Replicas:'
- />
- </label>
- <div className='col-sm-8'>
- <p className='help-text'>{dataSourceReplicas}</p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxIdleConns'
- >
- <FormattedMessage
- id='admin.sql.maxConnectionsTitle'
- defaultMessage='Maximum Idle Connections:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxIdleConns'
- ref='MaxIdleConns'
- placeholder={formatMessage(holders.maxConnectionsExample)}
- defaultValue={this.props.config.SqlSettings.MaxIdleConns}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.maxConnectionsDescription'
- defaultMessage='Maximum number of idle connections held open to the database.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxOpenConns'
- >
- <FormattedMessage
- id='admin.sql.maxOpenTitle'
- defaultMessage='Maximum Open Connections:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxOpenConns'
- ref='MaxOpenConns'
- placeholder={formatMessage(holders.maxOpenExample)}
- defaultValue={this.props.config.SqlSettings.MaxOpenConns}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.maxOpenDescription'
- defaultMessage='Maximum number of open connections held open to the database.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='AtRestEncryptKey'
- >
- <FormattedMessage
- id='admin.sql.keyTitle'
- defaultMessage='At Rest Encrypt Key:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='AtRestEncryptKey'
- ref='AtRestEncryptKey'
- placeholder={formatMessage(holders.keyExample)}
- defaultValue={this.props.config.SqlSettings.AtRestEncryptKey}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.keyDescription'
- defaultMessage='32-character salt available to encrypt and decrypt sensitive fields in database.'
- />
- </p>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerate}
- >
- <FormattedMessage
- id='admin.sql.regenerate'
- defaultMessage='Re-Generate'
- />
- </button>
- </div>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='Trace'
- >
- <FormattedMessage
- id='admin.sql.traceTitle'
- defaultMessage='Trace: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Trace'
- value='true'
- ref='Trace'
- defaultChecked={this.props.config.SqlSettings.Trace}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.sql.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='Trace'
- value='false'
- defaultChecked={!this.props.config.SqlSettings.Trace}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.sql.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.sql.traceDescription'
- defaultMessage='(Development Mode) When true, executing SQL statements are written to the log.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.sql.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-SqlSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(SqlSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/team_settings.jsx b/web/react/components/admin_console/team_settings.jsx
deleted file mode 100644
index cc4ff38ba..000000000
--- a/web/react/components/admin_console/team_settings.jsx
+++ /dev/null
@@ -1,416 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- siteNameExample: {
- id: 'admin.team.siteNameExample',
- defaultMessage: 'Ex "Mattermost"'
- },
- maxUsersExample: {
- id: 'admin.team.maxUsersExample',
- defaultMessage: 'Ex "25"'
- },
- restrictExample: {
- id: 'admin.team.restrictExample',
- defaultMessage: 'Ex "corp.mattermost.com, mattermost.org"'
- },
- saving: {
- id: 'admin.team.saving',
- defaultMessage: 'Saving Config...'
- }
-});
-
-class TeamSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- saveNeeded: false,
- serverError: null
- };
- }
-
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
- this.setState(s);
- }
-
- handleSubmit(e) {
- e.preventDefault();
- $('#save-button').button('loading');
-
- var config = this.props.config;
- config.TeamSettings.SiteName = ReactDOM.findDOMNode(this.refs.SiteName).value.trim();
- config.TeamSettings.RestrictCreationToDomains = ReactDOM.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
- config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked;
- config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked;
- config.TeamSettings.RestrictTeamNames = ReactDOM.findDOMNode(this.refs.RestrictTeamNames).checked;
- config.TeamSettings.EnableTeamListing = ReactDOM.findDOMNode(this.refs.EnableTeamListing).checked;
-
- var MaxUsersPerTeam = 50;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
- MaxUsersPerTeam = parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
- }
- config.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam;
- ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
-
- Client.saveConfig(
- config,
- () => {
- AsyncClient.getConfig();
- this.setState({
- serverError: null,
- saveNeeded: false
- });
- $('#save-button').button('reset');
- },
- (err) => {
- this.setState({
- serverError: err.message,
- saveNeeded: true
- });
- $('#save-button').button('reset');
- }
- );
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var saveClass = 'btn';
- if (this.state.saveNeeded) {
- saveClass = 'btn btn-primary';
- }
-
- return (
- <div className='wrapper--fixed'>
-
- <h3>
- <FormattedMessage
- id='admin.team.title'
- defaultMessage='Team Settings'
- />
- </h3>
- <form
- className='form-horizontal'
- role='form'
- >
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='SiteName'
- >
- <FormattedMessage
- id='admin.team.siteNameTitle'
- defaultMessage='Site Name:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='SiteName'
- ref='SiteName'
- placeholder={formatMessage(holders.siteNameExample)}
- defaultValue={this.props.config.TeamSettings.SiteName}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.siteNameDescription'
- defaultMessage='Name of service shown in login screens and UI.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='MaxUsersPerTeam'
- >
- <FormattedMessage
- id='admin.team.maxUsersTitle'
- defaultMessage='Max Users Per Team:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='MaxUsersPerTeam'
- ref='MaxUsersPerTeam'
- placeholder={formatMessage(holders.maxUsersExample)}
- defaultValue={this.props.config.TeamSettings.MaxUsersPerTeam}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.maxUsersDescription'
- defaultMessage='Maximum total number of users per team, including both active and inactive users.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTeamCreation'
- >
- <FormattedMessage
- id='admin.team.teamCreationTitle'
- defaultMessage='Enable Team Creation: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamCreation'
- value='true'
- ref='EnableTeamCreation'
- defaultChecked={this.props.config.TeamSettings.EnableTeamCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamCreation'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableTeamCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.teamCreationDescription'
- defaultMessage='When false, the ability to create teams is disabled. The create team button displays error when pressed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableUserCreation'
- >
- <FormattedMessage
- id='admin.team.userCreationTitle'
- defaultMessage='Enable User Creation: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableUserCreation'
- value='true'
- ref='EnableUserCreation'
- defaultChecked={this.props.config.TeamSettings.EnableUserCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableUserCreation'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableUserCreation}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.userCreationDescription'
- defaultMessage='When false, the ability to create accounts is disabled. The create account button displays error when pressed.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='RestrictCreationToDomains'
- >
- <FormattedMessage
- id='admin.team.restrictTitle'
- defaultMessage='Restrict Creation To Domains:'
- />
- </label>
- <div className='col-sm-8'>
- <input
- type='text'
- className='form-control'
- id='RestrictCreationToDomains'
- ref='RestrictCreationToDomains'
- placeholder={formatMessage(holders.restrictExample)}
- defaultValue={this.props.config.TeamSettings.RestrictCreationToDomains}
- onChange={this.handleChange}
- />
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.restrictDescription'
- defaultMessage='Teams and user accounts can only be created from a specific domain (e.g. "mattermost.org") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.org").'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='RestrictTeamNames'
- >
- <FormattedMessage
- id='admin.team.restrictNameTitle'
- defaultMessage='Restrict Team Names: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='RestrictTeamNames'
- value='true'
- ref='RestrictTeamNames'
- defaultChecked={this.props.config.TeamSettings.RestrictTeamNames}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='RestrictTeamNames'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.RestrictTeamNames}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.restrictNameDesc'
- defaultMessage='When true, You cannot create a team name with reserved words like www, admin, support, test, channel, etc'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTeamListing'
- >
- <FormattedMessage
- id='admin.team.dirTitle'
- defaultMessage='Enable Team Directory: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='true'
- ref='EnableTeamListing'
- defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.dirDesc'
- defaultMessage='When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'
- />
- </p>
- </div>
- </div>
-
- <div className='form-group'>
- <div className='col-sm-12'>
- {serverError}
- <button
- disabled={!this.state.saveNeeded}
- type='submit'
- className={saveClass}
- onClick={this.handleSubmit}
- id='save-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
- >
- <FormattedMessage
- id='admin.team.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
-
- </form>
- </div>
- );
- }
-}
-
-TeamSettings.propTypes = {
- intl: intlShape.isRequired,
- config: React.PropTypes.object
-};
-
-export default injectIntl(TeamSettings); \ No newline at end of file
diff --git a/web/react/components/admin_console/team_users.jsx b/web/react/components/admin_console/team_users.jsx
deleted file mode 100644
index 708907ddb..000000000
--- a/web/react/components/admin_console/team_users.jsx
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-import UserItem from './user_item.jsx';
-import ResetPasswordModal from './reset_password_modal.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class UserList extends React.Component {
- constructor(props) {
- super(props);
-
- this.getTeamProfiles = this.getTeamProfiles.bind(this);
- this.getCurrentTeamProfiles = this.getCurrentTeamProfiles.bind(this);
- this.doPasswordReset = this.doPasswordReset.bind(this);
- this.doPasswordResetDismiss = this.doPasswordResetDismiss.bind(this);
- this.doPasswordResetSubmit = this.doPasswordResetSubmit.bind(this);
-
- this.state = {
- teamId: props.team.id,
- users: null,
- serverError: null,
- showPasswordModal: false,
- user: null
- };
- }
-
- componentDidMount() {
- this.getCurrentTeamProfiles();
- }
-
- getCurrentTeamProfiles() {
- this.getTeamProfiles(this.props.team.id);
- }
-
- getTeamProfiles(teamId) {
- Client.getProfilesForTeam(
- teamId,
- (users) => {
- var memberList = [];
- for (var id in users) {
- if (users.hasOwnProperty(id)) {
- memberList.push(users[id]);
- }
- }
-
- memberList.sort((a, b) => {
- if (a.username < b.username) {
- return -1;
- }
-
- if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- });
-
- this.setState({
- teamId: this.state.teamId,
- users: memberList,
- serverError: this.state.serverError,
- showPasswordModal: this.state.showPasswordModal,
- user: this.state.user
- });
- },
- (err) => {
- this.setState({
- teamId: this.state.teamId,
- users: null,
- serverError: err.message,
- showPasswordModal: this.state.showPasswordModal,
- user: this.state.user
- });
- }
- );
- }
-
- doPasswordReset(user) {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: true,
- user
- });
- }
-
- doPasswordResetDismiss() {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: false,
- user: null
- });
- }
-
- doPasswordResetSubmit() {
- this.setState({
- teamId: this.state.teamId,
- users: this.state.users,
- serverError: this.state.serverError,
- showPasswordModal: false,
- user: null
- });
- }
-
- componentWillReceiveProps(newProps) {
- this.getTeamProfiles(newProps.team.id);
- }
-
- render() {
- var serverError = '';
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- if (this.state.users == null) {
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.userList.title'
- defaultMessage='Users for {team}'
- values={{
- team: this.props.team.name
- }}
- />
- </h3>
- {serverError}
- <LoadingScreen/>
- </div>
- );
- }
-
- var memberList = this.state.users.map((user) => {
- return (
- <UserItem
- key={'user_' + user.id}
- user={user}
- refreshProfiles={this.getCurrentTeamProfiles}
- doPasswordReset={this.doPasswordReset}
- />);
- });
-
- return (
- <div className='wrapper--fixed'>
- <h3>
- <FormattedMessage
- id='admin.userList.title2'
- defaultMessage='Users for {team} ({count})'
- values={{
- team: this.props.team.name,
- count: this.state.users.length
- }}
- />
- </h3>
- {serverError}
- <form
- className='form-horizontal'
- role='form'
- >
- <table className='more-modal__list member-list-holder'>
- <tbody>
- {memberList}
- </tbody>
- </table>
- </form>
- <ResetPasswordModal
- user={this.state.user}
- show={this.state.showPasswordModal}
- team={this.props.team}
- onModalSubmit={this.doPasswordResetSubmit}
- onModalDismissed={this.doPasswordResetDismiss}
- />
- </div>
- );
- }
-}
-
-UserList.propTypes = {
- team: React.PropTypes.object
-};
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
deleted file mode 100644
index 722da922a..000000000
--- a/web/react/components/admin_console/user_item.jsx
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import UserStore from '../../stores/user_store.jsx';
-import ConfirmModal from '../confirm_modal.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class UserItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleMakeMember = this.handleMakeMember.bind(this);
- this.handleMakeActive = this.handleMakeActive.bind(this);
- this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
- this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
- this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
- this.handleResetPassword = this.handleResetPassword.bind(this);
- this.handleDemote = this.handleDemote.bind(this);
- this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
- this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
-
- this.state = {
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- };
- }
-
- handleMakeMember(e) {
- e.preventDefault();
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, '');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
-
- handleMakeActive(e) {
- e.preventDefault();
- Client.updateActive(this.props.user.id, true,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleMakeNotActive(e) {
- e.preventDefault();
- Client.updateActive(this.props.user.id, false,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleMakeAdmin(e) {
- e.preventDefault();
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
-
- handleMakeSystemAdmin(e) {
- e.preventDefault();
- const data = {
- user_id: this.props.user.id,
- new_roles: 'system_admin'
- };
-
- Client.updateRoles(data,
- () => {
- this.props.refreshProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleResetPassword(e) {
- e.preventDefault();
- this.props.doPasswordReset(this.props.user);
- }
-
- handleDemote(user, role) {
- this.setState({
- serverError: this.state.serverError,
- showDemoteModal: true,
- user,
- role
- });
- }
-
- handleDemoteCancel() {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
- }
-
- handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
- () => {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
-
- const teamUrl = TeamStore.getCurrentTeamUrl();
- if (teamUrl) {
- window.location.href = teamUrl;
- } else {
- window.location.href = '/';
- }
- },
- (err) => {
- this.setState({
- serverError: err.message
- });
- }
- );
- }
-
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='has-error control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- const user = this.props.user;
- let currentRoles = (
- <FormattedMessage
- id='admin.user_item.member'
- defaultMessage='Member'
- />
- );
- if (user.roles.length > 0) {
- if (Utils.isSystemAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.sysAdmin'
- defaultMessage='System Admin'
- />
- );
- } else if (Utils.isAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.teamAdmin'
- defaultMessage='Team Admin'
- />
- );
- } else {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
- }
- }
-
- const email = user.email;
- let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
- let showMakeSystemAdmin = user.roles === '' || user.roles === 'admin';
- let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
-
- if (user.delete_at > 0) {
- currentRoles = (
- <FormattedMessage
- id='admin.user_item.inactive'
- defaultMessage='Inactive'
- />
- );
- showMakeMember = false;
- showMakeAdmin = false;
- showMakeSystemAdmin = false;
- showMakeActive = true;
- showMakeNotActive = false;
- }
-
- let makeSystemAdmin = null;
- if (showMakeSystemAdmin) {
- makeSystemAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeSystemAdmin}
- >
- <FormattedMessage
- id='admin.user_item.makeSysAdmin'
- defaultMessage='Make System Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeAdmin = null;
- if (showMakeAdmin) {
- makeAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeAdmin}
- >
- <FormattedMessage
- id='admin.user_item.makeTeamAdmin'
- defaultMessage='Make Team Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeMember = null;
- if (showMakeMember) {
- makeMember = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeMember}
- >
- <FormattedMessage
- id='admin.user_item.makeMember'
- defaultMessage='Make Member'
- />
- </a>
- </li>
- );
- }
-
- let makeActive = null;
- if (showMakeActive) {
- makeActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeActive}
- >
- <FormattedMessage
- id='admin.user_item.makeActive'
- defaultMessage='Make Active'
- />
- </a>
- </li>
- );
- }
-
- let makeNotActive = null;
- if (showMakeNotActive) {
- makeNotActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeNotActive}
- >
- <FormattedMessage
- id='admin.user_item.makeInactive'
- defaultMessage='Make Inactive'
- />
- </a>
- </li>
- );
- }
- const me = UserStore.getCurrentUser();
- let makeDemoteModal = null;
- if (this.props.user.id === me.id) {
- const title = (
- <FormattedMessage
- id='admin.user_item.confirmDemoteRoleTitle'
- defaultMessage='Confirm demotion from System Admin role'
- />
- );
-
- const message = (
- <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."
- />
- <br/>
- <br/>
- <FormattedMessage
- id='admin.user_item.confirmDemotionCmd'
- defaultMessage='platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
- />
- {serverError}
- </div>
- );
-
- const confirmButton = (
- <FormattedMessage
- id='admin.user_item.confirmDemotion'
- defaultMessage='Confirm Demotion'
- />
- );
-
- makeDemoteModal = (
- <ConfirmModal
- show={this.state.showDemoteModal}
- title={title}
- message={message}
- confirmButton={confirmButton}
- onConfirm={this.handleDemoteSubmit}
- onCancel={this.handleDemoteCancel}
- />
- );
- }
-
- return (
- <tr>
- <td className='more-modal__row'>
- <img
- className='more-modal__image pull-left'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
- height='36'
- width='36'
- />
- <span className='more-modal__name'>{Utils.getDisplayName(user)}</span>
- <span className='more-modal__description'>{email}</span>
- <div className='dropdown member-drop'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span>{currentRoles} </span>
- <span className='caret'></span>
- </a>
- <ul
- className='dropdown-menu member-menu'
- role='menu'
- >
- {makeAdmin}
- {makeMember}
- {makeActive}
- {makeNotActive}
- {makeSystemAdmin}
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleResetPassword}
- >
- <FormattedMessage
- id='admin.user_item.resetPwd'
- defaultMessage='Reset Password'
- />
- </a>
- </li>
- </ul>
- </div>
- {makeDemoteModal}
- {serverError}
- </td>
- </tr>
- );
- }
-}
-
-UserItem.propTypes = {
- user: React.PropTypes.object.isRequired,
- refreshProfiles: React.PropTypes.func.isRequired,
- doPasswordReset: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/analytics/doughnut_chart.jsx b/web/react/components/analytics/doughnut_chart.jsx
deleted file mode 100644
index 00bb66f0a..000000000
--- a/web/react/components/analytics/doughnut_chart.jsx
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class DoughnutChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart(this.props);
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.chart) {
- this.chart.destroy();
- this.initChart(nextProps);
- }
- }
-
- componentWillUnmount() {
- if (this.chart) {
- this.chart.destroy();
- }
- }
-
- initChart(props) {
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx).Doughnut(props.data, props.options || {}); //eslint-disable-line new-cap
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-6'>
- <div className='total-count'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-DoughnutChart.propTypes = {
- title: React.PropTypes.node,
- width: React.PropTypes.string,
- height: React.PropTypes.string,
- data: React.PropTypes.array,
- options: React.PropTypes.object
-};
diff --git a/web/react/components/analytics/line_chart.jsx b/web/react/components/analytics/line_chart.jsx
deleted file mode 100644
index d1bb6b9cb..000000000
--- a/web/react/components/analytics/line_chart.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class LineChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart();
- }
-
- componentDidUpdate() {
- if (this.chart) {
- this.chart.destroy();
- }
- this.initChart();
- }
-
- componentWillUnmount() {
- if (this.chart) {
- this.chart.destroy();
- }
- }
-
- initChart() {
- if (!this.refs.canvas) {
- return;
- }
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx).Line(this.props.data, this.props.options || {}); //eslint-disable-line new-cap
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else if (this.props.data.labels.length === 0) {
- content = (
- <h5>
- <FormattedMessage
- id='analytics.chart.meaningful'
- defaultMessage='Not enough data for a meaningful representation.'
- />
- </h5>
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-12'>
- <div className='total-count by-day'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-LineChart.propTypes = {
- title: React.PropTypes.node.isRequired,
- width: React.PropTypes.string.isRequired,
- height: React.PropTypes.string.isRequired,
- data: React.PropTypes.object,
- options: React.PropTypes.object
-};
-
diff --git a/web/react/components/analytics/statistic_count.jsx b/web/react/components/analytics/statistic_count.jsx
deleted file mode 100644
index cf457310f..000000000
--- a/web/react/components/analytics/statistic_count.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class StatisticCount extends React.Component {
- render() {
- let loading = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
-
- return (
- <div className='col-sm-3'>
- <div className='total-count'>
- <div className='title'>
- {this.props.title}
- <i className={'fa ' + this.props.icon}/>
- </div>
- <div className='content'>{this.props.count == null ? loading : this.props.count}</div>
- </div>
- </div>
- );
- }
-}
-
-StatisticCount.propTypes = {
- title: React.PropTypes.node.isRequired,
- icon: React.PropTypes.string.isRequired,
- count: React.PropTypes.number
-};
diff --git a/web/react/components/analytics/system_analytics.jsx b/web/react/components/analytics/system_analytics.jsx
deleted file mode 100644
index a2b783a79..000000000
--- a/web/react/components/analytics/system_analytics.jsx
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LineChart from './line_chart.jsx';
-import DoughnutChart from './doughnut_chart.jsx';
-import StatisticCount from './statistic_count.jsx';
-
-import AnalyticsStore from '../../stores/analytics_store.jsx';
-
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import Constants from '../../utils/constants.jsx';
-const StatTypes = Constants.StatTypes;
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- analyticsPublicChannels: {
- id: 'analytics.system.publicChannels',
- defaultMessage: 'Public Channels'
- },
- analyticsPrivateGroups: {
- id: 'analytics.system.privateGroups',
- defaultMessage: 'Private Groups'
- },
- analyticsFilePosts: {
- id: 'analytics.system.totalFilePosts',
- defaultMessage: 'Posts with Files'
- },
- analyticsHashtagPosts: {
- id: 'analytics.system.totalHashtagPosts',
- defaultMessage: 'Posts with Hashtags'
- },
- analyticsTextPosts: {
- id: 'analytics.system.textPosts',
- defaultMessage: 'Posts with Text-only'
- }
-});
-
-class SystemAnalytics extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {stats: AnalyticsStore.getAllSystem()};
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- AsyncClient.getStandardAnalytics();
- AsyncClient.getPostsPerDayAnalytics();
- AsyncClient.getUsersPerDayAnalytics();
-
- if (global.window.mm_license.IsLicensed === 'true') {
- AsyncClient.getAdvancedAnalytics();
- }
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.stats, this.state.stats)) {
- return true;
- }
-
- return false;
- }
-
- onChange() {
- this.setState({stats: AnalyticsStore.getAllSystem()});
- }
-
- render() {
- const stats = this.state.stats;
-
- let advancedCounts;
- let advancedGraphs;
- if (global.window.mm_license.IsLicensed === 'true') {
- advancedCounts = (
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalSessions'
- defaultMessage='Total Sessions'
- />
- }
- icon='fa-signal'
- count={stats[StatTypes.TOTAL_SESSIONS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalCommands'
- defaultMessage='Total Commands'
- />
- }
- icon='fa-terminal'
- count={stats[StatTypes.TOTAL_COMMANDS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalIncomingWebhooks'
- defaultMessage='Incoming Webhooks'
- />
- }
- icon='fa-arrow-down'
- count={stats[StatTypes.TOTAL_IHOOKS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalOutgoingWebhooks'
- defaultMessage='Outgoing Webhooks'
- />
- }
- icon='fa-arrow-up'
- count={stats[StatTypes.TOTAL_OHOOKS]}
- />
- </div>
- );
-
- const channelTypeData = formatChannelDoughtnutData(stats[StatTypes.TOTAL_PUBLIC_CHANNELS], stats[StatTypes.TOTAL_PRIVATE_GROUPS], this.props.intl);
- const postTypeData = formatPostDoughtnutData(stats[StatTypes.TOTAL_FILE_POSTS], stats[StatTypes.TOTAL_HASHTAG_POSTS], stats[StatTypes.TOTAL_POSTS], this.props.intl);
-
- advancedGraphs = (
- <div className='row'>
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.channelTypes'
- defaultMessage='Channel Types'
- />
- }
- data={channelTypeData}
- width='300'
- height='225'
- />
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.postTypes'
- defaultMessage='Posts, Files and Hashtags'
- />
- }
- data={postTypeData}
- width='300'
- height='225'
- />
- </div>
- );
- }
-
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <h3>
- <FormattedMessage
- id='analytics.system.title'
- defaultMessage='System Statistics'
- />
- </h3>
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalTeams'
- defaultMessage='Total Teams'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_TEAMS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalChannels'
- defaultMessage='Total Channels'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS] + stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- </div>
- {advancedCounts}
- {advancedGraphs}
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- width='740'
- height='225'
- />
- </div>
- </div>
- );
- }
-}
-
-SystemAnalytics.propTypes = {
- intl: intlShape.isRequired,
- team: React.PropTypes.object
-};
-
-export default injectIntl(SystemAnalytics);
-
-export function formatChannelDoughtnutData(totalPublic, totalPrivate, intl) {
- const {formatMessage} = intl;
- const channelTypeData = [
- {
- value: totalPublic,
- color: '#46BFBD',
- highlight: '#5AD3D1',
- label: formatMessage(holders.analyticsPublicChannels)
- },
- {
- value: totalPrivate,
- color: '#FDB45C',
- highlight: '#FFC870',
- label: formatMessage(holders.analyticsPrivateGroups)
- }
- ];
-
- return channelTypeData;
-}
-
-export function formatPostDoughtnutData(filePosts, hashtagPosts, totalPosts, intl) {
- const {formatMessage} = intl;
- const postTypeData = [
- {
- value: filePosts,
- color: '#46BFBD',
- highlight: '#5AD3D1',
- label: formatMessage(holders.analyticsFilePosts)
- },
- {
- value: hashtagPosts,
- color: '#F7464A',
- highlight: '#FF5A5E',
- label: formatMessage(holders.analyticsHashtagPosts)
- },
- {
- value: totalPosts - filePosts - hashtagPosts,
- color: '#FDB45C',
- highlight: '#FFC870',
- label: formatMessage(holders.analyticsTextPosts)
- }
- ];
-
- return postTypeData;
-}
-
-export function formatPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- fillColor: 'rgba(151,187,205,0.2)',
- strokeColor: 'rgba(151,187,205,1)',
- pointColor: 'rgba(151,187,205,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(151,187,205,1)',
- data: []
- }]
- };
-
- for (var index in data) {
- if (data[index]) {
- var row = data[index];
- chartData.labels.push(row.name);
- chartData.datasets[0].data.push(row.value);
- }
- }
-
- return chartData;
-}
-
-export function formatUsersWithPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- fillColor: 'rgba(151,187,205,0.2)',
- strokeColor: 'rgba(151,187,205,1)',
- pointColor: 'rgba(151,187,205,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(151,187,205,1)',
- data: []
- }]
- };
-
- for (var index in data) {
- if (data[index]) {
- var row = data[index];
- chartData.labels.push(row.name);
- chartData.datasets[0].data.push(row.value);
- }
- }
-
- return chartData;
-}
diff --git a/web/react/components/analytics/table_chart.jsx b/web/react/components/analytics/table_chart.jsx
deleted file mode 100644
index c94fa300b..000000000
--- a/web/react/components/analytics/table_chart.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class TableChart extends React.Component {
- render() {
- return (
- <div className='col-sm-6'>
- <div className='total-count recent-active-users'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- <table>
- <tbody>
- {
- this.props.data.map((item) => {
- const tooltip = (
- <Tooltip id={'tip-table-entry-' + item.name}>
- {item.tip}
- </Tooltip>
- );
-
- return (
- <tr key={'table-entry-' + item.name}>
- <td>
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={tooltip}
- >
- <time>
- {item.name}
- </time>
- </OverlayTrigger>
- </td>
- <td>
- {item.value}
- </td>
- </tr>
- );
- })
- }
- </tbody>
- </table>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TableChart.propTypes = {
- title: React.PropTypes.node,
- data: React.PropTypes.array
-};
diff --git a/web/react/components/analytics/team_analytics.jsx b/web/react/components/analytics/team_analytics.jsx
deleted file mode 100644
index 1236c070b..000000000
--- a/web/react/components/analytics/team_analytics.jsx
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LineChart from './line_chart.jsx';
-import StatisticCount from './statistic_count.jsx';
-import TableChart from './table_chart.jsx';
-
-import AnalyticsStore from '../../stores/analytics_store.jsx';
-
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import Constants from '../../utils/constants.jsx';
-const StatTypes = Constants.StatTypes;
-
-import {formatPostsPerDayData, formatUsersWithPostsPerDayData} from './system_analytics.jsx';
-import {injectIntl, intlShape, FormattedMessage, FormattedDate} from 'mm-intl';
-
-class TeamAnalytics extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {stats: AnalyticsStore.getAllTeam(this.props.team.id)};
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- this.getData(this.props.team.id);
- }
-
- getData(id) {
- AsyncClient.getStandardAnalytics(id);
- AsyncClient.getPostsPerDayAnalytics(id);
- AsyncClient.getUsersPerDayAnalytics(id);
- AsyncClient.getRecentAndNewUsersAnalytics(id);
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- componentWillReceiveProps(nextProps) {
- this.getData(nextProps.team.id);
- this.setState({stats: AnalyticsStore.getAllTeam(nextProps.team.id)});
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.stats, this.state.stats)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps.team, this.props.team)) {
- return true;
- }
-
- return false;
- }
-
- onChange() {
- this.setState({stats: AnalyticsStore.getAllTeam(this.props.team.id)});
- }
-
- render() {
- const stats = this.state.stats;
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
- const recentActiveUsers = formatRecentUsersData(stats[StatTypes.RECENTLY_ACTIVE_USERS]);
- const newlyCreatedUsers = formatNewUsersData(stats[StatTypes.NEWLY_CREATED_USERS]);
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <h3>
- <FormattedMessage
- id='analytics.team.title'
- defaultMessage='Team Statistics for {team}'
- values={{
- team: this.props.team.name
- }}
- />
- </h3>
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.publicChannels'
- defaultMessage='Public Channels'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.privateGroups'
- defaultMessage='Private Groups'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.team.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- width='740'
- height='225'
- />
- </div>
- <div className='row'>
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.activeUsers'
- defaultMessage='Recent Active Users'
- />
- }
- data={recentActiveUsers}
- />
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.newlyCreated'
- defaultMessage='Newly Created Users'
- />
- }
- data={newlyCreatedUsers}
- />
- </div>
- </div>
- );
- }
-}
-
-TeamAnalytics.propTypes = {
- intl: intlShape.isRequired,
- team: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(TeamAnalytics);
-
-export function formatRecentUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.last_activity_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}
-
-export function formatNewUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}
diff --git a/web/react/components/audio_video_preview.jsx b/web/react/components/audio_video_preview.jsx
deleted file mode 100644
index 739c8c95e..000000000
--- a/web/react/components/audio_video_preview.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import FileInfoPreview from './file_info_preview.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default class AudioVideoPreview extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleFileInfoChanged = this.handleFileInfoChanged.bind(this);
- this.handleLoadError = this.handleLoadError.bind(this);
-
- this.stop = this.stop.bind(this);
-
- this.state = {
- canPlay: true
- };
- }
-
- componentWillMount() {
- this.handleFileInfoChanged(this.props.fileInfo);
- }
-
- componentDidMount() {
- if (this.refs.source) {
- $(ReactDOM.findDOMNode(this.refs.source)).one('error', this.handleLoadError);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.fileUrl !== nextProps.fileUrl) {
- this.handleFileInfoChanged(nextProps.fileInfo);
- }
- }
-
- handleFileInfoChanged(fileInfo) {
- let video = ReactDOM.findDOMNode(this.refs.video);
- if (!video) {
- video = document.createElement('video');
- }
-
- const canPlayType = video.canPlayType(fileInfo.mime_type);
-
- this.setState({
- canPlay: canPlayType === 'probably' || canPlayType === 'maybe'
- });
- }
-
- componentDidUpdate() {
- if (this.refs.source) {
- $(ReactDOM.findDOMNode(this.refs.source)).one('error', this.handleLoadError);
- }
- }
-
- handleLoadError() {
- this.setState({
- canPlay: false
- });
- }
-
- stop() {
- if (this.refs.video) {
- const video = ReactDOM.findDOMNode(this.refs.video);
- video.pause();
- video.currentTime = 0;
- }
- }
-
- render() {
- if (!this.state.canPlay) {
- return (
- <FileInfoPreview
- filename={this.props.filename}
- fileUrl={this.props.fileUrl}
- fileInfo={this.props.fileInfo}
- formatMessage={this.props.formatMessage}
- />
- );
- }
-
- let width = Constants.WEB_VIDEO_WIDTH;
- let height = Constants.WEB_VIDEO_HEIGHT;
- if (Utils.isMobile()) {
- width = Constants.MOBILE_VIDEO_WIDTH;
- height = Constants.MOBILE_VIDEO_HEIGHT;
- }
-
- // add a key to the video to prevent React from using an old video source while a new one is loading
- return (
- <video
- key={this.props.filename}
- ref='video'
- style={{maxHeight: this.props.maxHeight}}
- data-setup='{}'
- controls='controls'
- width={width}
- height={height}
- >
- <source
- ref='source'
- src={this.props.fileUrl}
- />
- </video>
- );
- }
-}
-
-AudioVideoPreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- maxHeight: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).isRequired,
- formatMessage: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/audit_table.jsx b/web/react/components/audit_table.jsx
deleted file mode 100644
index 917093840..000000000
--- a/web/react/components/audit_table.jsx
+++ /dev/null
@@ -1,624 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate, FormattedTime} from 'mm-intl';
-
-const holders = defineMessages({
- sessionRevoked: {
- id: 'audit_table.sessionRevoked',
- defaultMessage: 'The session with id {sessionId} was revoked'
- },
- channelCreated: {
- id: 'audit_table.channelCreated',
- defaultMessage: 'Created the {channelName} channel/group'
- },
- establishedDM: {
- id: 'audit_table.establishedDM',
- defaultMessage: 'Established a direct message channel with {username}'
- },
- nameUpdated: {
- id: 'audit_table.nameUpdated',
- defaultMessage: 'Updated the {channelName} channel/group name'
- },
- headerUpdated: {
- id: 'audit_table.headerUpdated',
- defaultMessage: 'Updated the {channelName} channel/group header'
- },
- channelDeleted: {
- id: 'audit_table.channelDeleted',
- defaultMessage: 'Deleted the channel/group with the URL {url}'
- },
- userAdded: {
- id: 'audit_table.userAdded',
- defaultMessage: 'Added {username} to the {channelName} channel/group'
- },
- userRemoved: {
- id: 'audit_table.userRemoved',
- defaultMessage: 'Removed {username} to the {channelName} channel/group'
- },
- attemptedRegisterApp: {
- id: 'audit_table.attemptedRegisterApp',
- defaultMessage: 'Attempted to register a new OAuth Application with ID {id}'
- },
- attemptedAllowOAuthAccess: {
- id: 'audit_table.attemptedAllowOAuthAccess',
- defaultMessage: 'Attempted to allow a new OAuth service access'
- },
- successfullOAuthAccess: {
- id: 'audit_table.successfullOAuthAccess',
- defaultMessage: 'Successfully gave a new OAuth service access'
- },
- failedOAuthAccess: {
- id: 'audit_table.failedOAuthAccess',
- defaultMessage: 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback'
- },
- attemptedOAuthToken: {
- id: 'audit_table.attemptedOAuthToken',
- defaultMessage: 'Attempted to get an OAuth access token'
- },
- successfullOAuthToken: {
- id: 'audit_table.successfullOAuthToken',
- defaultMessage: 'Successfully added a new OAuth service'
- },
- oauthTokenFailed: {
- id: 'audit_table.oauthTokenFailed',
- defaultMessage: 'Failed to get an OAuth access token - {token}'
- },
- attemptedLogin: {
- id: 'audit_table.attemptedLogin',
- defaultMessage: 'Attempted to login'
- },
- successfullLogin: {
- id: 'audit_table.successfullLogin',
- defaultMessage: 'Successfully logged in'
- },
- failedLogin: {
- id: 'audit_table.failedLogin',
- defaultMessage: 'FAILED login attempt'
- },
- updatePicture: {
- id: 'audit_table.updatePicture',
- defaultMessage: 'Updated your profile picture'
- },
- updateGeneral: {
- id: 'audit_table.updateGeneral',
- defaultMessage: 'Updated the general settings of your account'
- },
- attemptedPassword: {
- id: 'audit_table.attemptedPassword',
- defaultMessage: 'Attempted to change password'
- },
- successfullPassword: {
- id: 'audit_table.successfullPassword',
- defaultMessage: 'Successfully changed password'
- },
- failedPassword: {
- id: 'audit_table.failedPassword',
- defaultMessage: 'Failed to change password - tried to update user password who was logged in through oauth'
- },
- updatedRol: {
- id: 'audit_table.updatedRol',
- defaultMessage: 'Updated user role(s) to '
- },
- member: {
- id: 'audit_table.member',
- defaultMessage: 'member'
- },
- accountActive: {
- id: 'audit_table.accountActive',
- defaultMessage: 'Account made active'
- },
- accountInactive: {
- id: 'audit_table.accountInactive',
- defaultMessage: 'Account made inactive'
- },
- by: {
- id: 'audit_table.by',
- defaultMessage: ' by {username}'
- },
- byAdmin: {
- id: 'audit_table.byAdmin',
- defaultMessage: ' by an admin'
- },
- sentEmail: {
- id: 'audit_table.sentEmail',
- defaultMessage: 'Sent an email to {email} to reset your password'
- },
- attemptedReset: {
- id: 'audit_table.attemptedReset',
- defaultMessage: 'Attempted to reset password'
- },
- successfullReset: {
- id: 'audit_table.successfullReset',
- defaultMessage: 'Successfully reset password'
- },
- updateGlobalNotifications: {
- id: 'audit_table.updateGlobalNotifications',
- defaultMessage: 'Updated your global notification settings'
- },
- attemptedWebhookCreate: {
- id: 'audit_table.attemptedWebhookCreate',
- defaultMessage: 'Attempted to create a webhook'
- },
- succcessfullWebhookCreate: {
- id: 'audit_table.successfullWebhookCreate',
- defaultMessage: 'Successfully created a webhook'
- },
- failedWebhookCreate: {
- id: 'audit_table.failedWebhookCreate',
- defaultMessage: 'Failed to create a webhook - bad channel permissions'
- },
- attemptedWebhookDelete: {
- id: 'audit_table.attemptedWebhookDelete',
- defaultMessage: 'Attempted to delete a webhook'
- },
- successfullWebhookDelete: {
- id: 'audit_table.successfullWebhookDelete',
- defaultMessage: 'Successfully deleted a webhook'
- },
- failedWebhookDelete: {
- id: 'audit_table.failedWebhookDelete',
- defaultMessage: 'Failed to delete a webhook - inappropriate conditions'
- },
- logout: {
- id: 'audit_table.logout',
- defaultMessage: 'Logged out of your account'
- },
- verified: {
- id: 'audit_table.verified',
- defaultMessage: 'Sucessfully verified your email address'
- },
- revokedAll: {
- id: 'audit_table.revokedAll',
- defaultMessage: 'Revoked all current sessions for the team'
- },
- loginAttempt: {
- id: 'audit_table.loginAttempt',
- defaultMessage: ' (Login attempt)'
- },
- loginFailure: {
- id: 'audit_table.loginFailure',
- defaultMessage: ' (Login failure)'
- },
- attemptedLicenseAdd: {
- id: 'audit_table.attemptedLicenseAdd',
- defaultMessage: 'Attempted to add new license'
- },
- successfullLicenseAdd: {
- id: 'audit_table.successfullLicenseAdd',
- defaultMessage: 'Successfully added new license'
- },
- failedExpiredLicenseAdd: {
- id: 'audit_table.failedExpiredLicenseAdd',
- defaultMessage: 'Failed to add a new license as it has either expired or not yet been started'
- },
- failedInvalidLicenseAdd: {
- id: 'audit_table.failedInvalidLicenseAdd',
- defaultMessage: 'Failed to add an invalid license'
- },
- licenseRemoved: {
- id: 'audit_table.licenseRemoved',
- defaultMessage: 'Successfully removed a license'
- }
-});
-
-class AuditTable extends React.Component {
- render() {
- var accessList = [];
-
- const {formatMessage} = this.props.intl;
- for (var i = 0; i < this.props.audits.length; i++) {
- const audit = this.props.audits[i];
- const auditInfo = formatAuditInfo(audit, formatMessage);
-
- let uContent;
- if (this.props.showUserId) {
- uContent = <td>{auditInfo.userId}</td>;
- }
-
- let iContent;
- if (this.props.showIp) {
- iContent = <td>{auditInfo.ip}</td>;
- }
-
- let sContent;
- if (this.props.showSession) {
- sContent = <td>{auditInfo.sessionId}</td>;
- }
-
- let descStyle = {};
- if (auditInfo.desc.toLowerCase().indexOf('fail') !== -1) {
- descStyle.color = 'red';
- }
-
- accessList[i] = (
- <tr key={audit.id}>
- <td>{auditInfo.timestamp}</td>
- {uContent}
- <td style={descStyle}>{auditInfo.desc}</td>
- {iContent}
- {sContent}
- </tr>
- );
- }
-
- let userIdContent;
- if (this.props.showUserId) {
- userIdContent = (
- <th>
- <FormattedMessage
- id='audit_table.userId'
- defaultMessage='User ID'
- />
- </th>
- );
- }
-
- let ipContent;
- if (this.props.showIp) {
- ipContent = (
- <th>
- <FormattedMessage
- id='audit_table.ip'
- defaultMessage='IP Address'
- />
- </th>
- );
- }
-
- let sessionContent;
- if (this.props.showSession) {
- sessionContent = (
- <th>
- <FormattedMessage
- id='audit_table.session'
- defaultMessage='Session ID'
- />
- </th>
- );
- }
-
- return (
- <table className='table'>
- <thead>
- <tr>
- <th>
- <FormattedMessage
- id='audit_table.timestamp'
- defaultMessage='Timestamp'
- />
- </th>
- {userIdContent}
- <th>
- <FormattedMessage
- id='audit_table.action'
- defaultMessage='Action'
- />
- </th>
- {ipContent}
- {sessionContent}
- </tr>
- </thead>
- <tbody>
- {accessList}
- </tbody>
- </table>
- );
- }
-}
-
-AuditTable.propTypes = {
- intl: intlShape.isRequired,
- audits: React.PropTypes.array.isRequired,
- showUserId: React.PropTypes.bool,
- showIp: React.PropTypes.bool,
- showSession: React.PropTypes.bool
-};
-
-export default injectIntl(AuditTable);
-
-export function formatAuditInfo(audit, formatMessage) {
- const actionURL = audit.action.replace(/\/api\/v[1-9]/, '');
- let auditDesc = '';
-
- if (actionURL.indexOf('/channels') === 0) {
- const channelInfo = audit.extra_info.split(' ');
- const channelNameField = channelInfo[0].split('=');
-
- let channelURL = '';
- let channelObj;
- let channelName = '';
- if (channelNameField.indexOf('name') >= 0) {
- channelURL = channelNameField[channelNameField.indexOf('name') + 1];
- channelObj = ChannelStore.getByName(channelURL);
- if (channelObj) {
- channelName = channelObj.display_name;
- } else {
- channelName = channelURL;
- }
- }
-
- switch (actionURL) {
- case '/channels/create':
- auditDesc = formatMessage(holders.channelCreated, {channelName});
- break;
- case '/channels/create_direct':
- auditDesc = formatMessage(holders.establishedDM, {username: Utils.getDirectTeammate(channelObj.id).username});
- break;
- case '/channels/update':
- auditDesc = formatMessage(holders.nameUpdated, {channelName});
- break;
- case '/channels/update_desc': // support the old path
- case '/channels/update_header':
- auditDesc = formatMessage(holders.headerUpdated, {channelName});
- break;
- default: {
- let userIdField = [];
- let userId = '';
- let username = '';
-
- if (channelInfo[1]) {
- userIdField = channelInfo[1].split('=');
-
- if (userIdField.indexOf('user_id') >= 0) {
- userId = userIdField[userIdField.indexOf('user_id') + 1];
- username = UserStore.getProfile(userId).username;
- }
- }
-
- if (/\/channels\/[A-Za-z0-9]+\/delete/.test(actionURL)) {
- auditDesc = formatMessage(holders.channelDeleted, {url: channelURL});
- } else if (/\/channels\/[A-Za-z0-9]+\/add/.test(actionURL)) {
- auditDesc = formatMessage(holders.userAdded, {username, channelName});
- } else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(actionURL)) {
- auditDesc = formatMessage(holders.userRemoved, {username, channelName});
- }
-
- break;
- }
- }
- } else if (actionURL.indexOf('/oauth') === 0) {
- const oauthInfo = audit.extra_info.split(' ');
-
- switch (actionURL) {
- case '/oauth/register': {
- const clientIdField = oauthInfo[0].split('=');
-
- if (clientIdField[0] === 'client_id') {
- auditDesc = formatMessage(holders.attemptedRegisterApp, {id: clientIdField[1]});
- }
-
- break;
- }
- case '/oauth/allow':
- if (oauthInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedAllowOAuthAccess);
- } else if (oauthInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullOAuthAccess);
- } else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
- auditDesc = formatMessage(holders.failedOAuthAccess);
- }
-
- break;
- case '/oauth/access_token':
- if (oauthInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedOAuthToken);
- } else if (oauthInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullOAuthToken);
- } else {
- const oauthTokenFailure = oauthInfo[0].split('-');
-
- if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
- auditDesc = formatMessage(oauthTokenFailure, {token: oauthTokenFailure[1].trim()});
- }
- }
-
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/users') === 0) {
- const userInfo = audit.extra_info.split(' ');
-
- switch (actionURL) {
- case '/users/login':
- if (userInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedLogin);
- } else if (userInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullLogin);
- } else if (userInfo[0]) {
- auditDesc = formatMessage(holders.failedLogin);
- }
-
- break;
- case '/users/revoke_session':
- auditDesc = formatMessage(holders.sessionRevoked, {sessionId: userInfo[0].split('=')[1]});
- break;
- case '/users/newimage':
- auditDesc = formatMessage(holders.updatePicture);
- break;
- case '/users/update':
- auditDesc = formatMessage(holders.updateGeneral);
- break;
- case '/users/newpassword':
- if (userInfo[0] === 'attempted') {
- auditDesc = formatMessage(holders.attemptedPassword);
- } else if (userInfo[0] === 'completed') {
- auditDesc = formatMessage(holders.successfullPassword);
- } else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
- auditDesc = formatMessage(holders.failedPassword);
- }
-
- break;
- case '/users/update_roles': {
- const userRoles = userInfo[0].split('=')[1];
-
- auditDesc = formatMessage(holders.updatedRol);
- if (userRoles.trim()) {
- auditDesc += userRoles;
- } else {
- auditDesc += formatMessage(holders.member);
- }
-
- break;
- }
- case '/users/update_active': {
- const updateType = userInfo[0].split('=')[0];
- const updateField = userInfo[0].split('=')[1];
-
- /* Either describes account activation/deactivation or a revoked session as part of an account deactivation */
- if (updateType === 'active') {
- if (updateField === 'true') {
- auditDesc = formatMessage(holders.accountActive);
- } else if (updateField === 'false') {
- auditDesc = formatMessage(holders.accountInactive);
- }
-
- const actingUserInfo = userInfo[1].split('=');
- if (actingUserInfo[0] === 'session_user') {
- const actingUser = UserStore.getProfile(actingUserInfo[1]);
- const user = UserStore.getCurrentUser();
- if (user && actingUser && (Utils.isAdmin(user.roles) || Utils.isSystemAdmin(user.roles))) {
- auditDesc += formatMessage(holders.by, {username: actingUser.username});
- } else if (user && actingUser) {
- auditDesc += formatMessage(holders.byAdmin);
- }
- }
- } else if (updateType === 'session_id') {
- auditDesc = formatMessage(holders.sessionRevoked, {sessionId: updateField});
- }
-
- break;
- }
- case '/users/send_password_reset':
- auditDesc = formatMessage(holders.sentEmail, {email: userInfo[0].split('=')[1]});
- break;
- case '/users/reset_password':
- if (userInfo[0] === 'attempt') {
- auditDesc = formatMessage(holders.attemptedReset);
- } else if (userInfo[0] === 'success') {
- auditDesc = formatMessage(holders.successfullReset);
- }
-
- break;
- case '/users/update_notify':
- auditDesc = formatMessage(holders.updateGlobalNotifications);
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/hooks') === 0) {
- const webhookInfo = audit.extra_info;
-
- switch (actionURL) {
- case '/hooks/incoming/create':
- if (webhookInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedWebhookCreate);
- } else if (webhookInfo === 'success') {
- auditDesc = formatMessage(holders.succcessfullWebhookCreate);
- } else if (webhookInfo === 'fail - bad channel permissions') {
- auditDesc = formatMessage(holders.failedWebhookCreate);
- }
-
- break;
- case '/hooks/incoming/delete':
- if (webhookInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedWebhookDelete);
- } else if (webhookInfo === 'success') {
- auditDesc = formatMessage(holders.successfullWebhookDelete);
- } else if (webhookInfo === 'fail - inappropriate conditions') {
- auditDesc = formatMessage(holders.failedWebhookDelete);
- }
-
- break;
- default:
- break;
- }
- } else if (actionURL.indexOf('/license') === 0) {
- const licenseInfo = audit.extra_info;
-
- switch (actionURL) {
- case '/license/add':
- if (licenseInfo === 'attempt') {
- auditDesc = formatMessage(holders.attemptedLicenseAdd);
- } else if (licenseInfo === 'success') {
- auditDesc = formatMessage(holders.successfullLicenseAdd);
- } else if (licenseInfo === 'failed - expired or non-started license') {
- auditDesc = formatMessage(holders.failedExpiredLicenseAdd);
- } else if (licenseInfo === 'failed - invalid license') {
- auditDesc = formatMessage(holders.failedInvalidLicenseAdd);
- }
-
- break;
- case '/license/remove':
- auditDesc = formatMessage(holders.licenseRemoved);
- break;
- default:
- break;
- }
- } else {
- switch (actionURL) {
- case '/logout':
- auditDesc = formatMessage(holders.logout);
- break;
- case '/verify_email':
- auditDesc = formatMessage(holders.verified);
- break;
- default:
- break;
- }
- }
-
- /* If all else fails... */
- if (!auditDesc) {
- /* Currently not called anywhere */
- if (audit.extra_info.indexOf('revoked_all=') >= 0) {
- auditDesc = formatMessage(holders.revokedAll);
- } else {
- let actionDesc = '';
- if (actionURL && actionURL.lastIndexOf('/') !== -1) {
- actionDesc = actionURL.substring(actionURL.lastIndexOf('/') + 1).replace('_', ' ');
- actionDesc = Utils.toTitleCase(actionDesc);
- }
-
- let extraInfoDesc = '';
- if (audit.extra_info) {
- extraInfoDesc = audit.extra_info;
-
- if (extraInfoDesc.indexOf('=') !== -1) {
- extraInfoDesc = extraInfoDesc.substring(extraInfoDesc.indexOf('=') + 1);
- }
- }
- auditDesc = actionDesc + ' ' + extraInfoDesc;
- }
- }
-
- const date = new Date(audit.create_at);
- const auditInfo = {};
- auditInfo.timestamp = (
- <div>
- <FormattedDate
- value={date}
- day='2-digit'
- month='short'
- year='numeric'
- />
- {' - '}
- <FormattedTime
- value={date}
- hour='2-digit'
- minute='2-digit'
- />
- </div>
- );
- auditInfo.userId = audit.user_id;
- auditInfo.desc = auditDesc;
- auditInfo.ip = audit.ip_address;
- auditInfo.sessionId = audit.session_id;
-
- return auditInfo;
-}
diff --git a/web/react/components/authorize.jsx b/web/react/components/authorize.jsx
deleted file mode 100644
index 4b1cebcf4..000000000
--- a/web/react/components/authorize.jsx
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class Authorize extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleAllow = this.handleAllow.bind(this);
- this.handleDeny = this.handleDeny.bind(this);
-
- this.state = {};
- }
- handleAllow() {
- const responseType = this.props.responseType;
- const clientId = this.props.clientId;
- const redirectUri = this.props.redirectUri;
- const state = this.props.state;
- const scope = this.props.scope;
-
- Client.allowOAuth2(responseType, clientId, redirectUri, state, scope,
- (data) => {
- if (data.redirect) {
- window.location.replace(data.redirect);
- }
- },
- () => {
- //Do nothing on error
- }
- );
- }
- handleDeny() {
- window.location.replace(this.props.redirectUri + '?error=access_denied');
- }
- render() {
- return (
- <div className='container-fluid'>
- <div className='prompt'>
- <div className='prompt__heading'>
- <div className='prompt__app-icon'>
- <img
- src='/static/images/icon50x50.png'
- width='50'
- height='50'
- alt=''
- />
- </div>
- <div className='text'>
- <FormattedMessage
- id='authorize.title'
- defaultMessage='An application would like to connect to your {teamName} account'
- values={{
- teamName: this.props.teamName
- }}
- />
- </div>
- </div>
- <p>
- <FormattedHTMLMessage
- id='authorize.app'
- defaultMessage='The app <strong>{appName}</strong> would like the ability to access and modify your basic information.'
- values={{
- appName: this.props.appName
- }}
- />
- </p>
- <h2 className='prompt__allow'>
- <FormattedHTMLMessage
- id='authorize.access'
- defaultMessage='Allow <strong>{appName}</strong> access?'
- values={{
- appName: this.props.appName
- }}
- />
- </h2>
- <div className='prompt__buttons'>
- <button
- type='submit'
- className='btn authorize-btn'
- onClick={this.handleDeny}
- >
- <FormattedMessage
- id='authorize.deny'
- defaultMessage='Deny'
- />
- </button>
- <button
- type='submit'
- className='btn btn-primary authorize-btn'
- onClick={this.handleAllow}
- >
- <FormattedMessage
- id='authorize.allow'
- defaultMessage='Allow'
- />
- </button>
- </div>
- </div>
- </div>
- );
- }
-}
-
-Authorize.propTypes = {
- appName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- responseType: React.PropTypes.string,
- clientId: React.PropTypes.string,
- redirectUri: React.PropTypes.string,
- state: React.PropTypes.string,
- scope: React.PropTypes.string
-};
diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx
deleted file mode 100644
index 9a5f7a5d1..000000000
--- a/web/react/components/center_panel.jsx
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TutorialIntroScreens from './tutorial/tutorial_intro_screens.jsx';
-import CreatePost from './create_post.jsx';
-import PostsViewContainer from './posts_view_container.jsx';
-import PostFocusView from './post_focus_view.jsx';
-import ChannelHeader from './channel_header.jsx';
-import Navbar from './navbar.jsx';
-import FileUploadOverlay from './file_upload_overlay.jsx';
-
-import PreferenceStore from '../stores/preference_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import Constants from '../utils/constants.jsx';
-const TutorialSteps = Constants.TutorialSteps;
-const Preferences = Constants.Preferences;
-
-export default class CenterPanel extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.validState = this.validState.bind(this);
- this.onStoresChange = this.onStoresChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- getStateFromStores() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
- return {
- showTutorialScreens: tutorialStep <= TutorialSteps.INTRO_SCREENS,
- showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS,
- user: UserStore.getCurrentUser(),
- channel: ChannelStore.getCurrent(),
- profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))
- };
- }
- validState() {
- return this.state.user && this.state.channel && this.state.profiles;
- }
- onStoresChange() {
- this.setState(this.getStateFromStores());
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onStoresChange);
- ChannelStore.addChangeListener(this.onStoresChange);
- UserStore.addChangeListener(this.onStoresChange);
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onStoresChange);
- ChannelStore.removeChangeListener(this.onStoresChange);
- UserStore.removeChangeListener(this.onStoresChange);
- }
- render() {
- if (!this.validState()) {
- return null;
- }
- const channel = this.state.channel;
- var handleClick = null;
- let postsContainer;
- let createPost;
- if (this.state.showTutorialScreens) {
- postsContainer = <TutorialIntroScreens/>;
- createPost = null;
- } else if (this.state.showPostFocus) {
- postsContainer = <PostFocusView profiles={this.state.profiles}/>;
-
- handleClick = function clickHandler(e) {
- e.preventDefault();
- Utils.switchChannel(channel);
- };
-
- createPost = (
- <div
- id='archive-link-home'
- onClick={handleClick}
- >
- <a href=''>
- <FormattedMessage
- id='center_panel.recent'
- defaultMessage='Click here to jump to recent messages. '
- />
- <i className='fa fa-arrow-down'></i>
- </a>
- </div>
- );
- } else {
- postsContainer = <PostsViewContainer profiles={this.state.profiles}/>;
- createPost = (
- <div
- className='post-create__container'
- id='post-create'
- >
- <CreatePost/>
- </div>
- );
- }
-
- return (
- <div className='inner-wrap channel__wrap'>
- <div className='row header'>
- <div id='navbar'>
- <Navbar/>
- </div>
- </div>
- <div className='row main'>
- <FileUploadOverlay
- id='file_upload_overlay'
- overlayType='center'
- />
- <div
- id='app-content'
- className='app__content'
- >
- <div id='channel-header'>
- <ChannelHeader
- user={this.state.user}
- />
- </div>
- {postsContainer}
- {createPost}
- </div>
- </div>
- </div>
- );
- }
-}
-
-CenterPanel.defaultProps = {
-};
-
-CenterPanel.propTypes = {
-};
diff --git a/web/react/components/change_url_modal.jsx b/web/react/components/change_url_modal.jsx
deleted file mode 100644
index 49d1b86b4..000000000
--- a/web/react/components/change_url_modal.jsx
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChangeUrlModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onURLChanged = this.onURLChanged.bind(this);
- this.doSubmit = this.doSubmit.bind(this);
- this.doCancel = this.doCancel.bind(this);
-
- this.state = {
- currentURL: props.currentURL,
- urlError: '',
- userEdit: false
- };
- }
- componentWillReceiveProps(nextProps) {
- // This check prevents the url being deleted when we re-render
- // because of user status check
- if (!this.state.userEdit) {
- this.setState({
- currentURL: nextProps.currentURL
- });
- }
- }
- componentDidUpdate(prevProps) {
- if (this.props.show === true && prevProps.show === false) {
- ReactDOM.findDOMNode(this.refs.urlinput).select();
- }
- }
- onURLChanged(e) {
- const url = e.target.value.trim();
- this.setState({currentURL: url.replace(/[^A-Za-z0-9-_]/g, '').toLowerCase(), userEdit: true});
- }
- getURLError(url) {
- let error = []; //eslint-disable-line prefer-const
- if (url.length < 2) {
- error.push(
- <span key='error1'>
- <FormattedMessage
- id='change_url.longer'
- defaultMessage='Must be longer than two characters'
- />
- <br/>
- </span>
- );
- }
- if (url.charAt(0) === '-' || url.charAt(0) === '_') {
- error.push(
- <span key='error2'>
- <FormattedMessage
- id='change_url.startWithLetter'
- defaultMessage='Must start with a letter or number'
- />
- <br/>
- </span>
- );
- }
- if (url.length > 1 && (url.charAt(url.length - 1) === '-' || url.charAt(url.length - 1) === '_')) {
- error.push(
- <span key='error3'>
- <FormattedMessage
- id='change_url.endWithLetter'
- defaultMessage='Must end with a letter or number'
- />
- <br/>
- </span>);
- }
- if (url.indexOf('__') > -1) {
- error.push(
- <span key='error4'>
- <FormattedMessage
- id='change_url.noUnderscore'
- defaultMessage='Can not contain two underscores in a row.'
- />
- <br/>
- </span>);
- }
-
- // In case of error we don't detect
- if (error.length === 0) {
- error.push(
- <span key='errorlast'>
- <FormattedMessage
- id='change_url.invalidUrl'
- defaultMessage='Invalid URL'
- />
- <br/>
- </span>);
- }
- return error;
- }
- doSubmit(e) {
- e.preventDefault();
-
- const url = ReactDOM.findDOMNode(this.refs.urlinput).value;
- const cleanedURL = Utils.cleanUpUrlable(url);
- if (cleanedURL !== url || url.length < 2 || url.indexOf('__') > -1) {
- this.setState({urlError: this.getURLError(url)});
- return;
- }
- this.setState({urlError: '', userEdit: false});
- this.props.onModalSubmit(url);
- }
- doCancel() {
- this.setState({urlError: '', userEdit: false});
- this.props.onModalDismissed();
- }
- render() {
- let urlClass = 'input-group input-group--limit';
- let urlError = null;
- let serverError = null;
-
- if (this.state.urlError) {
- urlClass += ' has-error';
- urlError = (<p className='input__help error'>{this.state.urlError}</p>);
- }
-
- if (this.props.serverError) {
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
- }
-
- const fullTeamUrl = Utils.getTeamURLFromAddressBar();
- const teamURL = Utils.getShortenedTeamURL();
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.doCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>{this.props.title}</Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div className='modal-intro'>{this.props.description}</div>
- <div className='form-group'>
- <label className='col-sm-2 form__label control-label'>{this.props.urlLabel}</label>
- <div className='col-sm-10'>
- <div className={urlClass}>
- <span
- data-toggle='tooltip'
- title={fullTeamUrl}
- className='input-group-addon'
- >
- {teamURL}
- </span>
- <input
- type='text'
- ref='urlinput'
- className='form-control'
- maxLength='22'
- onChange={this.onURLChanged}
- value={this.state.currentURL}
- autoFocus={true}
- tabIndex='1'
- />
- </div>
- {urlError}
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.doCancel}
- >
- <FormattedMessage
- id='change_url.close'
- defaultMessage='Close'
- />
- </button>
- <button
- onClick={this.doSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='2'
- >
- {this.props.submitButtonText}
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-ChangeUrlModal.defaultProps = {
- show: false,
- title: 'Change URL',
- desciption: '',
- urlLabel: 'URL',
- submitButtonText: 'Save',
- currentURL: '',
- serverError: ''
-};
-
-ChangeUrlModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- title: React.PropTypes.string,
- description: React.PropTypes.string,
- urlLabel: React.PropTypes.string,
- submitButtonText: React.PropTypes.string,
- currentURL: React.PropTypes.string,
- serverError: React.PropTypes.string,
- onModalSubmit: React.PropTypes.func.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
deleted file mode 100644
index 882c575f0..000000000
--- a/web/react/components/channel_header.jsx
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NavbarSearchBox from './search_bar.jsx';
-import MessageWrapper from './message_wrapper.jsx';
-import PopoverListMembers from './popover_list_members.jsx';
-import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
-import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
-import ChannelInfoModal from './channel_info_modal.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-import ChannelMembersModal from './channel_members_modal.jsx';
-import ChannelNotificationsModal from './channel_notifications_modal.jsx';
-import DeleteChannelModal from './delete_channel_modal.jsx';
-import RenameChannelModal from './rename_channel_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-const Popover = ReactBootstrap.Popover;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-const Tooltip = ReactBootstrap.Tooltip;
-
-export default class ChannelHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleLeave = this.handleLeave.bind(this);
- this.searchMentions = this.searchMentions.bind(this);
- this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
- this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
-
- const state = this.getStateFromStores();
- state.showEditChannelPurposeModal = false;
- state.showMembersModal = false;
- state.showRenameChannelModal = false;
- this.state = state;
- }
- getStateFromStores() {
- const extraInfo = ChannelStore.getCurrentExtraInfo();
-
- return {
- channel: ChannelStore.getCurrent(),
- memberChannel: ChannelStore.getCurrentMember(),
- users: extraInfo.members,
- userCount: extraInfo.member_count,
- searchVisible: SearchStore.getSearchResults() !== null,
- currentUser: UserStore.getCurrentUser()
- };
- }
- validState() {
- if (!this.state.channel ||
- !this.state.memberChannel ||
- !this.state.users ||
- !this.state.userCount ||
- !this.state.currentUser) {
- return false;
- }
- return true;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onListenerChange);
- ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
- SearchStore.addSearchChangeListener(this.onListenerChange);
- PreferenceStore.addChangeListener(this.onListenerChange);
- UserStore.addChangeListener(this.onListenerChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onListenerChange);
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- SearchStore.removeSearchChangeListener(this.onListenerChange);
- PreferenceStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
- }
- handleLeave() {
- Client.leaveChannel(this.state.channel.id,
- () => {
- AppDispatcher.handleViewAction({
- type: ActionTypes.LEAVE_CHANNEL,
- id: this.state.channel.id
- });
-
- const townsquare = ChannelStore.getByName('town-square');
- Utils.switchChannel(townsquare);
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleLeave');
- }
- );
- }
- searchMentions(e) {
- e.preventDefault();
-
- const user = this.state.currentUser;
-
- let terms = '';
- if (user.notify_props && user.notify_props.mention_keys) {
- const termKeys = UserStore.getMentionKeys(user.id);
-
- if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
- termKeys.splice(termKeys.indexOf('@all'), 1);
- }
-
- if (user.notify_props.channel === 'true' && termKeys.indexOf('@channel') !== -1) {
- termKeys.splice(termKeys.indexOf('@channel'), 1);
- }
- terms = termKeys.join(' ');
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: terms,
- do_search: true,
- is_mention_search: true
- });
- }
- showRenameChannelModal(e) {
- e.preventDefault();
-
- this.setState({
- showRenameChannelModal: true
- });
- }
- hideRenameChannelModal() {
- this.setState({
- showRenameChannelModal: false
- });
- }
- render() {
- if (!this.validState()) {
- return null;
- }
-
- const channel = this.state.channel;
- const recentMentionsTooltip = (
- <Tooltip id='recentMentionsTooltip'>
- <FormattedMessage
- id='channel_header.recentMentions'
- defaultMessage='Recent Mentions'
- />
- </Tooltip>
- );
- const popoverContent = (
- <Popover
- id='hader-popover'
- bStyle='info'
- bSize='large'
- placement='bottom'
- className='description'
- onMouseOver={() => this.refs.headerOverlay.show()}
- onMouseOut={() => this.refs.headerOverlay.hide()}
- >
- <MessageWrapper
- message={channel.header}
- />
- </Popover>
- );
- let channelTitle = channel.display_name;
- const currentId = this.state.currentUser.id;
- const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || Utils.isAdmin(this.state.currentUser.roles);
- const isDirect = (this.state.channel.type === 'D');
-
- if (isDirect) {
- if (this.state.users.length > 1) {
- let contact;
- if (this.state.users[0].id === currentId) {
- contact = this.state.users[1];
- } else {
- contact = this.state.users[0];
- }
- channelTitle = Utils.displayUsername(contact.id);
- }
- }
-
- let channelTerm = (
- <FormattedMessage
- id='channel_header.channel'
- defaultMessage='Channel'
- />
- );
- if (channel.type === Constants.PRIVATE_CHANNEL) {
- channelTerm = (
- <FormattedMessage
- id='channel_header.group'
- defaultMessage='Group'
- />
- );
- }
-
- let popoverListMembers;
- if (!isDirect) {
- popoverListMembers = (
- <PopoverListMembers
- members={this.state.users}
- memberCount={this.state.userCount}
- channelId={channel.id}
- />
- );
- }
-
- const dropdownContents = [];
- if (isDirect) {
- dropdownContents.push(
- <li
- key='edit_header_direct'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={EditChannelHeaderModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.channelHeader'
- defaultMessage='Set Channel Header...'
- />
- </ToggleModalButton>
- </li>
- );
- } else {
- dropdownContents.push(
- <li
- key='view_info'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInfoModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.viewInfo'
- defaultMessage='View Info'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='add_members'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInviteModal}
- dialogProps={{channel, currentUser: this.state.currentUser}}
- >
- <FormattedMessage
- id='chanel_header.addMembers'
- defaultMessage='Add Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (isAdmin) {
- dropdownContents.push(
- <li
- key='manage_members'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showMembersModal: true})}
- >
- <FormattedMessage
- id='channel_header.manageMembers'
- defaultMessage='Manage Members'
- />
- </a>
- </li>
- );
- }
- }
-
- dropdownContents.push(
- <li
- key='set_channel_header'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={EditChannelHeaderModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.setHeader'
- defaultMessage='Set {term} Header...'
- values={{
- term: (channelTerm)
- }}
- />
- </ToggleModalButton>
- </li>
- );
- dropdownContents.push(
- <li
- key='set_channel_purpose'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showEditChannelPurposeModal: true})}
- >
- <FormattedMessage
- id='channel_header.setPurpose'
- defaultMessage='Set {term} Purpose...'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
- dropdownContents.push(
- <li
- key='notification_preferences'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelNotificationsModal}
- dialogProps={{
- channel,
- channelMember: this.state.memberChannel,
- currentUser: this.state.currentUser
- }}
- >
- <FormattedMessage
- id='channel_header.notificationPreferences'
- defaultMessage='Notification Preferences'
- />
- </ToggleModalButton>
- </li>
- );
-
- if (isAdmin) {
- dropdownContents.push(
- <li
- key='rename_channel'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={this.showRenameChannelModal}
- >
- <FormattedMessage
- id='channel_header.rename'
- defaultMessage='Rename {term}...'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='delete_channel'
- role='presentation'
- >
- <ToggleModalButton
- role='menuitem'
- dialogType={DeleteChannelModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='channel_header.delete'
- defaultMessage='Delete {term}...'
- values={{
- term: (channelTerm)
- }}
- />
- </ToggleModalButton>
- </li>
- );
- }
- }
-
- if (!ChannelStore.isDefault(channel)) {
- dropdownContents.push(
- <li
- key='leave_channel'
- role='presentation'
- >
- <a
- role='menuitem'
- href='#'
- onClick={this.handleLeave}
- >
- <FormattedMessage
- id='channel_header.leave'
- defaultMessage='Leave {term}'
- values={{
- term: (channelTerm)
- }}
- />
- </a>
- </li>
- );
- }
- }
-
- return (
- <div>
- <table className='channel-header alt'>
- <tbody>
- <tr>
- <th>
- <div className='channel-header__info'>
- <div className='dropdown'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- id='channel_header_dropdown'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <strong className='heading'>{channelTitle} </strong>
- <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'/>
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- aria-labelledby='channel_header_dropdown'
- >
- {dropdownContents}
- </ul>
- </div>
- <OverlayTrigger
- trigger={'click'}
- placement='bottom'
- overlay={popoverContent}
- ref='headerOverlay'
- >
- <div
- onClick={TextFormatting.handleClick}
- className='description'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.header, {singleline: true, mentionHighlight: false})}}
- />
- </OverlayTrigger>
- </div>
- </th>
- <th>
- {popoverListMembers}
- </th>
- <th className='search-bar__container'><NavbarSearchBox/></th>
- <th>
- <div className='dropdown channel-header__links'>
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='bottom'
- overlay={recentMentionsTooltip}
- >
- <a
- href='#'
- type='button'
- onClick={this.searchMentions}
- >
- {'@'}
- </a>
- </OverlayTrigger>
- </div>
- </th>
- </tr>
- </tbody>
- </table>
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
- <ChannelMembersModal
- show={this.state.showMembersModal}
- onModalDismissed={() => this.setState({showMembersModal: false})}
- channel={channel}
- />
- <RenameChannelModal
- show={this.state.showRenameChannelModal}
- onHide={this.hideRenameChannelModal}
- channel={channel}
- />
- </div>
- );
- }
-}
-
-ChannelHeader.propTypes = {
-};
diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx
deleted file mode 100644
index 83f5aba65..000000000
--- a/web/react/components/channel_info_modal.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- notFound: {
- id: 'channel_info.notFound',
- defaultMessage: 'No Channel Found'
- }
-});
-
-class ChannelInfoModal extends React.Component {
- render() {
- const {formatMessage} = this.props.intl;
- let channel = this.props.channel;
- if (!channel) {
- channel = {
- display_name: formatMessage(holders.notFound),
- name: formatMessage(holders.notFound),
- purpose: formatMessage(holders.notFound),
- id: formatMessage(holders.notFound)
- };
- }
-
- const channelURL = Utils.getShortenedTeamURL() + channel.name;
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButtton={true}>
- {channel.display_name}
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.name'
- defaultMessage='Channel Name:'
- />
- </div>
- <div className='col-sm-9'>{channel.display_name}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.url'
- defaultMessage='Channel URL:'
- />
- </div>
- <div className='col-sm-9'>{channelURL}</div>
- </div>
- <div className='row form-group'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.id'
- defaultMessage='Channel ID:'
- />
- </div>
- <div className='col-sm-9'>{channel.id}</div>
- </div>
- <div className='row'>
- <div className='col-sm-3 info__label'>
- <FormattedMessage
- id='channel_info.purpose'
- defaultMessage='Channel Purpose:'
- />
- </div>
- <div className='col-sm-9'>{channel.purpose}</div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='channel_info.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ChannelInfoModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(ChannelInfoModal); \ No newline at end of file
diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx
deleted file mode 100644
index 4157812a9..000000000
--- a/web/react/components/channel_invite_modal.jsx
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class ChannelInviteModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleInvite = this.handleInvite.bind(this);
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.createInviteButton = this.createInviteButton.bind(this);
-
- this.state = this.getStateFromStores();
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!this.props.show && !nextProps.show) {
- return false;
- }
-
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
- getStateFromStores() {
- const users = UserStore.getActiveOnlyProfiles();
-
- if ($.isEmptyObject(users)) {
- return {
- loading: true
- };
- }
-
- // make sure we have all members of this channel before rendering
- const extraInfo = ChannelStore.getCurrentExtraInfo();
- if (extraInfo.member_count !== extraInfo.members.length) {
- AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
-
- return {
- loading: true
- };
- }
-
- const currentUser = UserStore.getCurrentUser();
- if (!currentUser) {
- return {
- loading: true
- };
- }
-
- const currentMember = ChannelStore.getCurrentMember();
- if (!currentMember) {
- return {
- loading: true
- };
- }
-
- const memberIds = extraInfo.members.map((user) => user.id);
-
- var nonmembers = [];
- for (var id in users) {
- if (memberIds.indexOf(id) === -1) {
- nonmembers.push(users[id]);
- }
- }
-
- nonmembers.sort((a, b) => {
- return a.username.localeCompare(b.username);
- });
-
- return {
- nonmembers,
- loading: false,
- currentUser,
- currentMember
- };
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.addChangeListener(this.onListenerChange);
- UserStore.addChangeListener(this.onListenerChange);
- this.onListenerChange();
- } else if (this.props.show && !nextProps.show) {
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- }
- componentWillUnmount() {
- ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
- ChannelStore.removeChangeListener(this.onListenerChange);
- UserStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(this.state, newState)) {
- this.setState(newState);
- }
- }
- handleInvite(user) {
- const data = {
- user_id: user.id
- };
-
- Client.addChannelMember(
- this.props.channel.id,
- data,
- () => {
- this.setState({inviteError: null});
- AsyncClient.getChannelExtraInfo();
- },
- (err) => {
- this.setState({inviteError: err.message});
- }
- );
- }
- createInviteButton({user}) {
- return (
- <a
- onClick={this.handleInvite.bind(this, user)}
- className='btn btn-sm btn-primary'
- >
- <i className='glyphicon glyphicon-envelope'/>
- <FormattedMessage
- id='channel_invite.add'
- defaultMessage=' Add'
- />
- </a>
- );
- }
- render() {
- var inviteError = null;
- if (this.state.inviteError) {
- inviteError = (<label className='has-error control-label'>{this.state.inviteError}</label>);
- }
-
- var content;
- if (this.state.loading) {
- content = (<LoadingScreen/>);
- } else {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
- content = (
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.nonmembers}
- actions={[this.createInviteButton]}
- />
- );
- }
-
- return (
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_invite.addNewMembers'
- defaultMessage='Add New Members to '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {inviteError}
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='channel_invite.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ChannelInviteModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_members_modal.jsx b/web/react/components/channel_members_modal.jsx
deleted file mode 100644
index dc8e3baaf..000000000
--- a/web/react/components/channel_members_modal.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import LoadingScreen from './loading_screen.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class ChannelMembersModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onChange = this.onChange.bind(this);
- this.handleRemove = this.handleRemove.bind(this);
-
- this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this);
-
- // the rest of the state gets populated when the modal is shown
- this.state = {
- showInviteModal: false
- };
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
- getStateFromStores() {
- const extraInfo = ChannelStore.getCurrentExtraInfo();
- const profiles = UserStore.getActiveOnlyProfiles();
-
- if (extraInfo.member_count !== extraInfo.members.length) {
- AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
-
- return {
- loading: true
- };
- }
-
- const memberList = extraInfo.members.map((member) => {
- return profiles[member.id];
- });
-
- function compareByUsername(a, b) {
- if (a.username < b.username) {
- return -1;
- } else if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- }
-
- memberList.sort(compareByUsername);
-
- return {
- memberList,
- loading: false
- };
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- ChannelStore.addExtraInfoChangeListener(this.onChange);
- ChannelStore.addChangeListener(this.onChange);
-
- this.onChange();
- } else if (this.props.show && !nextProps.show) {
- ChannelStore.removeExtraInfoChangeListener(this.onChange);
- ChannelStore.removeChangeListener(this.onChange);
- }
- }
- onChange() {
- const newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(this.state, newState)) {
- this.setState(newState);
- }
- }
- handleRemove(user) {
- const userId = user.id;
-
- const data = {};
- data.user_id = userId;
-
- Client.removeChannelMember(
- ChannelStore.getCurrentId(),
- data,
- () => {
- const memberList = this.state.memberList.slice();
- for (let i = 0; i < memberList.length; i++) {
- if (userId === memberList[i].id) {
- memberList.splice(i, 1);
- break;
- }
- }
-
- this.setState({memberList});
- AsyncClient.getChannelExtraInfo();
- },
- (err) => {
- this.setState({inviteError: err.message});
- }
- );
- }
- createRemoveMemberButton({user}) {
- if (user.id === UserStore.getCurrentId()) {
- return null;
- }
-
- return (
- <button
- type='button'
- className='btn btn-primary btn-message'
- onClick={this.handleRemove.bind(this, user)}
- >
- <FormattedMessage
- id='channel_members_modal.remove'
- defaultMessage='Remove'
- />
- </button>
- );
- }
- render() {
- let content;
- if (this.state.loading) {
- content = (<LoadingScreen/>);
- } else {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- content = (
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.memberList}
- actions={[this.createRemoveMemberButton]}
- />
- );
- }
-
- return (
- <div>
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onModalDismissed}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <span className='name'>{this.props.channel.display_name}</span>
- <FormattedMessage
- id='channel_memebers_modal.members'
- defaultMessage=' Members'
- />
- </Modal.Title>
- <a
- className='btn btn-md btn-primary'
- href='#'
- onClick={() => {
- this.setState({showInviteModal: true});
- this.props.onModalDismissed();
- }}
- >
- <FormattedMessage
- id='channel_members_modal.addNew'
- defaultMessage=' Add New Members'
- />
- </a>
- </Modal.Header>
- <Modal.Body
- ref='modalBody'
- >
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onModalDismissed}
- >
- <FormattedMessage
- id='channel_members_modal.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- <ChannelInviteModal
- show={this.state.showInviteModal}
- onHide={() => this.setState({showInviteModal: false})}
- channel={this.props.channel}
- />
- </div>
- );
- }
-}
-
-ChannelMembersModal.defaultProps = {
- show: false
-};
-
-ChannelMembersModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_notifications_modal.jsx b/web/react/components/channel_notifications_modal.jsx
deleted file mode 100644
index acefaf024..000000000
--- a/web/react/components/channel_notifications_modal.jsx
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-var Modal = ReactBootstrap.Modal;
-import SettingItemMin from './setting_item_min.jsx';
-import SettingItemMax from './setting_item_max.jsx';
-
-import * as Client from '../utils/client.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChannelNotificationsModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
-
- this.handleSubmitNotifyLevel = this.handleSubmitNotifyLevel.bind(this);
- this.handleUpdateNotifyLevel = this.handleUpdateNotifyLevel.bind(this);
- this.createNotifyLevelSection = this.createNotifyLevelSection.bind(this);
-
- this.handleSubmitMarkUnreadLevel = this.handleSubmitMarkUnreadLevel.bind(this);
- this.handleUpdateMarkUnreadLevel = this.handleUpdateMarkUnreadLevel.bind(this);
- this.createMarkUnreadLevelSection = this.createMarkUnreadLevelSection.bind(this);
-
- this.state = {
- activeSection: '',
- notifyLevel: '',
- unreadLevel: ''
- };
- }
- updateSection(section) {
- this.setState({activeSection: section});
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.show && nextProps.show) {
- this.setState({
- notifyLevel: nextProps.channelMember.notify_props.desktop,
- unreadLevel: nextProps.channelMember.notify_props.mark_unread
- });
- }
- }
- handleSubmitNotifyLevel() {
- var channelId = this.props.channel.id;
- var notifyLevel = this.state.notifyLevel;
-
- if (this.props.channelMember.notify_props.desktop === notifyLevel) {
- this.updateSection('');
- return;
- }
-
- var data = {};
- data.channel_id = channelId;
- data.user_id = this.props.currentUser.id;
- data.desktop = notifyLevel;
-
- //TODO: This should be moved to event_helpers
- Client.updateNotifyProps(data,
- () => {
- // YUCK
- var member = ChannelStore.getMember(channelId);
- member.notify_props.desktop = notifyLevel;
- ChannelStore.setChannelMember(member);
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleUpdateNotifyLevel(notifyLevel) {
- this.setState({notifyLevel});
- }
- createNotifyLevelSection(serverError) {
- // Get glabal user setting for notifications
- const globalNotifyLevel = this.props.currentUser.notify_props.desktop;
- let globalNotifyLevelName;
- if (globalNotifyLevel === 'all') {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.allActivity'
- defaultMessage='For all activity'
- />
- );
- } else if (globalNotifyLevel === 'mention') {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.onlyMentions'
- defaultMessage='Only for mentions'
- />
- );
- } else {
- globalNotifyLevelName = (
- <FormattedMessage
- id='channel_notifications.never'
- defaultMessage='Never'
- />
- );
- }
-
- const sendDesktop = (
- <FormattedMessage
- id='channel_notifications.sendDesktop'
- defaultMessage='Send desktop notifications'
- />
- );
-
- const notificationLevel = this.state.notifyLevel;
-
- if (this.state.activeSection === 'desktop') {
- const notifyActive = [false, false, false, false];
- if (notificationLevel === 'default') {
- notifyActive[0] = true;
- } else if (notificationLevel === 'all') {
- notifyActive[1] = true;
- } else if (notificationLevel === 'mention') {
- notifyActive[2] = true;
- } else {
- notifyActive[3] = true;
- }
-
- var inputs = [];
-
- inputs.push(
- <div key='channel-notification-level-radio'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[0]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'default')}
- />
- <FormattedMessage
- id='channel_notifications.globalDefault'
- defaultMessage='Global default ({notifyLevel})'
- values={{
- notifyLevel: (globalNotifyLevelName)
- }}
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[1]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'all')}
- />
- <FormattedMessage id='channel_notifications.allActivity'/>
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[2]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'mention')}
- />
- <FormattedMessage id='channel_notifications.onlyMentions'/>
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[3]}
- onChange={this.handleUpdateNotifyLevel.bind(this, 'none')}
- />
- <FormattedMessage id='channel_notifications.never'/>
- </label>
- </div>
- </div>
- );
-
- const handleUpdateSection = function updateSection(e) {
- this.updateSection('');
- this.onListenerChange();
- e.preventDefault();
- }.bind(this);
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='channel_notifications.override'
- defaultMessage='Selecting an option other than "Default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'
- />
- </span>
- );
-
- return (
- <SettingItemMax
- title={sendDesktop}
- inputs={inputs}
- submit={this.handleSubmitNotifyLevel}
- server_error={serverError}
- updateSection={handleUpdateSection}
- extraInfo={extraInfo}
- />
- );
- }
-
- var describe;
- if (notificationLevel === 'default') {
- describe = (
- <FormattedMessage
- id='channel_notifications.globalDefault'
- values={{
- notifyLevel: (globalNotifyLevelName)
- }}
- />
- );
- } else if (notificationLevel === 'mention') {
- describe = (<FormattedMessage id='channel_notifications.onlyMentions'/>);
- } else if (notificationLevel === 'all') {
- describe = (<FormattedMessage id='channel_notifications.allActivity'/>);
- } else {
- describe = (<FormattedMessage id='channel_notifications.never'/>);
- }
-
- return (
- <SettingItemMin
- title={sendDesktop}
- describe={describe}
- updateSection={() => {
- this.updateSection('desktop');
- }}
- />
- );
- }
-
- handleSubmitMarkUnreadLevel() {
- const channelId = this.props.channel.id;
- const markUnreadLevel = this.state.unreadLevel;
-
- if (this.props.channelMember.notify_props.mark_unread === markUnreadLevel) {
- this.updateSection('');
- return;
- }
-
- const data = {
- channel_id: channelId,
- user_id: this.props.currentUser.id,
- mark_unread: markUnreadLevel
- };
-
- //TODO: This should be fixed, moved to event_helpers
- Client.updateNotifyProps(data,
- () => {
- // Yuck...
- var member = ChannelStore.getMember(channelId);
- member.notify_props.mark_unread = markUnreadLevel;
- ChannelStore.setChannelMember(member);
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- handleUpdateMarkUnreadLevel(unreadLevel) {
- this.setState({unreadLevel});
- }
-
- createMarkUnreadLevelSection(serverError) {
- let content;
-
- const markUnread = (
- <FormattedMessage
- id='channel_notifications.markUnread'
- defaultMessage='Mark Channel Unread'
- />
- );
- if (this.state.activeSection === 'markUnreadLevel') {
- const inputs = [(
- <div key='channel-notification-unread-radio'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={this.state.unreadLevel === 'all'}
- onChange={this.handleUpdateMarkUnreadLevel.bind(this, 'all')}
- />
- <FormattedMessage
- id='channel_notifications.allUnread'
- defaultMessage='For all unread messages'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={this.state.unreadLevel === 'mention'}
- onChange={this.handleUpdateMarkUnreadLevel.bind(this, 'mention')}
- />
- <FormattedMessage id='channel_notifications.onlyMentions'/>
- </label>
- <br/>
- </div>
- </div>
- )];
-
- const handleUpdateSection = function handleUpdateSection(e) {
- this.updateSection('');
- this.onListenerChange();
- e.preventDefault();
- }.bind(this);
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='channel_notifications.unreadInfo'
- defaultMessage='The channel name is bolded in the sidebar when there are unread messages. Selecting "Only for mentions" will bold the channel only when you are mentioned.'
- />
- </span>
- );
-
- content = (
- <SettingItemMax
- title={markUnread}
- inputs={inputs}
- submit={this.handleSubmitMarkUnreadLevel}
- server_error={serverError}
- updateSection={handleUpdateSection}
- extraInfo={extraInfo}
- />
- );
- } else {
- let describe;
-
- if (!this.state.unreadLevel || this.state.unreadLevel === 'all') {
- describe = (
- <FormattedMessage
- id='channel_notifications.allUnread'
- defaultMessage='For all unread messages'
- />
- );
- } else {
- describe = (<FormattedMessage id='channel_notifications.onlyMentions'/>);
- }
-
- const handleUpdateSection = function handleUpdateSection(e) {
- this.updateSection('markUnreadLevel');
- e.preventDefault();
- }.bind(this);
-
- content = (
- <SettingItemMin
- title={markUnread}
- describe={describe}
- updateSection={handleUpdateSection}
- />
- );
- }
-
- return content;
- }
-
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- dialogClassName='settings-modal'
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_notifications.preferences'
- defaultMessage='Notification Preferences for '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <div className='settings-table'>
- <div className='settings-content'>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <br/>
- <div className='divider-dark first'/>
- {this.createNotifyLevelSection(serverError)}
- <div className='divider-light'/>
- {this.createMarkUnreadLevelSection(serverError)}
- <div className='divider-dark'/>
- </div>
- </div>
- </div>
- {serverError}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ChannelNotificationsModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired,
- channelMember: React.PropTypes.object.isRequired,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/channel_view.jsx b/web/react/components/channel_view.jsx
deleted file mode 100644
index 76744d6d7..000000000
--- a/web/react/components/channel_view.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import CenterPanel from '../components/center_panel.jsx';
-
-export default class ChannelView extends React.Component {
- render() {
- return (
- <CenterPanel/>
- );
- }
-}
-ChannelView.defaultProps = {
-};
-
-ChannelView.propTypes = {
- params: React.PropTypes.object
-};
diff --git a/web/react/components/claim/claim_account.jsx b/web/react/components/claim/claim_account.jsx
deleted file mode 100644
index 42fd8dafa..000000000
--- a/web/react/components/claim/claim_account.jsx
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EmailToSSO from './email_to_sso.jsx';
-import SSOToEmail from './sso_to_email.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ClaimAccount extends React.Component {
- constructor(props) {
- super(props);
-
- this.onTeamChange = this.onTeamChange.bind(this);
- this.updateStateFromStores = this.updateStateFromStores.bind(this);
-
- this.state = {};
- }
- componentWillMount() {
- this.setState({
- email: this.props.location.query.email,
- newType: this.props.location.query.new_type,
- oldType: this.props.location.query.old_type,
- teamName: this.props.params.team,
- teamDisplayName: ''
- });
- this.updateStateFromStores();
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- updateStateFromStores() {
- const team = TeamStore.getByName(this.state.teamName);
- let displayName = '';
- if (team) {
- displayName = team.displayName;
- }
- this.setState({
- teamDisplayName: displayName
- });
- }
- onTeamChange() {
- this.updateStateFromStores();
- }
- render() {
- if (this.state.teamDisplayName === '') {
- return (<div/>);
- }
- let content;
- if (this.state.email === '') {
- content = (
- <p>
- <FormattedMessage
- id='claim.account.noEmail'
- defaultMessage='No email specified'
- />
- </p>
- );
- } else if (this.state.oldType === '' && this.state.newType !== '') {
- content = (
- <EmailToSSO
- email={this.state.email}
- type={this.state.newType}
- teamName={this.state.teamName}
- teamDisplayName={this.state.teamDisplayName}
- />
- );
- } else {
- content = (
- <SSOToEmail
- email={this.state.email}
- currentType={this.state.oldType}
- teamName={this.state.teamName}
- teamDisplayName={this.state.teamDisplayName}
- />
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <div id='claim'>
- {content}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-ClaimAccount.defaultProps = {
-};
-ClaimAccount.propTypes = {
- params: React.PropTypes.object.isRequired,
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/claim/email_to_sso.jsx b/web/react/components/claim/email_to_sso.jsx
deleted file mode 100644
index c3eea9495..000000000
--- a/web/react/components/claim/email_to_sso.jsx
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- pwdError: {
- id: 'claim.email_to_sso.pwdError',
- defaultMessage: 'Please enter your password.'
- },
- pwd: {
- id: 'claim.email_to_sso.pwd',
- defaultMessage: 'Password'
- }
-});
-
-class EmailToSSO extends React.Component {
- constructor(props) {
- super(props);
-
- this.submit = this.submit.bind(this);
-
- this.state = {};
- }
- submit(e) {
- e.preventDefault();
- var state = {};
-
- var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password) {
- state.error = this.props.intl.formatMessage(holders.pwdError);
- this.setState(state);
- return;
- }
-
- state.error = null;
- this.setState(state);
-
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
- postData.service = this.props.type;
-
- Client.switchToSSO(postData,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- }
- },
- (error) => {
- this.setState({error});
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- const uiType = Utils.toTitleCase(this.props.type) + ' SSO';
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='claim.email_to_sso.title'
- defaultMessage='Switch Email/Password Account to {uiType}'
- values={{
- uiType: uiType
- }}
- />
- </h3>
- <form onSubmit={this.submit}>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.ssoType'
- defaultMessage='Upon claiming your account, you will only be able to login with {type} SSO'
- values={{
- type: Utils.toTitleCase(this.props.type)
- }}
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.ssoNote'
- defaultMessage='You must already have a valid {type} account'
- values={{
- type: Utils.toTitleCase(this.props.type)
- }}
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.email_to_sso.enterPwd'
- defaultMessage='Enter the password for your {team} {site} account'
- values={{
- team: this.props.teamDisplayName,
- site: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={this.props.intl.formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='claim.email_to_sso.switchTo'
- defaultMessage='Switch account to {uiType}'
- values={{
- uiType: uiType
- }}
- />
- </button>
- </form>
- </div>
- );
- }
-}
-
-EmailToSSO.defaultProps = {
-};
-EmailToSSO.propTypes = {
- intl: intlShape.isRequired,
- type: React.PropTypes.string.isRequired,
- email: React.PropTypes.string.isRequired,
- teamName: React.PropTypes.string.isRequired,
- teamDisplayName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(EmailToSSO);
diff --git a/web/react/components/claim/sso_to_email.jsx b/web/react/components/claim/sso_to_email.jsx
deleted file mode 100644
index a16efb57b..000000000
--- a/web/react/components/claim/sso_to_email.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- enterPwd: {
- id: 'claim.sso_to_email.enterPwd',
- defaultMessage: 'Please enter a password.'
- },
- pwdNotMatch: {
- id: 'claim.sso_to_email.pwdNotMatch',
- defaultMessage: 'Password do not match.'
- },
- newPwd: {
- id: 'claim.sso_to_email.newPwd',
- defaultMessage: 'New Password'
- },
- confirm: {
- id: 'claim.sso_to_email.confirm',
- defaultMessage: 'Confirm Password'
- }
-});
-
-class SSOToEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.submit = this.submit.bind(this);
-
- this.state = {};
- }
- submit(e) {
- const {formatMessage} = this.props.intl;
- e.preventDefault();
- const state = {};
-
- const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password) {
- state.error = formatMessage(holders.enterPwd);
- this.setState(state);
- return;
- }
-
- const confirmPassword = ReactDOM.findDOMNode(this.refs.passwordconfirm).value.trim();
- if (!confirmPassword || password !== confirmPassword) {
- state.error = formatMessage(holders.pwdNotMatch);
- this.setState(state);
- return;
- }
-
- state.error = null;
- this.setState(state);
-
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
-
- Client.switchToEmail(postData,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- }
- },
- (error) => {
- this.setState({error});
- }
- );
- }
- render() {
- const {formatMessage} = this.props.intl;
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- const uiType = Utils.toTitleCase(this.props.currentType) + ' SSO';
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='claim.sso_to_email.title'
- defaultMessage='Switch {type} Account to Email'
- values={{
- type: uiType
- }}
- />
- </h3>
- <form onSubmit={this.submit}>
- <p>
- <FormattedMessage
- id='claim.sso_to_email.description'
- defaultMessage='Upon changing your account type, you will only be able to login with your email and password.'
- />
- </p>
- <p>
- <FormattedMessage
- id='claim.sso_to_email_newPwd'
- defaultMessage='Enter a new password for your {team} {site} account'
- values={{
- team: this.props.teamDisplayName,
- site: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.newPwd)}
- spellCheck='false'
- />
- </div>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='passwordconfirm'
- ref='passwordconfirm'
- placeholder={formatMessage(holders.confirm)}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='claim.sso_to_email.switchTo'
- defaultMessage='Switch {type} to email and password'
- values={{
- type: uiType
- }}
- />
- </button>
- </form>
- </div>
- );
- }
-}
-
-SSOToEmail.defaultProps = {
-};
-SSOToEmail.propTypes = {
- intl: intlShape.isRequired,
- currentType: React.PropTypes.string.isRequired,
- email: React.PropTypes.string.isRequired,
- teamName: React.PropTypes.string.isRequired,
- teamDisplayName: React.PropTypes.string
-};
-
-export default injectIntl(SSOToEmail);
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
deleted file mode 100644
index bb3576684..000000000
--- a/web/react/components/confirm_modal.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-const Modal = ReactBootstrap.Modal;
-
-export default class ConfirmModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleConfirm = this.handleConfirm.bind(this);
- }
-
- handleConfirm() {
- this.props.onConfirm();
- }
-
- render() {
- return (
- <Modal
- className='modal-confirm'
- show={this.props.show}
- onHide={this.props.onCancel}
- >
- <Modal.Header closeButton={false}>
- <Modal.Title>{this.props.title}</Modal.Title>
- </Modal.Header>
- <Modal.Body>
- {this.props.message}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onCancel}
- >
- <FormattedMessage
- id='confirm_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.props.onConfirm}
- >
- {this.props.confirmButton}
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-ConfirmModal.defaultProps = {
- title: '',
- message: '',
- confirmButton: ''
-};
-ConfirmModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- title: React.PropTypes.node,
- message: React.PropTypes.node,
- confirmButton: React.PropTypes.node,
- onConfirm: React.PropTypes.func.isRequired,
- onCancel: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
deleted file mode 100644
index 24e0ff6e9..000000000
--- a/web/react/components/create_comment.jsx
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PostDeletedModal from './post_deleted_modal.jsx';
-import PostStore from '../stores/post_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import Textbox from './textbox.jsx';
-import MsgTyping from './msg_typing.jsx';
-import FileUpload from './file_upload.jsx';
-import FilePreview from './file_preview.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-const KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- commentLength: {
- id: 'create_comment.commentLength',
- defaultMessage: 'Comment length must be less than {max} characters.'
- },
- comment: {
- id: 'create_comment.comment',
- defaultMessage: 'Add Comment'
- },
- addComment: {
- id: 'create_comment.addComment',
- defaultMessage: 'Add a comment...'
- },
- commentTitle: {
- id: 'create_comment.commentTitle',
- defaultMessage: 'Comment'
- }
-});
-
-class CreateComment extends React.Component {
- constructor(props) {
- super(props);
-
- this.lastTime = 0;
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.commentMsgKeyPress = this.commentMsgKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handleUploadClick = this.handleUploadClick.bind(this);
- this.handleUploadStart = this.handleUploadStart.bind(this);
- this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
- this.handleUploadError = this.handleUploadError.bind(this);
- this.removePreview = this.removePreview.bind(this);
- this.getFileCount = this.getFileCount.bind(this);
- this.handleResize = this.handleResize.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.focusTextbox = this.focusTextbox.bind(this);
- this.showPostDeletedModal = this.showPostDeletedModal.bind(this);
- this.hidePostDeletedModal = this.hidePostDeletedModal.bind(this);
-
- PostStore.clearCommentDraftUploads();
-
- const draft = PostStore.getCommentDraft(this.props.rootId);
- this.state = {
- messageText: draft.message,
- uploadsInProgress: draft.uploadsInProgress,
- previews: draft.previews,
- submitting: false,
- windowWidth: Utils.windowWidth(),
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
- showPostDeletedModal: false
- };
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- window.addEventListener('resize', this.handleResize);
-
- this.focusTextbox();
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- window.removeEventListener('resize', this.handleResize);
- }
- onPreferenceChange() {
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- handleResize() {
- this.setState({windowWidth: Utils.windowWidth()});
- }
- componentDidUpdate(prevProps, prevState) {
- if (prevState.uploadsInProgress < this.state.uploadsInProgress) {
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- if (this.state.windowWidth > 768) {
- $('.post-right__scroll').perfectScrollbar('update');
- }
- }
-
- if (prevProps.rootId !== this.props.rootId) {
- this.focusTextbox();
- }
- }
- handleSubmit(e) {
- e.preventDefault();
-
- if (this.state.uploadsInProgress.length > 0) {
- return;
- }
-
- if (this.state.submitting) {
- return;
- }
-
- let post = {};
- post.filenames = [];
- post.message = this.state.messageText;
-
- if (post.message.trim().length === 0 && this.state.previews.length === 0) {
- return;
- }
-
- if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: this.props.intl.formatMessage(holders.commentLength, {max: Constants.CHARACTER_LIMIT})});
- return;
- }
-
- const userId = UserStore.getCurrentId();
-
- post.channel_id = this.props.channelId;
- post.root_id = this.props.rootId;
- post.parent_id = this.props.rootId;
- post.filenames = this.state.previews;
- const time = Utils.getTimestamp();
- post.pending_post_id = `${userId}:${time}`;
- post.user_id = userId;
- post.create_at = time;
-
- PostStore.storePendingPost(post);
- PostStore.storeCommentDraft(this.props.rootId, null);
-
- Client.createPost(
- post,
- ChannelStore.getCurrent(),
- (data) => {
- AsyncClient.getPosts(this.props.channelId);
-
- const channel = ChannelStore.get(this.props.channelId);
- let member = ChannelStore.getMember(this.props.channelId);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Date.now();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- (err) => {
- if (err.id === 'api.post.create_post.root_id.app_error') {
- this.showPostDeletedModal();
-
- PostStore.removePendingPost(post.channel_id, post.pending_post_id);
- } else {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- }
-
- this.setState({
- submitting: false
- });
- }
- );
-
- this.setState({
- messageText: '',
- submitting: false,
- postError: null,
- previews: [],
- serverError: null
- });
- }
- commentMsgKeyPress(e) {
- if (this.state.ctrlSend && e.ctrlKey || !this.state.ctrlSend) {
- if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.textbox).blur();
- this.handleSubmit(e);
- }
- }
-
- const t = Date.now();
- if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) {
- SocketStore.sendMessage({channel_id: this.props.channelId, action: 'typing', props: {parent_id: this.props.rootId}});
- this.lastTime = t;
- }
- }
- handleUserInput(messageText) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
- draft.message = messageText;
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- $('.post-right__scroll').perfectScrollbar('update');
- this.setState({messageText: messageText});
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.commentMsgKeyPress(e);
- return;
- }
-
- if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
- e.preventDefault();
-
- const lastPost = PostStore.getCurrentUsersLatestPost(this.props.channelId, this.props.rootId);
- if (!lastPost) {
- return;
- }
-
- AppDispatcher.handleViewAction({
- type: ActionTypes.RECEIVED_EDIT_POST,
- refocusId: '#reply_textbox',
- title: this.props.intl.formatMessage(holders.commentTitle),
- message: lastPost.message,
- postId: lastPost.id,
- channelId: lastPost.channel_id,
- comments: PostStore.getCommentCount(lastPost)
- });
- }
- }
- handleUploadClick() {
- this.focusTextbox();
- }
- handleUploadStart(clientIds) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
-
- // this is a bit redundant with the code that sets focus when the file input is clicked,
- // but this also resets the focus after a drag and drop
- this.focusTextbox();
- }
- handleFileUploadComplete(filenames, clientIds) {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- // remove each finished file from uploads
- for (let i = 0; i < clientIds.length; i++) {
- const index = draft.uploadsInProgress.indexOf(clientIds[i]);
-
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
- }
-
- draft.previews = draft.previews.concat(filenames);
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- handleUploadError(err, clientId) {
- if (clientId === -1) {
- this.setState({serverError: err});
- } else {
- let draft = PostStore.getCommentDraft(this.props.rootId);
-
- const index = draft.uploadsInProgress.indexOf(clientId);
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
-
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, serverError: err});
- }
- }
- removePreview(id) {
- let previews = this.state.previews;
- let uploadsInProgress = this.state.uploadsInProgress;
-
- // id can either be the path of an uploaded file or the client id of an in progress upload
- let index = previews.indexOf(id);
- if (index === -1) {
- index = uploadsInProgress.indexOf(id);
-
- if (index !== -1) {
- uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.getWrappedInstance().cancelUpload(id);
- }
- } else {
- previews.splice(index, 1);
- }
-
- let draft = PostStore.getCommentDraft(this.props.rootId);
- draft.previews = previews;
- draft.uploadsInProgress = uploadsInProgress;
- PostStore.storeCommentDraft(this.props.rootId, draft);
-
- this.setState({previews: previews, uploadsInProgress: uploadsInProgress});
- }
- componentWillReceiveProps(newProps) {
- if (newProps.rootId !== this.props.rootId) {
- const draft = PostStore.getCommentDraft(newProps.rootId);
- this.setState({messageText: draft.message, uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- }
- getFileCount() {
- return this.state.previews.length + this.state.uploadsInProgress.length;
- }
- focusTextbox() {
- if (!Utils.isMobile()) {
- this.refs.textbox.focus();
- }
- }
- showPostDeletedModal() {
- this.setState({
- showPostDeletedModal: true
- });
- }
- hidePostDeletedModal() {
- this.setState({
- showPostDeletedModal: false
- });
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let postError = null;
- if (this.state.postError) {
- postError = <label className='control-label'>{this.state.postError}</label>;
- }
-
- let preview = null;
- if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
- preview = (
- <FilePreview
- files={this.state.previews}
- onRemove={this.removePreview}
- uploadsInProgress={this.state.uploadsInProgress}
- />
- );
- }
-
- let postFooterClassName = 'post-create-footer';
- if (postError) {
- postFooterClassName += ' has-error';
- }
-
- let uploadsInProgressText = null;
- if (this.state.uploadsInProgress.length > 0) {
- uploadsInProgressText = (
- <span className='pull-right post-right-comments-upload-in-progress'>
- {this.state.uploadsInProgress.length === 1 ? (
- <FormattedMessage
- id='create_comment.file'
- defaultMessage='File uploading'
- />
- ) : (
- <FormattedMessage
- id='create_comment.files'
- defaultMessage='Files uploading'
- />
- )}
- </span>
- );
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='post-create'>
- <div
- id={this.props.rootId}
- className='post-create-body comment-create-body'
- >
- <div className='post-body__cell'>
- <Textbox
- onUserInput={this.handleUserInput}
- onKeyPress={this.commentMsgKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.messageText}
- createMessage={formatMessage(holders.addComment)}
- initialText=''
- supportsCommands={false}
- id='reply_textbox'
- ref='textbox'
- />
- <FileUpload
- ref='fileUpload'
- getFileCount={this.getFileCount}
- onClick={this.handleUploadClick}
- onUploadStart={this.handleUploadStart}
- onFileUpload={this.handleFileUploadComplete}
- onUploadError={this.handleUploadError}
- postType='comment'
- channelId={this.props.channelId}
- />
- </div>
- </div>
- <MsgTyping
- channelId={this.props.channelId}
- parentId={this.props.rootId}
- />
- <div className={postFooterClassName}>
- <input
- type='button'
- className='btn btn-primary comment-btn pull-right'
- value={formatMessage(holders.comment)}
- onClick={this.handleSubmit}
- />
- {uploadsInProgressText}
- {preview}
- {postError}
- {serverError}
- </div>
- </div>
- <PostDeletedModal
- show={this.state.showPostDeletedModal}
- onHide={this.hidePostDeletedModal}
- />
- </form>
- );
- }
-}
-
-CreateComment.propTypes = {
- intl: intlShape.isRequired,
- channelId: React.PropTypes.string.isRequired,
- rootId: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(CreateComment);
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
deleted file mode 100644
index 69cc74842..000000000
--- a/web/react/components/create_post.jsx
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import MsgTyping from './msg_typing.jsx';
-import Textbox from './textbox.jsx';
-import FileUpload from './file_upload.jsx';
-import FilePreview from './file_preview.jsx';
-import PostDeletedModal from './post_deleted_modal.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-const ActionTypes = Constants.ActionTypes;
-const KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- comment: {
- id: 'create_post.comment',
- defaultMessage: 'Comment'
- },
- post: {
- id: 'create_post.post',
- defaultMessage: 'Post'
- },
- write: {
- id: 'create_post.write',
- defaultMessage: 'Write a message...'
- }
-});
-
-class CreatePost extends React.Component {
- constructor(props) {
- super(props);
-
- this.lastTime = 0;
-
- this.getCurrentDraft = this.getCurrentDraft.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.postMsgKeyPress = this.postMsgKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleUploadClick = this.handleUploadClick.bind(this);
- this.handleUploadStart = this.handleUploadStart.bind(this);
- this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
- this.handleUploadError = this.handleUploadError.bind(this);
- this.removePreview = this.removePreview.bind(this);
- this.onChange = this.onChange.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.getFileCount = this.getFileCount.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.sendMessage = this.sendMessage.bind(this);
- this.focusTextbox = this.focusTextbox.bind(this);
- this.showPostDeletedModal = this.showPostDeletedModal.bind(this);
- this.hidePostDeletedModal = this.hidePostDeletedModal.bind(this);
-
- PostStore.clearDraftUploads();
-
- const draft = this.getCurrentDraft();
-
- this.state = {
- channelId: ChannelStore.getCurrentId(),
- messageText: draft.messageText,
- uploadsInProgress: draft.uploadsInProgress,
- previews: draft.previews,
- submitting: false,
- initialText: draft.messageText,
- ctrlSend: false,
- showTutorialTip: false,
- showPostDeletedModal: false
- };
- }
- getCurrentDraft() {
- const draft = PostStore.getCurrentDraft();
- const safeDraft = {previews: [], messageText: '', uploadsInProgress: []};
-
- if (draft) {
- if (draft.message) {
- safeDraft.messageText = draft.message;
- }
- if (draft.previews) {
- safeDraft.previews = draft.previews;
- }
- if (draft.uploadsInProgress) {
- safeDraft.uploadsInProgress = draft.uploadsInProgress;
- }
- }
-
- return safeDraft;
- }
- handleSubmit(e) {
- e.preventDefault();
-
- if (this.state.uploadsInProgress.length > 0 || this.state.submitting) {
- return;
- }
-
- const post = {};
- post.filenames = [];
- post.message = this.state.messageText;
-
- if (post.message.trim().length === 0 && this.state.previews.length === 0) {
- return;
- }
-
- if (post.message.length > Constants.CHARACTER_LIMIT) {
- this.setState({postError: `Post length must be less than ${Constants.CHARACTER_LIMIT} characters.`});
- return;
- }
-
- this.setState({submitting: true, serverError: null});
-
- if (post.message.indexOf('/') === 0) {
- Client.executeCommand(
- this.state.channelId,
- post.message,
- false,
- (data) => {
- PostStore.storeDraft(this.state.channelId, null);
- this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
-
- if (data.goto_location && data.goto_location.length > 0) {
- window.location.href = data.goto_location;
- }
- },
- (err) => {
- if (err.sendMessage) {
- this.sendMessage(post);
- } else {
- const state = {};
- state.serverError = err.message;
- state.submitting = false;
- this.setState(state);
- }
- }
- );
- } else {
- this.sendMessage(post);
- }
- }
- sendMessage(post) {
- post.channel_id = this.state.channelId;
- post.filenames = this.state.previews;
-
- const time = Utils.getTimestamp();
- const userId = UserStore.getCurrentId();
- post.pending_post_id = `${userId}:${time}`;
- post.user_id = userId;
- post.create_at = time;
- post.parent_id = this.state.parentId;
-
- const channel = ChannelStore.get(this.state.channelId);
-
- GlobalActions.emitUserPostedEvent(post);
- this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
-
- Client.createPost(post, channel,
- (data) => {
- AsyncClient.getPosts();
-
- const member = ChannelStore.getMember(channel.id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Date.now();
- ChannelStore.setChannelMember(member);
-
- GlobalActions.emitPostRecievedEvent(data);
- },
- (err) => {
- if (err.id === 'api.post.create_post.root_id.app_error') {
- // this should never actually happen since you can't reply from this textbox
- this.showPostDeletedModal();
-
- PostStore.removePendingPost(post.pending_post_id);
- } else {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- }
-
- this.setState({
- submitting: false
- });
- }
- );
- }
- focusTextbox() {
- if (!Utils.isMobile()) {
- this.refs.textbox.focus();
- }
- }
- postMsgKeyPress(e) {
- if (this.state.ctrlSend && e.ctrlKey || !this.state.ctrlSend) {
- if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.textbox).blur();
- this.handleSubmit(e);
- }
- }
-
- const t = Date.now();
- if ((t - this.lastTime) > Constants.UPDATE_TYPING_MS) {
- SocketStore.sendMessage({channel_id: this.state.channelId, action: 'typing', props: {parent_id: ''}, state: {}});
- this.lastTime = t;
- }
- }
- handleUserInput(messageText) {
- this.setState({messageText});
-
- const draft = PostStore.getCurrentDraft();
- draft.message = messageText;
- PostStore.storeCurrentDraft(draft);
- }
- handleUploadClick() {
- this.focusTextbox();
- }
- handleUploadStart(clientIds, channelId) {
- const draft = PostStore.getDraft(channelId);
-
- draft.uploadsInProgress = draft.uploadsInProgress.concat(clientIds);
- PostStore.storeDraft(channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
-
- // this is a bit redundant with the code that sets focus when the file input is clicked,
- // but this also resets the focus after a drag and drop
- this.focusTextbox();
- }
- handleFileUploadComplete(filenames, clientIds, channelId) {
- const draft = PostStore.getDraft(channelId);
-
- // remove each finished file from uploads
- for (let i = 0; i < clientIds.length; i++) {
- const index = draft.uploadsInProgress.indexOf(clientIds[i]);
-
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
- }
-
- draft.previews = draft.previews.concat(filenames);
- PostStore.storeDraft(channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress, previews: draft.previews});
- }
- handleUploadError(err, clientId) {
- let message = err;
- if (message && typeof message !== 'string') {
- // err is an AppError from the server
- message = err.message;
- }
-
- if (clientId !== -1) {
- const draft = PostStore.getDraft(this.state.channelId);
-
- const index = draft.uploadsInProgress.indexOf(clientId);
- if (index !== -1) {
- draft.uploadsInProgress.splice(index, 1);
- }
-
- PostStore.storeDraft(this.state.channelId, draft);
-
- this.setState({uploadsInProgress: draft.uploadsInProgress});
- }
-
- this.setState({serverError: message});
- }
- removePreview(id) {
- const previews = Object.assign([], this.state.previews);
- const uploadsInProgress = this.state.uploadsInProgress;
-
- // id can either be the path of an uploaded file or the client id of an in progress upload
- let index = previews.indexOf(id);
- if (index === -1) {
- index = uploadsInProgress.indexOf(id);
-
- if (index !== -1) {
- uploadsInProgress.splice(index, 1);
- this.refs.fileUpload.getWrappedInstance().cancelUpload(id);
- }
- } else {
- previews.splice(index, 1);
- }
-
- const draft = PostStore.getCurrentDraft();
- draft.previews = previews;
- draft.uploadsInProgress = uploadsInProgress;
- PostStore.storeCurrentDraft(draft);
-
- this.setState({previews, uploadsInProgress});
- }
- componentWillMount() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
-
- // wait to load these since they may have changed since the component was constructed (particularly in the case of skipping the tutorial)
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
- showTutorialTip: tutorialStep === TutorialSteps.POST_POPOVER
- });
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- PreferenceStore.addChangeListener(this.onPreferenceChange);
-
- this.focusTextbox();
- }
- componentDidUpdate(prevProps, prevState) {
- if (prevState.channelId !== this.state.channelId) {
- this.focusTextbox();
- }
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChange);
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- onChange() {
- const channelId = ChannelStore.getCurrentId();
- if (this.state.channelId !== channelId) {
- const draft = this.getCurrentDraft();
-
- this.setState({channelId, messageText: draft.messageText, initialText: draft.messageText, submitting: false, serverError: null, postError: null, previews: draft.previews, uploadsInProgress: draft.uploadsInProgress});
- }
- }
- onPreferenceChange() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
- this.setState({
- showTutorialTip: tutorialStep === TutorialSteps.POST_POPOVER,
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- getFileCount(channelId) {
- if (channelId === this.state.channelId) {
- return this.state.previews.length + this.state.uploadsInProgress.length;
- }
-
- const draft = PostStore.getDraft(channelId);
- return draft.previews.length + draft.uploadsInProgress.length;
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.postMsgKeyPress(e);
- return;
- }
-
- if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
- e.preventDefault();
-
- const channelId = ChannelStore.getCurrentId();
- const lastPost = PostStore.getCurrentUsersLatestPost(channelId);
- if (!lastPost) {
- return;
- }
- const {formatMessage} = this.props.intl;
- var type = (lastPost.root_id && lastPost.root_id.length > 0) ? formatMessage(holders.comment) : formatMessage(holders.post);
-
- AppDispatcher.handleViewAction({
- type: ActionTypes.RECEIVED_EDIT_POST,
- refocusId: '#post_textbox',
- title: type,
- message: lastPost.message,
- postId: lastPost.id,
- channelId: lastPost.channel_id,
- comments: PostStore.getCommentCount(lastPost)
- });
- }
- }
- showPostDeletedModal() {
- this.setState({
- showPostDeletedModal: true
- });
- }
- hidePostDeletedModal() {
- this.setState({
- showPostDeletedModal: false
- });
- }
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='create_post.tutorialTip'
- defaultMessage='<h4>Sending Messages</h4><p>Type here to write a message and press <strong>Enter</strong> to post it.</p><p>Click the <strong>Attachment</strong> button to upload an image or a file.</p>'
- />
- </div>
- );
-
- return (
- <TutorialTip
- placement='top'
- screens={screens}
- overlayClass='tip-overlay--chat'
- />
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let postError = null;
- if (this.state.postError) {
- postError = <label className='control-label'>{this.state.postError}</label>;
- }
-
- let preview = null;
- if (this.state.previews.length > 0 || this.state.uploadsInProgress.length > 0) {
- preview = (
- <FilePreview
- files={this.state.previews}
- onRemove={this.removePreview}
- uploadsInProgress={this.state.uploadsInProgress}
- />
- );
- }
-
- let postFooterClassName = 'post-create-footer';
- if (postError) {
- postFooterClassName += ' has-error';
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <form
- id='create_post'
- ref='topDiv'
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className='post-create'>
- <div className='post-create-body'>
- <div className='post-body__cell'>
- <Textbox
- onUserInput={this.handleUserInput}
- onKeyPress={this.postMsgKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.messageText}
- createMessage={this.props.intl.formatMessage(holders.write)}
- channelId={this.state.channelId}
- id='post_textbox'
- ref='textbox'
- />
- <FileUpload
- ref='fileUpload'
- getFileCount={this.getFileCount}
- onClick={this.handleUploadClick}
- onUploadStart={this.handleUploadStart}
- onFileUpload={this.handleFileUploadComplete}
- onUploadError={this.handleUploadError}
- postType='post'
- channelId=''
- />
- </div>
- <a
- className='send-button theme'
- onClick={this.handleSubmit}
- >
- <i className='fa fa-paper-plane'/>
- </a>
- {tutorialTip}
- </div>
- <div className={postFooterClassName}>
- <MsgTyping
- channelId={this.state.channelId}
- parentId=''
- />
- {preview}
- {postError}
- {serverError}
- </div>
- </div>
- <PostDeletedModal
- show={this.state.showPostDeletedModal}
- onHide={this.hidePostDeletedModal}
- />
- </form>
- );
- }
-}
-
-CreatePost.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(CreatePost);
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
deleted file mode 100644
index 70e7a67a8..000000000
--- a/web/react/components/delete_channel_modal.jsx
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-const Modal = ReactBootstrap.Modal;
-import TeamStore from '../stores/team_store.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-export default class DeleteChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDelete = this.handleDelete.bind(this);
- }
-
- handleDelete() {
- if (this.props.channel.id.length !== 26) {
- return;
- }
-
- browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
- Client.deleteChannel(
- this.props.channel.id,
- () => {
- AsyncClient.getChannels(true);
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleDelete');
- }
- );
- }
-
- render() {
- let channelTerm = (
- <FormattedMessage
- id='delete_channel.channel'
- defaultMessage='channel'
- />
- );
- if (this.props.channel.type === Constants.PRIVATE_CHANNEL) {
- channelTerm = (
- <FormattedMessage
- id='delete_channel.group'
- defaultMessage='group'
- />
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <h4 className='modal-title'>
- <FormattedMessage
- id='delete_channel.confirm'
- defaultMessage='Confirm DELETE Channel'
- />
- </h4>
- </Modal.Header>
- <Modal.Body>
- <FormattedMessage
- id='delete_channel.question'
- defaultMessage='Are you sure you wish to delete the {display_name} {term}?'
- values={{
- display_name: this.props.channel.display_name,
- term: (channelTerm)
- }}
- />
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='delete_channel.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-danger'
- data-dismiss='modal'
- onClick={this.handleDelete}
- >
- <FormattedMessage
- id='delete_channel.del'
- defaultMessage='Delete'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-DeleteChannelModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
deleted file mode 100644
index 5b2dd1197..000000000
--- a/web/react/components/delete_post_modal.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import PostStore from '../stores/post_store.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-var Modal = ReactBootstrap.Modal;
-import * as AsyncClient from '../utils/async_client.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-export default class DeletePostModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDelete = this.handleDelete.bind(this);
- this.handleToggle = this.handleToggle.bind(this);
- this.handleHide = this.handleHide.bind(this);
-
- this.state = {
- show: false,
- post: null,
- commentCount: 0,
- error: ''
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_DELETE_POST_MODAL, this.handleToggle);
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (this.state.show && !prevState.show) {
- setTimeout(() => {
- $(ReactDOM.findDOMNode(this.refs.deletePostBtn)).focus();
- }, 0);
- }
- }
-
- handleDelete() {
- Client.deletePost(
- this.state.post.channel_id,
- this.state.post.id,
- () => {
- PostStore.deletePost(this.state.post);
- AsyncClient.getPosts(this.state.post.channel_id);
-
- if (this.state.post.id === PostStore.getSelectedPostId()) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- },
- (err) => {
- AsyncClient.dispatchError(err, 'deletePost');
- }
- );
-
- this.handleHide();
- }
-
- handleToggle(value, args) {
- this.setState({
- show: value,
- post: args.post,
- commentCount: args.commentCount,
- error: ''
- });
- }
-
- handleHide() {
- this.setState({show: false});
- }
-
- render() {
- if (!this.state.post) {
- return null;
- }
-
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var commentWarning = '';
- if (this.state.commentCount > 0) {
- commentWarning = (
- <FormattedMessage
- id='delete_post.warning'
- defaultMessage='This post has {count} comment(s) on it.'
- values={{
- count: this.state.commentCount
- }}
- />
- );
- }
-
- const postTerm = this.state.post.root_id ? (
- <FormattedMessage
- id='delete_post.comment'
- defaultMessage='Comment'
- />
- ) : (
- <FormattedMessage
- id='delete_post.post'
- defaultMessage='Post'
- />
- );
-
- return (
- <Modal
- show={this.state.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='delete_post.confirm'
- defaultMessage='Confirm {term} Delete'
- values={{
- term: (postTerm)
- }}
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <FormattedMessage
- id='delete_post.question'
- defaultMessage='Are you sure you want to delete this {term}?'
- values={{
- term: (postTerm)
- }}
- />
- <br/>
- <br/>
- {commentWarning}
- {error}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='delete_post.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- ref='deletePostBtn'
- type='button'
- className='btn btn-danger'
- onClick={this.handleDelete}
- >
- <FormattedMessage
- id='delete_post.del'
- defaultMessage='Delete'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
diff --git a/web/react/components/do_verify_email.jsx b/web/react/components/do_verify_email.jsx
deleted file mode 100644
index df98bf463..000000000
--- a/web/react/components/do_verify_email.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import * as Client from '../utils/client.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import {browserHistory} from 'react-router';
-
-export default class DoVerifyEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- verifyStatus: 'pending',
- serverError: ''
- };
- }
- componentWillMount() {
- const uid = this.props.location.query.uid;
- const hid = this.props.location.query.hid;
- const teamName = this.props.location.query.teamname;
- const email = this.props.location.query.email;
-
- Client.verifyEmail(
- () => {
- browserHistory.push('/' + teamName + '/login?extra=verified&email=' + email);
- },
- (err) => {
- this.setState({verifyStatus: 'failure', serverError: err.message});
- },
- uid,
- hid
- );
- }
- render() {
- if (this.state.verifyStatus !== 'failure') {
- return (<LoadingScreen/>);
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='email_verify.almost'
- defaultMessage='{siteName}: You are almost done'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h3>
- <div>
- <p>
- <FormattedMessage id='email_verify.verifyFailed'/>
- </p>
- <p className='alert alert-danger'>
- <i className='fa fa-times'/>
- {this.state.serverError}
- </p>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-DoVerifyEmail.defaultProps = {
-};
-DoVerifyEmail.propTypes = {
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/edit_channel_header_modal.jsx b/web/react/components/edit_channel_header_modal.jsx
deleted file mode 100644
index f6865fadd..000000000
--- a/web/react/components/edit_channel_header_modal.jsx
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- error: {
- id: 'edit_channel_header_modal.error',
- defaultMessage: 'This channel header is too long, please enter a shorter one'
- }
-});
-
-class EditChannelHeaderModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.onShow = this.onShow.bind(this);
- this.onHide = this.onHide.bind(this);
-
- this.state = {
- header: props.channel.header,
- serverError: ''
- };
- }
-
- componentDidMount() {
- if (this.props.show) {
- this.onShow();
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.channel.header !== nextProps.channel.header) {
- this.setState({
- header: nextProps.channel.header
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.onShow();
- }
- }
-
- handleChange(e) {
- this.setState({
- header: e.target.value
- });
- }
-
- handleSubmit() {
- Client.updateChannelHeader(
- this.props.channel.id,
- this.state.header,
- (channel) => {
- this.setState({serverError: ''});
- this.onHide();
-
- AppDispatcher.handleServerAction({
- type: Constants.ActionTypes.RECEIVED_CHANNEL,
- channel
- });
- },
- (err) => {
- if (err.id === 'api.context.invalid_param.app_error') {
- this.setState({serverError: this.props.intl.formatMessage(holders.error)});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
-
- onShow() {
- const textarea = ReactDOM.findDOMNode(this.refs.textarea);
- Utils.placeCaretAtEnd(textarea);
- }
-
- onHide() {
- this.setState({
- serverError: '',
- header: this.props.channel.header
- });
-
- this.props.onHide();
- }
-
- render() {
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><br/><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='edit_channel_header_modal.title'
- defaultMessage='Edit Header for {channel}'
- values={{
- channel: this.props.channel.display_name
- }}
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='edit_channel_header_modal.description'
- defaultMessage='Edit the text appearing next to the channel name in the channel header.'
- />
- </p>
- <textarea
- ref='textarea'
- className='form-control no-resize'
- rows='6'
- id='edit_header'
- maxLength='1024'
- value={this.state.header}
- onChange={this.handleChange}
- />
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.onHide}
- >
- <FormattedMessage
- id='edit_channel_header_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleSubmit}
- >
- <FormattedMessage
- id='edit_channel_header_modal.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-EditChannelHeaderModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(EditChannelHeaderModal);
diff --git a/web/react/components/edit_channel_purpose_modal.jsx b/web/react/components/edit_channel_purpose_modal.jsx
deleted file mode 100644
index d8354f59d..000000000
--- a/web/react/components/edit_channel_purpose_modal.jsx
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- error: {
- id: 'edit_channel_purpose_modal.error',
- defaultMessage: 'This channel purpose is too long, please enter a shorter one'
- }
-});
-
-export default class EditChannelPurposeModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- this.handleSave = this.handleSave.bind(this);
-
- this.state = {serverError: ''};
- }
-
- componentDidUpdate() {
- if (this.props.show) {
- $(ReactDOM.findDOMNode(this.refs.purpose)).focus();
- }
- }
-
- handleHide() {
- this.setState({serverError: ''});
-
- if (this.props.onModalDismissed) {
- this.props.onModalDismissed();
- }
- }
-
- handleSave() {
- if (!this.props.channel) {
- return;
- }
-
- const data = {
- channel_id: this.props.channel.id,
- channel_purpose: ReactDOM.findDOMNode(this.refs.purpose).value.trim()
- };
-
- Client.updateChannelPurpose(data,
- () => {
- AsyncClient.getChannel(this.props.channel.id);
-
- this.handleHide();
- },
- (err) => {
- if (err.id === 'api.context.invalid_param.app_error') {
- this.setState({serverError: this.props.intl.formatMessage(holders.error)});
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- }
-
- render() {
- if (!this.props.show) {
- return null;
- }
-
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <br/>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- let title = (
- <span>
- <FormattedMessage
- id='edit_channel_purpose_modal.title1'
- defaultMessage='Edit Purpose'
- />
- </span>
- );
- if (this.props.channel.display_name) {
- title = (
- <span>
- <FormattedMessage
- id='edit_channel_purpose_modal.title2'
- defaultMessage='Edit Purpose for '
- />
- <span className='name'>{this.props.channel.display_name}</span>
- </span>
- );
- }
-
- let channelType = (
- <FormattedMessage
- id='edit_channel_purpose_modal.channel'
- defaultMessage='Channel'
- />
- );
- if (this.props.channel.type === Constants.PRIVATE_CHANNEL) {
- channelType = (
- <FormattedMessage
- id='edit_channel_purpose_modal.group'
- defaultMessage='Group'
- />
- );
- }
-
- return (
- <Modal
- className='modal-edit-channel-purpose'
- ref='modal'
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- {title}
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='edit_channel_purpose_modal.body'
- defaultMessage='Describe how this {type} should be used. This text appears in the channel list in the "More..." menu and helps others decide whether to join.'
- values={{
- type: (channelType)
- }}
- />
- </p>
- <textarea
- ref='purpose'
- className='form-control no-resize'
- rows='6'
- maxLength='128'
- defaultValue={this.props.channel.purpose}
- />
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='edit_channel_purpose_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleSave}
- >
- <FormattedMessage
- id='edit_channel_purpose_modal.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-EditChannelPurposeModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channel: React.PropTypes.object,
- onModalDismissed: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(EditChannelPurposeModal); \ No newline at end of file
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
deleted file mode 100644
index f02239fcf..000000000
--- a/web/react/components/edit_post_modal.jsx
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import Textbox from './textbox.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-var KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- editPost: {
- id: 'edit_post.editPost',
- defaultMessage: 'Edit the post...'
- }
-});
-
-class EditPostModal extends React.Component {
- constructor() {
- super();
-
- this.handleEdit = this.handleEdit.bind(this);
- this.handleEditInput = this.handleEditInput.bind(this);
- this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
-
- this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
- }
- handleEdit() {
- var updatedPost = {};
- updatedPost.message = this.state.editText.trim();
-
- if (updatedPost.message.length === 0) {
- var tempState = this.state;
- delete tempState.editText;
- BrowserStore.setItem('edit_state_transfer', tempState);
- $('#edit_post').modal('hide');
- GlobalActions.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
- return;
- }
-
- updatedPost.id = this.state.post_id;
- updatedPost.channel_id = this.state.channel_id;
-
- Client.updatePost(updatedPost,
- function success() {
- AsyncClient.getPosts(updatedPost.channel_id);
- window.scrollTo(0, 0);
- },
- function error(err) {
- AsyncClient.dispatchError(err, 'updatePost');
- }
- );
-
- $('#edit_post').modal('hide');
- }
- handleEditInput(editMessage) {
- this.setState({editText: editMessage});
- }
- handleEditKeyPress(e) {
- if (!this.state.ctrlSend && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
- e.preventDefault();
- ReactDOM.findDOMNode(this.refs.editbox).blur();
- this.handleEdit(e);
- }
- }
- handleUserInput(e) {
- this.setState({editText: e.target.value});
- }
- handleEditPostEvent(options) {
- this.setState({
- editText: options.message || '',
- title: options.title || '',
- post_id: options.postId || '',
- channel_id: options.channelId || '',
- comments: options.comments || 0,
- refocusId: options.refocusId || ''
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('show');
- }
- handleKeyDown(e) {
- if (this.state.ctrlSend && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
- this.handleEdit(e);
- }
- }
- onPreferenceChange() {
- this.setState({
- ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
- });
- }
- componentDidMount() {
- var self = this;
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
- self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
- var button = e.relatedTarget;
- if (!button) {
- return;
- }
- self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
- self.refs.editbox.resize();
- $('#edit_textbox').get(0).focus();
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
- if (self.state.refocusId !== '') {
- setTimeout(() => {
- $(self.state.refocusId).get(0).focus();
- });
- }
- });
-
- PostStore.addEditPostListener(this.handleEditPostEvent);
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- }
- componentWillUnmount() {
- PostStore.removeEditPostListner(this.handleEditPostEvent);
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- render() {
- var error = (<div className='form-group'><br/></div>);
- if (this.state.error) {
- error = (<div className='form-group has-error'><br/><label className='control-label'>{this.state.error}</label></div>);
- }
-
- return (
- <div
- className='modal fade edit-modal'
- ref='modal'
- id='edit_post'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog modal-push-down'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.handleEditClose}
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='edit_post.edit'
- defaultMessage='Edit {title}'
- values={{
- title: this.state.title
- }}
- />
- </h4>
- </div>
- <div className='edit-modal-body modal-body'>
- <Textbox
- onUserInput={this.handleEditInput}
- onKeyPress={this.handleEditKeyPress}
- onKeyDown={this.handleKeyDown}
- messageText={this.state.editText}
- createMessage={this.props.intl.formatMessage(holders.editPost)}
- supportsCommands={false}
- id='edit_textbox'
- ref='editbox'
- />
- {error}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='edit_post.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleEdit}
- >
- <FormattedMessage
- id='edit_post.save'
- defaultMessage='Save'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-EditPostModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(EditPostModal); \ No newline at end of file
diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx
deleted file mode 100644
index 9a114c544..000000000
--- a/web/react/components/error_bar.jsx
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ErrorStore from '../stores/error_store.jsx';
-
-// import mm-intl is required for the tool to be able to extract the messages
-import {defineMessages} from 'mm-intl';
-
-var messages = defineMessages({
- preview: {
- id: 'error_bar.preview_mode',
- defaultMessage: 'Preview Mode: Email notifications have not been configured'
- }
-});
-
-export default class ErrorBar extends React.Component {
- constructor() {
- super();
-
- this.onErrorChange = this.onErrorChange.bind(this);
- this.handleClose = this.handleClose.bind(this);
-
- this.state = ErrorStore.getLastError();
- }
-
- static propTypes() {
- return {
- intl: ReactIntl.intlShape.isRequired
- };
- }
-
- isValidError(s) {
- if (!s) {
- return false;
- }
-
- if (!s.message) {
- return false;
- }
-
- return true;
- }
-
- componentWillMount() {
- if (global.window.mm_config.SendEmailNotifications === 'false') {
- ErrorStore.storeLastError({message: this.props.intl.formatMessage(messages.preview)});
- this.onErrorChange();
- }
- }
-
- componentDidMount() {
- ErrorStore.addChangeListener(this.onErrorChange);
- }
-
- componentWillUnmount() {
- ErrorStore.removeChangeListener(this.onErrorChange);
- }
-
- onErrorChange() {
- var newState = ErrorStore.getLastError();
-
- if (newState) {
- this.setState(newState);
- } else {
- this.setState({message: null});
- }
- }
-
- handleClose(e) {
- if (e) {
- e.preventDefault();
- }
-
- ErrorStore.clearLastError();
- this.setState({message: null});
- }
-
- render() {
- if (!this.isValidError(this.state)) {
- return <div/>;
- }
-
- return (
- <div className='error-bar'>
- <span>{this.state.message}</span>
- <a
- href='#'
- className='error-bar__close'
- onClick={this.handleClose}
- >
- &times;
- </a>
- </div>
- );
- }
-}
-
-export default ReactIntl.injectIntl(ErrorBar);
diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx
deleted file mode 100644
index 383529212..000000000
--- a/web/react/components/file_attachment.jsx
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- download: {
- id: 'file_attachment.download',
- defaultMessage: 'Download'
- }
-});
-
-class FileAttachment extends React.Component {
- constructor(props) {
- super(props);
-
- this.loadFiles = this.loadFiles.bind(this);
- this.addBackgroundImage = this.addBackgroundImage.bind(this);
-
- this.canSetState = false;
- this.state = {fileSize: -1};
- }
- componentDidMount() {
- this.loadFiles();
- }
- componentDidUpdate(prevProps) {
- if (this.props.filename !== prevProps.filename) {
- this.loadFiles();
- }
- }
- loadFiles() {
- this.canSetState = true;
-
- var filename = this.props.filename;
-
- if (filename) {
- var fileInfo = this.getFileInfoFromName(filename);
- var type = utils.getFileType(fileInfo.ext);
-
- if (type === 'image') {
- var self = this; // Need this reference since we use the given "this"
- $('<img/>').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) {
- return function loader() {
- $(this).remove();
- if (name in self.refs) {
- var imgDiv = ReactDOM.findDOMNode(self.refs[name]);
-
- $(imgDiv).removeClass('post-image__load');
- $(imgDiv).addClass('post-image');
-
- var width = this.width || $(this).width();
- var height = this.height || $(this).height();
-
- if (width < Constants.THUMBNAIL_WIDTH &&
- height < Constants.THUMBNAIL_HEIGHT) {
- $(imgDiv).addClass('small');
- } else {
- $(imgDiv).addClass('normal');
- }
-
- self.addBackgroundImage(name, path);
- }
- };
- }(fileInfo.path, filename));
- }
- }
- }
- componentWillUnmount() {
- // keep track of when this component is mounted so that we can asynchronously change state without worrying about whether or not we're mounted
- this.canSetState = false;
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!utils.areObjectsEqual(nextProps, this.props)) {
- return true;
- }
-
- // the only time this object should update is when it receives an updated file size which we can usually handle without re-rendering
- if (nextState.fileSize !== this.state.fileSize) {
- if (this.refs.fileSize) {
- // update the UI element to display the file size without re-rendering the whole component
- ReactDOM.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize);
-
- return false;
- }
-
- // we can't find the element that should hold the file size so we must not have rendered yet
- return true;
- }
-
- return true;
- }
- getFileInfoFromName(name) {
- var fileInfo = utils.splitFileLocation(name);
-
- fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
-
- return fileInfo;
- }
- addBackgroundImage(name, path) {
- var fileUrl = path;
-
- if (name in this.refs) {
- if (!path) {
- fileUrl = this.getFileInfoFromName(name).path;
- }
-
- var imgDiv = ReactDOM.findDOMNode(this.refs[name]);
- var re1 = new RegExp(' ', 'g');
- var re2 = new RegExp('\\(', 'g');
- var re3 = new RegExp('\\)', 'g');
- var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
-
- $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)');
- }
- }
- removeBackgroundImage(name) {
- if (name in this.refs) {
- $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial');
- }
- }
- render() {
- var filename = this.props.filename;
-
- var fileInfo = utils.splitFileLocation(filename);
- var fileUrl = utils.getFileUrl(filename, true);
- var type = utils.getFileType(fileInfo.ext);
-
- var thumbnail;
- if (type === 'image') {
- thumbnail = (
- <div
- ref={filename}
- className='post-image__load'
- style={{backgroundImage: 'url(/static/images/load.gif)'}}
- />
- );
- } else {
- thumbnail = <div className={'file-icon ' + utils.getIconClassName(type)}/>;
- }
-
- var fileSizeString = '';
- if (this.state.fileSize < 0) {
- Client.getFileInfo(
- filename,
- function success(data) {
- if (this.canSetState) {
- this.setState({fileSize: parseInt(data.size, 10)});
- }
- }.bind(this),
- function error() {
- // Do nothing
- }
- );
- } else {
- fileSizeString = utils.fileSizeToString(this.state.fileSize);
- }
-
- var filenameString = decodeURIComponent(utils.getFileName(filename));
- var trimmedFilename;
- if (filenameString.length > 35) {
- trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + '...';
- } else {
- trimmedFilename = filenameString;
- }
-
- return (
- <div
- className='post-image__column'
- key={filename}
- >
- <a className='post-image__thumbnail'
- href='#'
- onClick={() => this.props.handleImageClick(this.props.index)}
- >
- {thumbnail}
- </a>
- <div className='post-image__details'>
- <a
- href={fileUrl}
- download={filenameString}
- data-toggle='tooltip'
- title={this.props.intl.formatMessage(holders.download) + ' \"' + filenameString + '\"'}
- className='post-image__name'
- target='_blank'
- >
- {trimmedFilename}
- </a>
- <div>
- <a
- href={fileUrl}
- download={filenameString}
- className='post-image__download'
- target='_blank'
- >
- <span
- className='fa fa-download'
- />
- </a>
- <span className='post-image__type'>{fileInfo.ext.toUpperCase()}</span>
- <span className='post-image__size'>{fileSizeString}</span>
- </div>
- </div>
- </div>
- );
- }
-}
-
-FileAttachment.propTypes = {
- intl: intlShape.isRequired,
-
- // a list of file pathes displayed by the parent FileAttachmentList
- filename: React.PropTypes.string.isRequired,
-
- // the index of this attachment preview in the parent FileAttachmentList
- index: React.PropTypes.number.isRequired,
-
- // handler for when the thumbnail is clicked passed the index above
- handleImageClick: React.PropTypes.func
-};
-
-export default injectIntl(FileAttachment);
diff --git a/web/react/components/file_attachment_list.jsx b/web/react/components/file_attachment_list.jsx
deleted file mode 100644
index da1b2ba3d..000000000
--- a/web/react/components/file_attachment_list.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ViewImageModal from './view_image.jsx';
-import FileAttachment from './file_attachment.jsx';
-import Constants from '../utils/constants.jsx';
-
-export default class FileAttachmentList extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleImageClick = this.handleImageClick.bind(this);
-
- this.state = {showPreviewModal: false, startImgId: 0};
- }
- handleImageClick(indexClicked) {
- this.setState({showPreviewModal: true, startImgId: indexClicked});
- }
- render() {
- var filenames = this.props.filenames;
-
- var postFiles = [];
- for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) {
- postFiles.push(
- <FileAttachment
- key={'file_attachment_' + i}
- filename={filenames[i]}
- index={i}
- handleImageClick={this.handleImageClick}
- />
- );
- }
-
- return (
- <div>
- <div className='post-image__columns'>
- {postFiles}
- </div>
- <ViewImageModal
- show={this.state.showPreviewModal}
- onModalDismissed={() => this.setState({showPreviewModal: false})}
- channelId={this.props.channelId}
- userId={this.props.userId}
- startId={this.state.startImgId}
- filenames={filenames}
- />
- </div>
- );
- }
-}
-
-FileAttachmentList.propTypes = {
-
- // a list of file pathes displayed by this
- filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
-
- // the channel that this is part of
- channelId: React.PropTypes.string,
-
- // the user that owns the post that this is attached to
- userId: React.PropTypes.string
-};
diff --git a/web/react/components/file_info_preview.jsx b/web/react/components/file_info_preview.jsx
deleted file mode 100644
index 30c3fc97f..000000000
--- a/web/react/components/file_info_preview.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- type: {
- id: 'file_info_preview.type',
- defaultMessage: 'File type '
- },
- size: {
- id: 'file_info_preview.size',
- defaultMessage: 'Size '
- }
-});
-
-export default function FileInfoPreview({filename, fileUrl, fileInfo, formatMessage}) {
- // non-image files include a section providing details about the file
- const infoParts = [];
-
- if (fileInfo.extension !== '') {
- infoParts.push(formatMessage(holders.type) + fileInfo.extension.toUpperCase());
- }
-
- infoParts.push(formatMessage(holders.size) + Utils.fileSizeToString(fileInfo.size));
-
- const infoString = infoParts.join(', ');
-
- const name = decodeURIComponent(Utils.getFileName(filename));
-
- return (
- <div className='file-details__container'>
- <a
- className={'file-details__preview'}
- href={fileUrl}
- target='_blank'
- >
- <span className='file-details__preview-helper'/>
- <img src={Utils.getPreviewImagePath(filename)}/>
- </a>
- <div className='file-details'>
- <div className='file-details__name'>{name}</div>
- <div className='file-details__info'>{infoString}</div>
- </div>
- </div>
- );
-}
-
-FileInfoPreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- formatMessage: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/file_preview.jsx b/web/react/components/file_preview.jsx
deleted file mode 100644
index 97949ca3d..000000000
--- a/web/react/components/file_preview.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-export default class FilePreview extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleRemove = this.handleRemove.bind(this);
- }
-
- componentDidUpdate() {
- if (this.props.uploadsInProgress.length > 0) {
- ReactDOM.findDOMNode(this.refs[this.props.uploadsInProgress[0]]).scrollIntoView();
- }
- }
-
- handleRemove(e) {
- var previewDiv = e.target.parentNode.parentNode;
-
- if (previewDiv.hasAttribute('data-filename')) {
- this.props.onRemove(previewDiv.getAttribute('data-filename'));
- } else if (previewDiv.hasAttribute('data-client-id')) {
- this.props.onRemove(previewDiv.getAttribute('data-client-id'));
- }
- }
-
- render() {
- var previews = [];
- this.props.files.forEach((fullFilename) => {
- var filename = fullFilename;
- var originalFilename = filename;
- var filenameSplit = filename.split('.');
- var ext = filenameSplit[filenameSplit.length - 1];
- var type = Utils.getFileType(ext);
-
- filename = Utils.getFileUrl(filename);
-
- if (type === 'image') {
- previews.push(
- <div
- key={filename}
- className='file-preview'
- data-filename={originalFilename}
- >
- <img
- className='file-preview__image'
- src={filename}
- />
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- } else {
- previews.push(
- <div
- key={filename}
- className='file-preview custom-file'
- data-filename={originalFilename}
- >
- <div className={'file-icon ' + Utils.getIconClassName(type)}/>
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- }
- });
-
- this.props.uploadsInProgress.forEach((clientId) => {
- previews.push(
- <div
- ref={clientId}
- key={clientId}
- className='file-preview'
- data-client-id={clientId}
- >
- <img
- className='spinner'
- src='/static/images/load.gif'
- />
- <a
- className='file-preview__remove'
- onClick={this.handleRemove}
- >
- <i className='glyphicon glyphicon-remove'/>
- </a>
- </div>
- );
- });
-
- return (
- <div className='file-preview__container'>
- {previews}
- </div>
- );
- }
-}
-
-FilePreview.defaultProps = {
- files: [],
- uploadsInProgress: []
-};
-FilePreview.propTypes = {
- onRemove: React.PropTypes.func.isRequired,
- files: React.PropTypes.array,
- uploadsInProgress: React.PropTypes.array
-};
diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx
deleted file mode 100644
index f8965e13e..000000000
--- a/web/react/components/file_upload.jsx
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- limited: {
- id: 'file_upload.limited',
- defaultMessage: 'Uploads limited to {count} files maximum. Please use additional posts for more files.'
- },
- filesAbove: {
- id: 'file_upload.filesAbove',
- defaultMessage: 'Files above {max}MB could not be uploaded: {filenames}'
- },
- fileAbove: {
- id: 'file_upload.fileAbove',
- defaultMessage: 'File above {max}MB could not be uploaded: {filename}'
- },
- pasted: {
- id: 'file_upload.pasted',
- defaultMessage: 'Image Pasted at '
- }
-});
-
-class FileUpload extends React.Component {
- constructor(props) {
- super(props);
-
- this.uploadFiles = this.uploadFiles.bind(this);
- this.handleChange = this.handleChange.bind(this);
- this.handleDrop = this.handleDrop.bind(this);
- this.cancelUpload = this.cancelUpload.bind(this);
-
- this.state = {
- requests: {}
- };
- }
-
- fileUploadSuccess(channelId, data) {
- this.props.onFileUpload(data.filenames, data.client_ids, channelId);
-
- var requests = this.state.requests;
- for (var j = 0; j < data.client_ids.length; j++) {
- delete requests[data.client_ids[j]];
- }
- this.setState({requests});
- }
-
- fileUploadFail(clientId, err) {
- this.props.onUploadError(err, clientId);
- }
-
- uploadFiles(files) {
- // clear any existing errors
- this.props.onUploadError(null);
-
- var channelId = this.props.channelId || ChannelStore.getCurrentId();
-
- var uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
- var numUploads = 0;
-
- // keep track of how many files have been too large
- var tooLargeFiles = [];
-
- for (let i = 0; i < files.length && numUploads < uploadsRemaining; i++) {
- if (files[i].size > Constants.MAX_FILE_SIZE) {
- tooLargeFiles.push(files[i]);
- continue;
- }
-
- // generate a unique id that can be used by other components to refer back to this upload
- const clientId = Utils.generateId();
-
- // prepare data to be uploaded
- var formData = new FormData();
- formData.append('channel_id', channelId);
- formData.append('files', files[i], files[i].name);
- formData.append('client_ids', clientId);
-
- var request = Client.uploadFile(formData,
- this.fileUploadSuccess.bind(this, channelId),
- this.fileUploadFail.bind(this, clientId)
- );
-
- var requests = this.state.requests;
- requests[clientId] = request;
- this.setState({requests});
-
- this.props.onUploadStart([clientId], channelId);
-
- numUploads += 1;
- }
-
- const {formatMessage} = this.props.intl;
- if (files.length > uploadsRemaining) {
- this.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
- } else if (tooLargeFiles.length > 1) {
- var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', ');
-
- this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filenames: tooLargeFilenames}));
- } else if (tooLargeFiles.length > 0) {
- this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filename: tooLargeFiles[0].name}));
- }
- }
-
- handleChange(e) {
- if (e.target.files.length > 0) {
- this.uploadFiles(e.target.files);
-
- Utils.clearFileInput(e.target);
- }
- }
-
- handleDrop(e) {
- this.props.onUploadError(null);
-
- var files = e.originalEvent.dataTransfer.files;
-
- if (typeof files !== 'string' && files.length) {
- this.uploadFiles(files);
- }
- }
-
- componentDidMount() {
- var inputDiv = ReactDOM.findDOMNode(this.refs.input);
- var self = this;
- const {formatMessage} = this.props.intl;
-
- if (this.props.postType === 'post') {
- $('.row.main').dragster({
- enter(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.center-file-overlay').removeClass('hidden');
- }
- },
- leave(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.center-file-overlay').addClass('hidden');
- }
- },
- drop(dragsterEvent, e) {
- $('.center-file-overlay').addClass('hidden');
- self.handleDrop(e);
- }
- });
- } else if (this.props.postType === 'comment') {
- $('.post-right__container').dragster({
- enter(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.right-file-overlay').removeClass('hidden');
- }
- },
- leave(dragsterEvent, e) {
- var files = e.originalEvent.dataTransfer;
-
- if (Utils.isFileTransfer(files)) {
- $('.right-file-overlay').addClass('hidden');
- }
- },
- drop(dragsterEvent, e) {
- $('.right-file-overlay').addClass('hidden');
- self.handleDrop(e);
- }
- });
- }
-
- document.addEventListener('paste', (e) => {
- if (!e.clipboardData) {
- return;
- }
-
- var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
-
- if (textarea !== e.target && !$.contains(textarea, e.target)) {
- return;
- }
-
- self.props.onUploadError(null);
-
- // This looks redundant, but must be done this way due to
- // setState being an asynchronous call
- var items = e.clipboardData.items;
- var numItems = 0;
- if (items) {
- for (let i = 0; i < items.length; i++) {
- if (items[i].type.indexOf('image') !== -1) {
- var testExt = items[i].type.split('/')[1].toLowerCase();
-
- if (Constants.IMAGE_TYPES.indexOf(testExt) < 0) {
- continue;
- }
-
- numItems++;
- }
- }
-
- var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - self.props.getFileCount(ChannelStore.getCurrentId()), numItems);
-
- if (numItems > numToUpload) {
- self.props.onUploadError(formatMessage(holders.limited, {count: Constants.MAX_UPLOAD_FILES}));
- }
-
- for (var i = 0; i < items.length && i < numToUpload; i++) {
- if (items[i].type.indexOf('image') !== -1) {
- var file = items[i].getAsFile();
-
- var ext = items[i].type.split('/')[1].toLowerCase();
-
- if (Constants.IMAGE_TYPES.indexOf(ext) < 0) {
- continue;
- }
-
- var channelId = self.props.channelId || ChannelStore.getCurrentId();
-
- // generate a unique id that can be used by other components to refer back to this file upload
- var clientId = Utils.generateId();
-
- var formData = new FormData();
- formData.append('channel_id', channelId);
- var d = new Date();
- var hour;
- if (d.getHours() < 10) {
- hour = '0' + d.getHours();
- } else {
- hour = String(d.getHours());
- }
- var min;
- if (d.getMinutes() < 10) {
- min = '0' + d.getMinutes();
- } else {
- min = String(d.getMinutes());
- }
-
- var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
- formData.append('files', file, name);
- formData.append('client_ids', clientId);
-
- var request = Client.uploadFile(formData,
- self.fileUploadSuccess.bind(self, channelId),
- self.fileUploadFail.bind(self, clientId)
- );
-
- var requests = self.state.requests;
- requests[clientId] = request;
- self.setState({requests});
-
- self.props.onUploadStart([clientId], channelId);
- }
- }
- }
- });
- }
-
- componentWillUnmount() {
- let target;
- if (this.props.postType === 'post') {
- target = $('.row.main');
- } else {
- target = $('.post-right__container');
- }
-
- // jquery-dragster doesn't provide a function to unregister itself so do it manually
- target.off('dragenter dragleave dragover drop dragster:enter dragster:leave dragster:over dragster:drop');
- }
-
- cancelUpload(clientId) {
- var requests = this.state.requests;
- var request = requests[clientId];
-
- if (request) {
- request.abort();
-
- delete requests[clientId];
- this.setState({requests});
- }
- }
-
- render() {
- let multiple = true;
- if (Utils.isMobileApp()) {
- // iOS WebViews don't upload videos properly in multiple mode
- multiple = false;
- }
-
- let accept = '';
- if (Utils.isIosChrome()) {
- // iOS Chrome can't upload videos at all
- accept = 'image/*';
- }
-
- return (
- <span
- ref='input'
- className='btn btn-file'
- >
- <span>
- <i className='glyphicon glyphicon-paperclip'/>
- </span>
- <input
- ref='fileInput'
- type='file'
- onChange={this.handleChange}
- onClick={this.props.onClick}
- multiple={multiple}
- accept={accept}
- />
- </span>
- );
- }
-}
-
-FileUpload.propTypes = {
- intl: intlShape.isRequired,
- onUploadError: React.PropTypes.func,
- getFileCount: React.PropTypes.func,
- onClick: React.PropTypes.func,
- onFileUpload: React.PropTypes.func,
- onUploadStart: React.PropTypes.func,
- onTextDrop: React.PropTypes.func,
- channelId: React.PropTypes.string,
- postType: React.PropTypes.string
-};
-
-export default injectIntl(FileUpload, {withRef: true});
diff --git a/web/react/components/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx
deleted file mode 100644
index 497d5aee2..000000000
--- a/web/react/components/file_upload_overlay.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class FileUploadOverlay extends React.Component {
- render() {
- var overlayClass = 'file-overlay hidden';
- if (this.props.overlayType === 'right') {
- overlayClass += ' right-file-overlay';
- } else if (this.props.overlayType === 'center') {
- overlayClass += ' center-file-overlay';
- }
-
- return (
- <div className={overlayClass}>
- <div className='overlay__indent'>
- <div className='overlay__circle'>
- <img
- className='overlay__files'
- src='/static/images/filesOverlay.png'
- alt='Files'
- />
- <span><i className='fa fa-upload'></i>
- <FormattedMessage
- id='upload_overlay.info'
- defaultMessage='Drop a file to upload it.'
- />
- </span>
- <img
- className='overlay__logo'
- src='/static/images/logoWhite.png'
- width='100'
- alt='Logo'
- />
- </div>
- </div>
- </div>
- );
- }
-}
-
-FileUploadOverlay.propTypes = {
- overlayType: React.PropTypes.string
-};
diff --git a/web/react/components/filtered_user_list.jsx b/web/react/components/filtered_user_list.jsx
deleted file mode 100644
index 77a8c8810..000000000
--- a/web/react/components/filtered_user_list.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserList from './user_list.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- member: {
- id: 'filtered_user_list.member',
- defaultMessage: 'Member'
- },
- search: {
- id: 'filtered_user_list.search',
- defaultMessage: 'Search members'
- }
-});
-
-class FilteredUserList extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleFilterChange = this.handleFilterChange.bind(this);
-
- this.state = {
- filter: ''
- };
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (prevState.filter !== this.state.filter) {
- $(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
- }
- }
-
- handleFilterChange(e) {
- this.setState({
- filter: e.target.value
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- let users = this.props.users;
-
- if (this.state.filter) {
- const filter = this.state.filter.toLowerCase();
-
- users = users.filter((user) => {
- return user.username.toLowerCase().indexOf(filter) !== -1 ||
- (user.first_name && user.first_name.toLowerCase().indexOf(filter) !== -1) ||
- (user.last_name && user.last_name.toLowerCase().indexOf(filter) !== -1) ||
- (user.nickname && user.nickname.toLowerCase().indexOf(filter) !== -1);
- });
- }
-
- let count;
- if (users.length === this.props.users.length) {
- count = (
- <FormattedMessage
- id='filtered_user_list.count'
- defaultMessage='{count} {count, plural,
- one {member}
- other {members}
- }'
- values={{
- count: users.length
- }}
- />
- );
- } else {
- count = (
- <FormattedMessage
- id='filtered_user_list.countTotal'
- defaultMessage='{count} {count, plural,
- one {member}
- other {members}
- } of {total} Total'
- values={{
- count: users.length,
- total: this.props.users.length
- }}
- />
- );
- }
-
- return (
- <div
- className='filtered-user-list'
- style={this.props.style}
- >
- <div className='filter-row'>
- <div className='col-sm-6'>
- <input
- ref='filter'
- className='form-control filter-textbox'
- placeholder={formatMessage(holders.search)}
- onInput={this.handleFilterChange}
- />
- </div>
- <div className='col-sm-6'>
- <span className='member-count'>{count}</span>
- </div>
- </div>
- <div
- ref='userList'
- className='more-modal__list'
- >
- <UserList
- users={users}
- actions={this.props.actions}
- />
- </div>
- </div>
- );
- }
-}
-
-FilteredUserList.defaultProps = {
- users: [],
- actions: []
-};
-
-FilteredUserList.propTypes = {
- intl: intlShape.isRequired,
- users: React.PropTypes.arrayOf(React.PropTypes.object),
- actions: React.PropTypes.arrayOf(React.PropTypes.func),
- style: React.PropTypes.object
-};
-
-export default injectIntl(FilteredUserList);
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
deleted file mode 100644
index dd21f21e0..000000000
--- a/web/react/components/get_link_modal.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class GetLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.onHide = this.onHide.bind(this);
-
- this.copyLink = this.copyLink.bind(this);
-
- this.state = {
- copiedLink: false
- };
- }
-
- onHide() {
- this.setState({copiedLink: false});
-
- this.props.onHide();
- }
-
- copyLink() {
- var copyTextarea = $(ReactDOM.findDOMNode(this.refs.textarea));
- copyTextarea.select();
-
- try {
- var successful = document.execCommand('copy');
- if (successful) {
- this.setState({copiedLink: true});
- } else {
- this.setState({copiedLink: false});
- }
- } catch (err) {
- this.setState({copiedLink: false});
- }
- }
-
- render() {
- let helpText = null;
- if (this.props.helpText) {
- helpText = (
- <p>
- {this.props.helpText}
- <br/>
- <br/>
- </p>
- );
- }
-
- let copyLink = null;
- if (document.queryCommandSupported('copy')) {
- copyLink = (
- <button
- data-copy-btn='true'
- type='button'
- className='btn btn-primary pull-left'
- onClick={this.copyLink}
- >
- <FormattedMessage
- id='get_link.copy'
- defaultMessage='Copy Link'
- />
- </button>
- );
- }
-
- const linkText = (
- <textarea
- className='form-control no-resize min-height'
- readOnly='true'
- ref='textarea'
- value={this.props.link}
- />
- );
-
- var copyLinkConfirm = null;
- if (this.state.copiedLink) {
- copyLinkConfirm = (
- <p className='alert alert-success alert--confirm'>
- <i className='fa fa-check'></i>
- <FormattedMessage
- id='get_link.clipboard'
- defaultMessage=' Link copied to clipboard.'
- />
- </p>
- );
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.onHide}
- >
- <Modal.Header closeButton={true}>
- <h4 className='modal-title'>{this.props.title}</h4>
- </Modal.Header>
- <Modal.Body>
- {helpText}
- {linkText}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.onHide}
- >
- <FormattedMessage
- id='get_link.close'
- defaultMessage='Close'
- />
- </button>
- {copyLink}
- {copyLinkConfirm}
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-GetLinkModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- title: React.PropTypes.string.isRequired,
- helpText: React.PropTypes.string,
- link: React.PropTypes.string.isRequired
-};
-
-GetLinkModal.defaultProps = {
- helpText: null
-};
diff --git a/web/react/components/get_post_link_modal.jsx b/web/react/components/get_post_link_modal.jsx
deleted file mode 100644
index b7b2a8e7c..000000000
--- a/web/react/components/get_post_link_modal.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import GetLinkModal from './get_link_modal.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- title: {
- id: 'get_post_link_modal.title',
- defaultMessage: 'Copy Permalink'
- },
- help: {
- id: 'get_post_link_modal.help',
- defaultMessage: 'The link below allows authorized users to see your post.'
- }
-});
-
-class GetPostLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
-
- this.hide = this.hide.bind(this);
-
- this.state = {
- show: false,
- post: {}
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_POST_LINK_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_POST_LINK_MODAL, this.handleToggle);
- }
-
- handleToggle(value, args) {
- this.setState({
- show: value,
- post: args.post
- });
- }
-
- hide() {
- this.setState({
- show: false
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- return (
- <GetLinkModal
- show={this.state.show}
- onHide={this.hide}
- title={formatMessage(holders.title)}
- helpText={formatMessage(holders.help)}
- link={TeamStore.getCurrentTeamUrl() + '/pl/' + this.state.post.id}
- />
- );
- }
-}
-
-GetPostLinkModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(GetPostLinkModal);
diff --git a/web/react/components/get_team_invite_link_modal.jsx b/web/react/components/get_team_invite_link_modal.jsx
deleted file mode 100644
index ba6164dbf..000000000
--- a/web/react/components/get_team_invite_link_modal.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import GetLinkModal from './get_link_modal.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- title: {
- id: 'get_team_invite_link_modal.title',
- defaultMessage: 'Team Invite Link'
- },
- help: {
- id: 'get_team_invite_link_modal.help',
- defaultMessage: 'Send teammates the link below for them to sign-up to this team site. The Team Invite Link can be shared with multiple teammates as it does not change unless it\'s regenerated in Team Settings by a Team Admin.'
- },
- helpDisabled: {
- id: 'get_team_invite_link_modal.helpDisabled',
- defaultMessage: 'User creation has been disabled for your team. Please ask your team administrator for details.'
- }
-});
-
-class GetTeamInviteLinkModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
-
- this.state = {
- show: false
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(Constants.ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL, this.handleToggle);
- }
-
- handleToggle(value) {
- this.setState({
- show: value
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
-
- let helpText = formatMessage(holders.helpDisabled);
-
- if (global.window.mm_config.EnableUserCreation === 'true') {
- helpText = formatMessage(holders.help);
- }
-
- return (
- <GetLinkModal
- show={this.state.show}
- onHide={() => this.setState({show: false})}
- title={formatMessage(holders.title)}
- helpText={helpText}
- link={TeamStore.getCurrentInviteLink()}
- />
- );
- }
-}
-
-GetTeamInviteLinkModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(GetTeamInviteLinkModal);
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
deleted file mode 100644
index 71cd5b8b6..000000000
--- a/web/react/components/invite_member_modal.jsx
+++ /dev/null
@@ -1,515 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-import * as Client from '../utils/client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import ConfirmModal from './confirm_modal.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- emailError: {
- id: 'invite_member.emailError',
- defaultMessage: 'Please enter a valid email address'
- },
- firstname: {
- id: 'invite_member.firstname',
- defaultMessage: 'First name'
- },
- lastname: {
- id: 'invite_member.lastname',
- defaultMessage: 'Last name'
- },
- modalTitle: {
- id: 'invite_member.modalTitle',
- defaultMessage: 'Discard Invitations?'
- },
- modalMessage: {
- id: 'invite_member.modalMessage',
- defaultMessage: 'You have unsent invitations, are you sure you want to discard them?'
- },
- modalButton: {
- id: 'invite_member.modalButton',
- defaultMessage: 'Yes, Discard'
- }
-});
-
-class InviteMemberModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleToggle = this.handleToggle.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleHide = this.handleHide.bind(this);
- this.addInviteFields = this.addInviteFields.bind(this);
- this.clearFields = this.clearFields.bind(this);
- this.removeInviteFields = this.removeInviteFields.bind(this);
- this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
-
- this.state = {
- show: false,
- inviteIds: [0],
- idCount: 0,
- emailErrors: {},
- firstNameErrors: {},
- lastNameErrors: {},
- emailEnabled: global.window.mm_config.SendEmailNotifications === 'true',
- userCreationEnabled: global.window.mm_config.EnableUserCreation === 'true',
- showConfirmModal: false,
- isSendingEmails: false
- };
- }
-
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle);
- }
-
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_INVITE_MEMBER_MODAL, this.handleToggle);
- }
-
- handleToggle(value) {
- this.setState({
- show: value
- });
- }
-
- handleSubmit() {
- if (!this.state.emailEnabled) {
- return;
- }
-
- var inviteIds = this.state.inviteIds;
- var count = inviteIds.length;
- var invites = [];
- var emailErrors = this.state.emailErrors;
- var firstNameErrors = this.state.firstNameErrors;
- var lastNameErrors = this.state.lastNameErrors;
- var valid = true;
-
- for (var i = 0; i < count; i++) {
- var index = inviteIds[i];
- var invite = {};
- invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
- if (!invite.email || !utils.isEmail(invite.email)) {
- emailErrors[index] = this.props.intl.formatMessage(holders.emailError);
- valid = false;
- } else {
- emailErrors[index] = '';
- }
-
- invite.firstName = ReactDOM.findDOMNode(this.refs['first_name' + index]).value.trim();
-
- invite.lastName = ReactDOM.findDOMNode(this.refs['last_name' + index]).value.trim();
-
- invites.push(invite);
- }
-
- this.setState({emailErrors: emailErrors, firstNameErrors: firstNameErrors, lastNameErrors: lastNameErrors});
-
- if (!valid || invites.length === 0) {
- return;
- }
-
- var data = {};
- data.invites = invites;
-
- this.setState({isSendingEmails: true});
-
- Client.inviteMembers(
- data,
- () => {
- this.handleHide(false);
- this.setState({isSendingEmails: false});
- },
- (err) => {
- if (err.id === 'api.team.invite_members.already.app_error') {
- emailErrors[err.detailed_error] = err.message;
- this.setState({emailErrors: emailErrors});
- } else {
- this.setState({serverError: err.message});
- }
-
- this.setState({isSendingEmails: false});
- }
- );
- }
-
- handleHide(requireConfirm) {
- if (requireConfirm) {
- var notEmpty = false;
- for (var i = 0; i < this.state.inviteIds.length; i++) {
- var index = this.state.inviteIds[i];
- if (ReactDOM.findDOMNode(this.refs['email' + index]).value.trim() !== '') {
- notEmpty = true;
- break;
- }
- }
-
- if (notEmpty) {
- this.setState({
- showConfirmModal: true
- });
-
- return;
- }
- }
-
- this.clearFields();
-
- this.setState({
- show: false,
- showConfirmModal: false
- });
- }
-
- componentDidUpdate(prevProps, prevState) {
- if (!prevState.show && this.state.show) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
- }
- }
- }
-
- addInviteFields() {
- var count = this.state.idCount + 1;
- var inviteIds = this.state.inviteIds;
- inviteIds.push(count);
- this.setState({inviteIds: inviteIds, idCount: count});
- }
-
- clearFields() {
- var inviteIds = this.state.inviteIds;
-
- for (var i = 0; i < inviteIds.length; i++) {
- var index = inviteIds[i];
- ReactDOM.findDOMNode(this.refs['email' + index]).value = '';
- ReactDOM.findDOMNode(this.refs['first_name' + index]).value = '';
- ReactDOM.findDOMNode(this.refs['last_name' + index]).value = '';
- }
-
- this.setState({
- inviteIds: [0],
- idCount: 0,
- emailErrors: {},
- firstNameErrors: {},
- lastNameErrors: {}
- });
- }
-
- removeInviteFields(index) {
- var count = this.state.idCount;
- var inviteIds = this.state.inviteIds;
- var i = inviteIds.indexOf(index);
- if (i > -1) {
- inviteIds.splice(i, 1);
- }
- if (!inviteIds.length) {
- inviteIds.push(++count);
- }
- this.setState({inviteIds: inviteIds, idCount: count});
- }
-
- showGetTeamInviteLinkModal() {
- this.handleHide(false);
-
- GlobalActions.showGetTeamInviteLinkModal();
- }
-
- render() {
- var currentUser = UserStore.getCurrentUser();
- const {formatMessage} = this.props.intl;
-
- if (currentUser != null) {
- var inviteSections = [];
- var inviteIds = this.state.inviteIds;
- for (var i = 0; i < inviteIds.length; i++) {
- var index = inviteIds[i];
- var emailError = null;
- if (this.state.emailErrors[index]) {
- emailError = <label className='control-label'>{this.state.emailErrors[index]}</label>;
- }
- var firstNameError = null;
- if (this.state.firstNameErrors[index]) {
- firstNameError = <label className='control-label'>{this.state.firstNameErrors[index]}</label>;
- }
- var lastNameError = null;
- if (this.state.lastNameErrors[index]) {
- lastNameError = <label className='control-label'>{this.state.lastNameErrors[index]}</label>;
- }
-
- var removeButton = null;
- if (index) {
- removeButton = (<div>
- <button
- type='button'
- className='btn btn-link remove__member'
- onClick={this.removeInviteFields.bind(this, index)}
- >
- <span className='fa fa-trash'></span>
- </button>
- </div>);
- }
- var emailClass = 'form-group invite';
- if (emailError) {
- emailClass += ' has-error';
- }
-
- var nameFields = null;
-
- var firstNameClass = 'form-group';
- if (firstNameError) {
- firstNameClass += ' has-error';
- }
- var lastNameClass = 'form-group';
- if (lastNameError) {
- lastNameClass += ' has-error';
- }
- nameFields = (<div className='row--invite'>
- <div className='col-sm-6'>
- <div className={firstNameClass}>
- <input
- type='text'
- className='form-control'
- ref={'first_name' + index}
- placeholder={formatMessage(holders.firstname)}
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {firstNameError}
- </div>
- </div>
- <div className='col-sm-6'>
- <div className={lastNameClass}>
- <input
- type='text'
- className='form-control'
- ref={'last_name' + index}
- placeholder={formatMessage(holders.lastname)}
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {lastNameError}
- </div>
- </div>
- </div>);
-
- inviteSections[index] = (
- <div key={'key' + index}>
- {removeButton}
- <div className={emailClass}>
- <input
- onKeyUp={this.displayNameKeyUp}
- type='text'
- ref={'email' + index}
- className='form-control'
- placeholder='email@domain.com'
- maxLength='64'
- disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
- spellCheck='false'
- />
- {emailError}
- </div>
- {nameFields}
- </div>
- );
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var content = null;
- var sendButton = null;
-
- var defaultChannelName = '';
- if (ChannelStore.getByName(Constants.DEFAULT_CHANNEL)) {
- defaultChannelName = ChannelStore.getByName(Constants.DEFAULT_CHANNEL).display_name;
- }
-
- if (this.state.emailEnabled && this.state.userCreationEnabled) {
- content = (
- <div>
- {serverError}
- <button
- type='button'
- className='btn btn-default'
- onClick={this.addInviteFields}
- >
- <FormattedMessage
- id='invite_member.addAnother'
- defaultMessage='Add another'
- />
- </button>
- <br/>
- <br/>
- <span>
- <FormattedHTMLMessage
- id='invite_member.autoJoin'
- defaultMessage='People invited automatically join the <strong>{channel}</strong> channel.'
- values={{
- channel: defaultChannelName
- }}
- />
- </span>
- </div>
- );
-
- var sendButtonLabel = (
- <FormattedMessage
- id='invite_member.send'
- defaultMessage='Send Invitation'
- />
- );
- if (this.state.isSendingEmails) {
- sendButtonLabel = (
- <span><i className='fa fa-spinner fa-spin'/>
- <FormattedMessage
- id='invite_member.sending'
- defaultMessage=' Sending'
- />
- </span>
- );
- } else if (this.state.inviteIds.length > 1) {
- sendButtonLabel = (
- <FormattedMessage
- id='invite_member.send2'
- defaultMessage='Send Invitations'
- />
- );
- }
-
- sendButton = (
- <button
- onClick={this.handleSubmit}
- type='button'
- className='btn btn-primary'
- disabled={this.state.isSendingEmails}
- >
- {sendButtonLabel}
- </button>
- );
- } else if (this.state.userCreationEnabled) {
- var teamInviteLink = null;
- if (currentUser && TeamStore.getCurrent().type === 'O') {
- var link = (
- <a
- href='#'
- onClick={this.showGetTeamInviteLinkModal}
- >
- <FormattedMessage
- id='invite_member.inviteLink'
- defaultMessage='Team Invite Link'
- />
- </a>
- );
-
- teamInviteLink = (
- <p>
- <FormattedMessage
- id='invite_member.teamInviteLink'
- defaultMessage='You can also invite people using the {link}.'
- values={{
- link: (link)
- }}
- />
- </p>
- );
- }
-
- content = (
- <div>
- <p>
- <FormattedMessage
- id='invite_member.content'
- defaultMessage='Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'
- />
- </p>
- {teamInviteLink}
- </div>
- );
- } else {
- content = (
- <div>
- <p>
- <FormattedMessage
- id='invite_member.disabled'
- defaultMessage='User creation has been disabled for your team. Please ask your team administrator for details.'
- />
- </p>
- </div>
- );
- }
-
- return (
- <div>
- <Modal
- dialogClassName='modal-invite-member'
- show={this.state.show}
- onHide={this.handleHide.bind(this, true)}
- enforceFocus={!this.state.showConfirmModal}
- backdrop={this.state.isSendingEmails ? 'static' : true}
- >
- <Modal.Header closeButton={!this.state.isSendingEmails}>
- <Modal.Title>
- <FormattedMessage
- id='invite_member.newMember'
- defaultMessage='Invite New Member'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <form role='form'>
- {inviteSections}
- </form>
- {content}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide.bind(this, true)}
- disabled={this.state.isSendingEmails}
- >
- <FormattedMessage
- id='invite_member.cancel'
- defaultMessage='Cancel'
- />
- </button>
- {sendButton}
- </Modal.Footer>
- </Modal>
- <ConfirmModal
- title={formatMessage(holders.modalTitle)}
- message={formatMessage(holders.modalMessage)}
- confirmButton={formatMessage(holders.modalButton)}
- show={this.state.showConfirmModal}
- onConfirm={this.handleHide.bind(this, false)}
- onCancel={() => this.setState({showConfirmModal: false})}
- />
- </div>
- );
- }
-
- return null;
- }
-}
-
-InviteMemberModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(InviteMemberModal);
diff --git a/web/react/components/loading_screen.jsx b/web/react/components/loading_screen.jsx
deleted file mode 100644
index 143b94467..000000000
--- a/web/react/components/loading_screen.jsx
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class LoadingScreen extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- return (
- <div
- className='loading-screen'
- style={{position: this.props.position}}
- >
- <div className='loading__content'>
- <h3>
- <FormattedMessage
- id='loading_screen.loading'
- defaultMessage='Loading'
- />
- </h3>
- <div className='round round-1'></div>
- <div className='round round-2'></div>
- <div className='round round-3'></div>
- </div>
- </div>
- );
- }
-}
-
-LoadingScreen.defaultProps = {
- position: 'relative'
-};
-LoadingScreen.propTypes = {
- position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit'])
-};
diff --git a/web/react/components/logged_in.jsx b/web/react/components/logged_in.jsx
deleted file mode 100644
index 1ed3694e9..000000000
--- a/web/react/components/logged_in.jsx
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SocketStore from '../stores/socket_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-import ErrorBar from '../components/error_bar.jsx';
-
-import {browserHistory} from 'react-router';
-
-import SidebarRight from '../components/sidebar_right.jsx';
-import SidebarRightMenu from '../components/sidebar_right_menu.jsx';
-
-// Modals
-import GetPostLinkModal from '../components/get_post_link_modal.jsx';
-import GetTeamInviteLinkModal from '../components/get_team_invite_link_modal.jsx';
-import EditPostModal from '../components/edit_post_modal.jsx';
-import DeletePostModal from '../components/delete_post_modal.jsx';
-import MoreChannelsModal from '../components/more_channels.jsx';
-import TeamSettingsModal from '../components/team_settings_modal.jsx';
-import RemovedFromChannelModal from '../components/removed_from_channel_modal.jsx';
-import RegisterAppModal from '../components/register_app_modal.jsx';
-import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
-import InviteMemberModal from '../components/invite_member_modal.jsx';
-import SelectTeamModal from '../components/admin_console/select_team_modal.jsx';
-
-const CLIENT_STATUS_INTERVAL = 30000;
-const BACKSPACE_CHAR = 8;
-
-export default class LoggedIn extends React.Component {
- constructor(params) {
- super(params);
-
- this.onUserChanged = this.onUserChanged.bind(this);
- }
- onUserChanged() {
- // Grab the current user
- const user = UserStore.getCurrentUser();
-
- // Update segment indentify
- if (global.window.mm_config.SegmentDeveloperKey != null && global.window.mm_config.SegmentDeveloperKey !== '') {
- global.window.analytics.identify(user.id, {
- name: user.nickname,
- email: user.email,
- createdAt: user.create_at,
- username: user.username,
- team_id: user.team_id,
- id: user.id
- });
- }
-
- // Update CSS classes to match user theme
- if (user) {
- if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
- Utils.applyTheme(user.theme_props);
- } else {
- Utils.applyTheme(Constants.THEMES.default);
- }
- }
- }
- onSocketChange(msg) {
- if (msg && msg.user_id && msg.user_id !== UserStore.getCurrentId()) {
- UserStore.setStatus(msg.user_id, 'online');
- }
- }
- componentWillMount() {
- // Emit view action
- GlobalActions.viewLoggedIn();
-
- // Listen for user
- UserStore.addChangeListener(this.onUserChanged);
-
- // Add listner for socker store
- SocketStore.addChangeListener(this.onSocketChange);
-
- // Get all statuses regularally. (Soon to be switched to websocket)
- this.intervalId = setInterval(() => AsyncClient.getStatuses(), CLIENT_STATUS_INTERVAL);
-
- // Force logout of all tabs if one tab is logged out
- $(window).bind('storage', (e) => {
- // when one tab on a browser logs out, it sets __logout__ in localStorage to trigger other tabs to log out
- if (e.originalEvent.key === '__logout__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
- // make sure it isn't this tab that is sending the logout signal (only necessary for IE11)
- if (window.BrowserStore.isSignallingLogout(e.originalEvent.newValue)) {
- return;
- }
-
- console.log('detected logout from a different tab'); //eslint-disable-line no-console
- browserHistory.push('/' + this.props.params.team);
- }
-
- if (e.originalEvent.key === '__login__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
- // make sure it isn't this tab that is sending the logout signal (only necessary for IE11)
- if (window.BrowserStore.isSignallingLogin(e.originalEvent.newValue)) {
- return;
- }
-
- console.log('detected login from a different tab'); //eslint-disable-line no-console
- location.reload();
- }
- });
-
- // Because current CSS requires the root tag to have specific stuff
- $('#root').attr('class', 'channel-view');
-
- // ???
- $('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
- $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
- } else {
- $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
- $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
- }
- });
-
- $('body').on('mouseenter mouseleave', '.search-item__container .post', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).closest('.search-item__container').find('.date-separator').addClass('hovered--after');
- $(this).closest('.search-item__container').next('div').find('.date-separator').addClass('hovered--before');
- } else {
- $(this).closest('.search-item__container').find('.date-separator').removeClass('hovered--after');
- $(this).closest('.search-item__container').next('div').find('.date-separator').removeClass('hovered--before');
- }
- });
-
- $('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(ev) {
- if (ev.type === 'mouseenter') {
- $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
- $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--comment');
- } else {
- $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--comment');
- $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--comment');
- }
- });
-
- // Device tracking setup
- var iOS = (/(iPad|iPhone|iPod)/g).test(navigator.userAgent);
- if (iOS) {
- $('body').addClass('ios');
- }
-
- // Set up tracking for whether the window is active
- window.isActive = true;
- $(window).on('focus', () => {
- AsyncClient.updateLastViewedAt();
- ChannelStore.resetCounts(ChannelStore.getCurrentId());
- ChannelStore.emitChange();
- window.isActive = true;
- });
- $(window).on('blur', () => {
- window.isActive = false;
- });
-
- // if preferences have already been stored in local storage do not wait until preference store change is fired and handled in channel.jsx
- const selectedFont = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', Constants.DEFAULT_FONT);
- Utils.applyFont(selectedFont);
-
- // Pervent backspace from navigating back a page
- $(window).on('keydown.preventBackspace', (e) => {
- if (e.which === BACKSPACE_CHAR && !$(e.target).is('input, textarea')) {
- e.preventDefault();
- }
- });
- }
- componentWillUnmount() {
- $('#root').attr('class', '');
- clearInterval(this.intervalId);
-
- $(window).off('focus');
- $(window).off('blur');
-
- SocketStore.removeChangeListener(this.onSocketChange);
- UserStore.removeChangeListener(this.onUserChanged);
-
- $('body').off('click.userpopover');
- $('body').off('mouseenter mouseleave', '.post');
- $('body').off('mouseenter mouseleave', '.post.post--comment.same--root');
-
- $('.modal').off('show.bs.modal');
-
- $(window).off('keydown.preventBackspace');
- }
- render() {
- return (
- <div className='channel-view'>
- <ErrorBar/>
- <div className='container-fluid'>
- <SidebarRight/>
- <SidebarRightMenu/>
- {this.props.sidebar}
- {this.props.center}
-
- <GetPostLinkModal/>
- <GetTeamInviteLinkModal/>
- <InviteMemberModal/>
- <ImportThemeModal/>
- <TeamSettingsModal/>
- <MoreChannelsModal/>
- <EditPostModal/>
- <DeletePostModal/>
- <RemovedFromChannelModal/>
- <RegisterAppModal/>
- <SelectTeamModal/>
- </div>
- </div>
- );
- }
-}
-
-LoggedIn.defaultProps = {
-};
-
-LoggedIn.propTypes = {
- children: React.PropTypes.object,
- sidebar: React.PropTypes.object,
- center: React.PropTypes.object,
- params: React.PropTypes.object
-};
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
deleted file mode 100644
index d3ee35082..000000000
--- a/web/react/components/login.jsx
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoginEmail from './login_email.jsx';
-import LoginUsername from './login_username.jsx';
-import LoginLdap from './login_ldap.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-export default class Login extends React.Component {
- constructor(props) {
- super(props);
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onTeamChange = this.onTeamChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- Client.getMeLoggedIn((data) => {
- if (data && data.logged_in !== 'false') {
- browserHistory.push('/' + this.props.params.team + '/channels/town-square');
- }
- });
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- getStateFromStores() {
- return {
- currentTeam: TeamStore.getByName(this.props.params.team)
- };
- }
- onTeamChange() {
- this.setState(this.getStateFromStores());
- }
- render() {
- const currentTeam = this.state.currentTeam;
- if (currentTeam == null) {
- return <div/>;
- }
-
- const teamDisplayName = currentTeam.display_name;
- const teamName = currentTeam.name;
- const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
- const usernameSigninEnabled = global.window.mm_config.EnableSignInWithUsername === 'true';
-
- let loginMessage = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- loginMessage.push(
- <a
- className='btn btn-custom-login gitlab'
- key='gitlab'
- href={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='login.gitlab'
- defaultMessage='with GitLab'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- loginMessage.push(
- <a
- className='btn btn-custom-login google'
- key='google'
- href={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='login.google'
- defaultMessage='with Google Apps'
- />
- </span>
- </a>
- );
- }
-
- const extraParam = Utils.getUrlParameter('extra');
- let extraBox = '';
- if (extraParam) {
- if (extraParam === Constants.SIGNIN_CHANGE) {
- extraBox = (
- <div className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='login.changed'
- defaultMessage=' Sign-in method changed successfully'
- />
- </div>
- );
- } else if (extraParam === Constants.SIGNIN_VERIFIED) {
- extraBox = (
- <div className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='login.verified'
- defaultMessage=' Email Verified'
- />
- </div>
- );
- } else if (extraParam === Constants.SESSION_EXPIRED) {
- extraBox = (
- <div className='alert alert-warning'>
- <i className='fa fa-exclamation-triangle'/>
- <FormattedMessage
- id='login.session_expired'
- defaultMessage=' Your session has expired. Please login again.'
- />
- </div>
- );
- }
- }
-
- let emailSignup;
- if (global.window.mm_config.EnableSignInWithEmail === 'true') {
- emailSignup = (
- <LoginEmail
- teamName={teamName}
- />
- );
- }
-
- if (loginMessage.length > 0 && emailSignup) {
- loginMessage = (
- <div>
- {loginMessage}
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- </div>
- );
- }
-
- let forgotPassword;
- if (emailSignup) {
- forgotPassword = (
- <div className='form-group'>
- <a href={'/' + teamName + '/reset_password'}>
- <FormattedMessage
- id='login.forgot'
- defaultMessage='I forgot my password'
- />
- </a>
- </div>
- );
- }
-
- let userSignUp = null;
- if (currentTeam.allow_open_invite) {
- userSignUp = (
- <div>
- <span>
- <FormattedMessage
- id='login.noAccount'
- defaultMessage="Don't have an account? "
- />
- <a
- href={'/signup_user_complete/?id=' + currentTeam.invite_id}
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.create'
- defaultMessage='Create one now'
- />
- </a>
- </span>
- </div>
- );
- }
-
- let teamSignUp = null;
- if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
- teamSignUp = (
- <div className='margin--extra'>
- <a
- href='/'
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.createTeam'
- defaultMessage='Create a new team'
- />
- </a>
- </div>
- );
- }
-
- let ldapLogin = null;
- if (global.window.mm_config.EnableLdap === 'true') {
- ldapLogin = (
- <LoginLdap
- teamName={teamName}
- />
- );
- }
-
- if (ldapEnabled && (loginMessage.length > 0 || emailSignup || usernameSigninEnabled)) {
- ldapLogin = (
- <div>
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- <LoginLdap
- teamName={teamName}
- />
- </div>
- );
- }
-
- let usernameLogin = null;
- if (global.window.mm_config.EnableSignInWithUsername === 'true') {
- usernameLogin = (
- <LoginUsername
- teamName={teamName}
- />
- );
- }
-
- if (usernameSigninEnabled && (loginMessage.length > 0 || emailSignup || ldapEnabled)) {
- usernameLogin = (
- <div>
- <div className='or__container'>
- <FormattedMessage
- id='login.or'
- defaultMessage='or'
- />
- </div>
- <LoginUsername
- teamName={teamName}
- />
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h5 className='margin--less'>
- <FormattedMessage
- id='login.signTo'
- defaultMessage='Sign in to:'
- />
- </h5>
- <h2 className='signup-team__name'>{teamDisplayName}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='login.on'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
- {extraBox}
- {loginMessage}
- {emailSignup}
- {usernameLogin}
- {ldapLogin}
- {userSignUp}
- {forgotPassword}
- {teamSignUp}
- </div>
- </div>
- </div>
- );
- }
-}
-
-Login.defaultProps = {
-};
-Login.propTypes = {
- params: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/login_email.jsx b/web/react/components/login_email.jsx
deleted file mode 100644
index 3e0d8919d..000000000
--- a/web/react/components/login_email.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-import {browserHistory} from 'react-router';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- badTeam: {
- id: 'login_email.badTeam',
- defaultMessage: 'Bad team name'
- },
- emailReq: {
- id: 'login_email.emailReq',
- defaultMessage: 'An email is required'
- },
- pwdReq: {
- id: 'login_email.pwdReq',
- defaultMessage: 'A password is required'
- },
- email: {
- id: 'login_email.email',
- defaultMessage: 'Email'
- },
- pwd: {
- id: 'login_email.pwd',
- defaultMessage: 'Password'
- }
-});
-
-class LoginEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const name = this.props.teamName;
- if (!name) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const email = this.refs.email.value.trim();
- if (!email) {
- state.serverError = formatMessage(holders.emailReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByEmail(name, email, password,
- () => {
- UserStore.setLastEmail(email);
- browserHistory.push('/' + name + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- window.location.href = '/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email);
- return;
- }
- state.serverError = err.message;
- this.valid = false;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
-
- let priorEmail = UserStore.getLastEmail();
- let focusEmail = false;
- let focusPassword = false;
- if (priorEmail === '') {
- focusEmail = true;
- } else {
- focusPassword = true;
- }
-
- const emailParam = Utils.getUrlParameter('email');
- if (emailParam) {
- priorEmail = decodeURIComponent(emailParam);
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusEmail}
- type='email'
- className='form-control'
- name='email'
- defaultValue={priorEmail}
- ref='email'
- placeholder={formatMessage(holders.email)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusPassword}
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_email.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginEmail.defaultProps = {
-};
-
-LoginEmail.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginEmail);
diff --git a/web/react/components/login_ldap.jsx b/web/react/components/login_ldap.jsx
deleted file mode 100644
index d67f15fa5..000000000
--- a/web/react/components/login_ldap.jsx
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- badTeam: {
- id: 'login_ldap.badTeam',
- defaultMessage: 'Bad team name'
- },
- idReq: {
- id: 'login_ldap.idlReq',
- defaultMessage: 'An LDAP ID is required'
- },
- pwdReq: {
- id: 'login_ldap.pwdReq',
- defaultMessage: 'An LDAP password is required'
- },
- username: {
- id: 'login_ldap.username',
- defaultMessage: 'LDAP Username'
- },
- pwd: {
- id: 'login_ldap.pwd',
- defaultMessage: 'LDAP Password'
- }
-});
-
-class LoginLdap extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const teamName = this.props.teamName;
- if (!teamName) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const id = this.refs.id.value.trim();
- if (!id) {
- state.serverError = formatMessage(holders.idReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByLdap(teamName, id, password,
- () => {
- const redirect = Utils.getUrlParameter('redirect');
- if (redirect) {
- window.location.href = decodeURIComponent(redirect);
- } else {
- window.location.href = '/' + teamName + '/channels/town-square';
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={true}
- className='form-control'
- ref='id'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- type='password'
- className='form-control'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_ldap.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginLdap.defaultProps = {
-};
-
-LoginLdap.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginLdap); \ No newline at end of file
diff --git a/web/react/components/login_username.jsx b/web/react/components/login_username.jsx
deleted file mode 100644
index 4bd9254c6..000000000
--- a/web/react/components/login_username.jsx
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-var holders = defineMessages({
- badTeam: {
- id: 'login_username.badTeam',
- defaultMessage: 'Bad team name'
- },
- usernameReq: {
- id: 'login_username.usernameReq',
- defaultMessage: 'A username is required'
- },
- pwdReq: {
- id: 'login_username.pwdReq',
- defaultMessage: 'A password is required'
- },
- verifyEmailError: {
- id: 'login_username.verifyEmailError',
- defaultMessage: 'Please verify your email address. Check your inbox for an email.'
- },
- userNotFoundError: {
- id: 'login_username.userNotFoundError',
- defaultMessage: "We couldn't find an existing account matching your username for this team."
- },
- username: {
- id: 'login_username.username',
- defaultMessage: 'Username'
- },
- pwd: {
- id: 'login_username.pwd',
- defaultMessage: 'Password'
- }
-});
-
-export default class LoginUsername extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {
- serverError: ''
- };
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var state = {};
-
- const name = this.props.teamName;
- if (!name) {
- state.serverError = formatMessage(holders.badTeam);
- this.setState(state);
- return;
- }
-
- const username = this.refs.username.value.trim();
- if (!username) {
- state.serverError = formatMessage(holders.usernameReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- Client.loginByUsername(name, username, password,
- () => {
- UserStore.setLastUsername(username);
-
- const redirect = Utils.getUrlParameter('redirect');
- if (redirect) {
- window.location.href = decodeURIComponent(redirect);
- } else {
- window.location.href = '/' + name + '/channels/town-square';
- }
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- state.serverError = formatMessage(holders.verifyEmailError);
- } else if (err.id === 'store.sql_user.get_by_username.app_error') {
- state.serverError = formatMessage(holders.userNotFoundError);
- } else {
- state.serverError = err.message;
- }
-
- this.valid = false;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError;
- let errorClass = '';
- if (this.state.serverError) {
- serverError = <label className='control-label'>{this.state.serverError}</label>;
- errorClass = ' has-error';
- }
-
- let priorUsername = UserStore.getLastUsername();
- let focusUsername = false;
- let focusPassword = false;
- if (priorUsername === '') {
- focusUsername = true;
- } else {
- focusPassword = true;
- }
-
- const emailParam = Utils.getUrlParameter('email');
- if (emailParam) {
- priorUsername = decodeURIComponent(emailParam);
- }
-
- const {formatMessage} = this.props.intl;
- return (
- <form onSubmit={this.handleSubmit}>
- <div className='signup__email-container'>
- <div className={'form-group' + errorClass}>
- {serverError}
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusUsername}
- type='username'
- className='form-control'
- name='username'
- defaultValue={priorUsername}
- ref='username'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- />
- </div>
- <div className={'form-group' + errorClass}>
- <input
- autoFocus={focusPassword}
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- />
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='login_username.signin'
- defaultMessage='Sign in'
- />
- </button>
- </div>
- </div>
- </form>
- );
- }
-}
-LoginUsername.defaultProps = {
-};
-
-LoginUsername.propTypes = {
- intl: intlShape.isRequired,
- teamName: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(LoginUsername);
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
deleted file mode 100644
index cfd5359b7..000000000
--- a/web/react/components/member_list_team.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FilteredUserList from './filtered_user_list.jsx';
-import TeamMembersDropdown from './team_members_dropdown.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-export default class MemberListTeam extends React.Component {
- constructor(props) {
- super(props);
-
- this.getUsers = this.getUsers.bind(this);
- this.onChange = this.onChange.bind(this);
-
- this.state = {
- users: this.getUsers()
- };
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
-
- getUsers() {
- const profiles = UserStore.getProfiles();
- const users = [];
-
- for (const id of Object.keys(profiles)) {
- users.push(profiles[id]);
- }
-
- users.sort((a, b) => a.username.localeCompare(b.username));
-
- return users;
- }
-
- onChange() {
- this.setState({
- users: this.getUsers()
- });
- }
-
- render() {
- return (
- <FilteredUserList
- style={this.props.style}
- users={this.state.users}
- actions={[TeamMembersDropdown]}
- />
- );
- }
-}
-
-MemberListTeam.propTypes = {
- style: React.PropTypes.object
-};
diff --git a/web/react/components/message_wrapper.jsx b/web/react/components/message_wrapper.jsx
deleted file mode 100644
index 8e0380c06..000000000
--- a/web/react/components/message_wrapper.jsx
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-export default class MessageWrapper extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- if (this.props.message) {
- return <div dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.message, this.props.options)}}/>;
- }
-
- return <div/>;
- }
-}
-
-MessageWrapper.defaultProps = {
- message: ''
-};
-MessageWrapper.propTypes = {
- message: React.PropTypes.string,
- options: React.PropTypes.object
-};
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
deleted file mode 100644
index 2ba435449..000000000
--- a/web/react/components/more_channels.jsx
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import LoadingScreen from './loading_screen.jsx';
-import NewChannelFlow from './new_channel_flow.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-function getStateFromStores() {
- return {
- channels: ChannelStore.getMoreAll(),
- serverError: null
- };
-}
-
-export default class MoreChannels extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleJoin = this.handleJoin.bind(this);
- this.handleNewChannel = this.handleNewChannel.bind(this);
- this.createChannelRow = this.createChannelRow.bind(this);
-
- var initState = getStateFromStores();
- initState.channelType = '';
- initState.joiningChannel = -1;
- initState.showNewChannelModal = false;
- this.state = initState;
- }
- componentDidMount() {
- ChannelStore.addMoreChangeListener(this.onListenerChange);
- $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => {
- AsyncClient.getMoreChannels(true);
- });
-
- var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
- var button = e.relatedTarget;
- self.setState({channelType: $(button).attr('data-channeltype')});
- });
- }
- componentWillUnmount() {
- ChannelStore.removeMoreChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = getStateFromStores();
- if (!Utils.areObjectsEqual(newState.channels, this.state.channels)) {
- this.setState(newState);
- }
- }
- handleJoin(channel, channelIndex) {
- this.setState({joiningChannel: channelIndex});
- client.joinChannel(channel.id,
- () => {
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- AsyncClient.getChannel(channel.id);
- Utils.switchChannel(channel);
- this.setState({joiningChannel: -1});
- },
- (err) => {
- this.setState({joiningChannel: -1, serverError: err.message});
- }
- );
- }
- handleNewChannel() {
- $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
- this.setState({showNewChannelModal: true});
- }
- createChannelRow(channel, index) {
- let joinButton;
- if (this.state.joiningChannel === index) {
- joinButton = (
- <img
- className='join-channel-loading-gif'
- src='/static/images/load.gif'
- />
- );
- } else {
- joinButton = (
- <button
- onClick={this.handleJoin.bind(self, channel, index)}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='more_channels.join'
- defaultMessage='Join'
- />
- </button>
- );
- }
-
- return (
- <div
- className='more-modal__row'
- key={channel.id}
- >
- <div className='more-modal__details'>
- <p className='more-modal__name'>{channel.display_name}</p>
- <p className='more-modal__description'>{channel.purpose}</p>
- </div>
- <div className='more-modal__actions'>
- {joinButton}
- </div>
- </div>
- );
- }
- render() {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- var serverError;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var moreChannels;
-
- if (this.state.channels != null) {
- var channels = this.state.channels;
- if (channels.loading) {
- moreChannels = <LoadingScreen/>;
- } else if (channels.length) {
- moreChannels = (
- <div className='more-modal__list'>
- {channels.map(this.createChannelRow)}
- </div>
- );
- } else {
- moreChannels = (
- <div className='no-channel-message'>
- <p className='primary-message'>
- <FormattedMessage
- id='more_channels.noMore'
- defaultMessage='No more channels to join'
- />
- </p>
- <p className='secondary-message'>
- <FormattedMessage
- id='more_channels.createClick'
- defaultMessage="Click 'Create New Channel' to make a new one"
- />
- </p>
- </div>
- );
- }
- }
-
- return (
- <div
- className='modal fade'
- id='more_channels'
- ref='modal'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- <span className='sr-only'>
- <FormattedMessage
- id='more_channels.close'
- defaultMessage='Close'
- />
- </span>
- </button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='more_channels.title'
- defaultMessage='More Channels'
- />
- </h4>
- <button
- type='button'
- className='btn btn-primary channel-create-btn'
- onClick={this.handleNewChannel}
- >
- <FormattedMessage
- id='more_channels.create'
- defaultMessage='Create New Channel'
- />
- </button>
- <NewChannelFlow
- show={this.state.showNewChannelModal}
- channelType={this.state.channelType}
- onModalDismissed={() => this.setState({showNewChannelModal: false})}
- />
- </div>
- <div
- className='modal-body'
- style={{maxHeight}}
- >
- {moreChannels}
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='more_channels.close'
- defaultMessage='Close'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
-
- );
- }
-}
diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx
deleted file mode 100644
index 0814ac1b3..000000000
--- a/web/react/components/more_direct_channels.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-const Modal = ReactBootstrap.Modal;
-import FilteredUserList from './filtered_user_list.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class MoreDirectChannels extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
- this.handleUserChange = this.handleUserChange.bind(this);
-
- this.createJoinDirectChannelButton = this.createJoinDirectChannelButton.bind(this);
-
- this.state = {
- users: this.getUsersFromStore(),
- loadingDMChannel: -1
- };
- }
-
- getUsersFromStore() {
- const currentId = UserStore.getCurrentId();
- const profiles = UserStore.getActiveOnlyProfiles();
- const users = [];
-
- for (const id in profiles) {
- if (id !== currentId) {
- users.push(profiles[id]);
- }
- }
-
- users.sort((a, b) => a.username.localeCompare(b.username));
-
- return users;
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.handleUserChange);
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.handleUserChange);
- }
-
- handleHide() {
- if (this.props.onModalDismissed) {
- this.props.onModalDismissed();
- }
- }
-
- handleShowDirectChannel(teammate, e) {
- e.preventDefault();
-
- if (this.state.loadingDMChannel !== -1) {
- return;
- }
-
- this.setState({loadingDMChannel: teammate.id});
- Utils.openDirectChannelToUser(
- teammate,
- (channel) => {
- Utils.switchChannel(channel);
- this.setState({loadingDMChannel: -1});
- this.handleHide();
- },
- () => {
- this.setState({loadingDMChannel: -1});
- }
- );
- }
-
- handleUserChange() {
- this.setState({users: this.getUsersFromStore()});
- }
-
- createJoinDirectChannelButton({user}) {
- if (this.state.loadingDMChannel === user.id) {
- return (
- <img
- className='channel-loading-gif'
- src='/static/images/load.gif'
- />
- );
- }
-
- return (
- <button
- type='button'
- className='btn btn-primary btn-message'
- onClick={this.handleShowDirectChannel.bind(this, user)}
- >
- <FormattedMessage
- id='more_direct_channels.message'
- defaultMessage='Message'
- />
- </button>
- );
- }
-
- render() {
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- return (
- <Modal
- dialogClassName='more-modal more-direct-channels'
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='more_direct_channels.title'
- defaultMessage='Direct Messages'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <FilteredUserList
- style={{maxHeight}}
- users={this.state.users}
- actions={[this.createJoinDirectChannelButton]}
- />
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='more_direct_channels.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-MoreDirectChannels.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func
-};
diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx
deleted file mode 100644
index f7a40b54e..000000000
--- a/web/react/components/msg_typing.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SocketStore from '../stores/socket_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const SocketEvents = Constants.SocketEvents;
-
-const holders = defineMessages({
- someone: {
- id: 'msg_typing.someone',
- defaultMessage: 'Someone'
- }
-});
-
-class MsgTyping extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
- this.updateTypingText = this.updateTypingText.bind(this);
- this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this);
-
- this.typingUsers = {};
- this.state = {
- text: ''
- };
- }
-
- componentDidMount() {
- SocketStore.addChangeListener(this.onChange);
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.props.channelId !== nextProps.channelId) {
- for (const u in this.typingUsers) {
- if (!this.typingUsers.hasOwnProperty(u)) {
- continue;
- }
-
- clearTimeout(this.typingUsers[u]);
- }
- this.typingUsers = {};
- this.setState({text: ''});
- }
- }
-
- componentWillUnmount() {
- SocketStore.removeChangeListener(this.onChange);
- }
-
- onChange(msg) {
- let username = this.props.intl.formatMessage(holders.someone);
- if (msg.action === SocketEvents.TYPING &&
- this.props.channelId === msg.channel_id &&
- this.props.parentId === msg.props.parent_id) {
- if (UserStore.hasProfile(msg.user_id)) {
- username = UserStore.getProfile(msg.user_id).username;
- }
-
- if (this.typingUsers[username]) {
- clearTimeout(this.typingUsers[username]);
- }
-
- this.typingUsers[username] = setTimeout(function myTimer(user) {
- delete this.typingUsers[user];
- this.updateTypingText();
- }.bind(this, username), Constants.UPDATE_TYPING_MS);
-
- this.updateTypingText();
- } else if (msg.action === SocketEvents.POSTED && msg.channel_id === this.props.channelId) {
- if (UserStore.hasProfile(msg.user_id)) {
- username = UserStore.getProfile(msg.user_id).username;
- }
- clearTimeout(this.typingUsers[username]);
- delete this.typingUsers[username];
- this.updateTypingText();
- }
- }
-
- updateTypingText() {
- const users = Object.keys(this.typingUsers);
- let text = '';
- switch (users.length) {
- case 0:
- text = '';
- break;
- case 1:
- text = (
- <FormattedMessage
- id='msg_typing.isTyping'
- defaultMessage='{user} is typing...'
- values={{
- user: users[0]
- }}
- />
- );
- break;
- default: {
- const last = users.pop();
- text = (
- <FormattedMessage
- id='msg_typing.areTyping'
- defaultMessage='{users} and {last} are typing...'
- values={{
- users: (users.join(', ')),
- last: (last)
- }}
- />
- );
- break;
- }
- }
-
- this.setState({text});
- }
-
- render() {
- return (
- <span className='msg-typing'>{this.state.text}</span>
- );
- }
-}
-
-MsgTyping.propTypes = {
- intl: intlShape.isRequired,
- channelId: React.PropTypes.string,
- parentId: React.PropTypes.string
-};
-
-export default injectIntl(MsgTyping); \ No newline at end of file
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
deleted file mode 100644
index 8382cb47a..000000000
--- a/web/react/components/navbar.jsx
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EditChannelHeaderModal from './edit_channel_header_modal.jsx';
-import EditChannelPurposeModal from './edit_channel_purpose_modal.jsx';
-import MessageWrapper from './message_wrapper.jsx';
-import NotifyCounts from './notify_counts.jsx';
-import ChannelInfoModal from './channel_info_modal.jsx';
-import ChannelInviteModal from './channel_invite_modal.jsx';
-import ChannelNotificationsModal from './channel_notifications_modal.jsx';
-import DeleteChannelModal from './delete_channel_modal.jsx';
-import RenameChannelModal from './rename_channel_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import attachFastClick from 'fastclick';
-
-const Popover = ReactBootstrap.Popover;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class Navbar extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
- this.handleLeave = this.handleLeave.bind(this);
- this.showSearch = this.showSearch.bind(this);
-
- this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
- this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
- this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
-
- this.createCollapseButtons = this.createCollapseButtons.bind(this);
- this.createDropdown = this.createDropdown.bind(this);
-
- const state = this.getStateFromStores();
- state.showEditChannelPurposeModal = false;
- state.showEditChannelHeaderModal = false;
- state.showMembersModal = false;
- state.showRenameChannelModal = false;
- this.state = state;
- }
- getStateFromStores() {
- return {
- channel: ChannelStore.getCurrent(),
- member: ChannelStore.getCurrentMember(),
- users: ChannelStore.getCurrentExtraInfo().members,
- currentUser: UserStore.getCurrentUser()
- };
- }
- stateValid() {
- return this.state.channel && this.state.member && this.state.users && this.state.currentUser;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- ChannelStore.addExtraInfoChangeListener(this.onChange);
- $('.inner-wrap').click(this.hideSidebars);
- attachFastClick(document.body);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChange);
- ChannelStore.removeExtraInfoChangeListener(this.onChange);
- }
- handleSubmit(e) {
- e.preventDefault();
- }
- handleLeave() {
- Client.leaveChannel(this.state.channel.id,
- () => {
- AsyncClient.getChannels(true);
- window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
- },
- (err) => {
- AsyncClient.dispatchError(err, 'handleLeave');
- }
- );
- }
- hideSidebars(e) {
- var windowWidth = $(window).outerWidth();
- if (windowWidth <= 768) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
-
- if (e.target.className !== 'navbar-toggle' && e.target.className !== 'icon-bar') {
- $('.inner-wrap').removeClass('move--right move--left move--left-small');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- $('.sidebar--menu').removeClass('move--left');
- }
- }
- }
- toggleLeftSidebar() {
- $('.inner-wrap').toggleClass('move--right');
- $('.sidebar--left').toggleClass('move--right');
- }
- toggleRightSidebar() {
- $('.inner-wrap').toggleClass('move--left-small');
- $('.sidebar--menu').toggleClass('move--left');
- }
- showSearch() {
- AppDispatcher.handleServerAction({
- type: ActionTypes.SHOW_SEARCH
- });
- }
- onChange() {
- this.setState(this.getStateFromStores());
- $('#navbar .navbar-brand .description').popover({placement: 'bottom', trigger: 'click', html: true});
- }
- showEditChannelHeaderModal() {
- // this can't be done using a ToggleModalButton because we can't use one inside an OverlayTrigger
- if (this.refs.headerOverlay) {
- this.refs.headerOverlay.hide();
- }
-
- this.setState({
- showEditChannelHeaderModal: true
- });
- }
- showRenameChannelModal(e) {
- e.preventDefault();
-
- this.setState({
- showRenameChannelModal: true
- });
- }
- hideRenameChannelModal() {
- this.setState({
- showRenameChannelModal: false
- });
- }
- createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent) {
- if (channel) {
- var viewInfoOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInfoModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='navbar.viewInfo'
- defaultMessage='View Info'
- />
- </ToggleModalButton>
- </li>
- );
-
- var setChannelHeaderOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.showEditChannelHeaderModal}
- >
- <FormattedMessage
- id='navbar.setHeader'
- defaultMessage='Set Channel Header...'
- />
- </a>
- </li>
- );
-
- var setChannelPurposeOption = null;
- if (!isDirect) {
- setChannelPurposeOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showEditChannelPurposeModal: true})}
- >
- <FormattedMessage
- id='navbar.setPurpose'
- defaultMessage='Set Channel Purpose...'
- />
- </a>
- </li>
- );
- }
-
- var addMembersOption;
- var leaveChannelOption;
- if (!isDirect && !ChannelStore.isDefault(channel)) {
- addMembersOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelInviteModal}
- dialogProps={{channel, currentUser: this.state.currentUser}}
- >
- <FormattedMessage
- id='navbar.addMembers'
- defaultMessage='Add Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- leaveChannelOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleLeave}
- >
- <FormattedMessage
- id='navbar.leave'
- defaultMessage='Leave Channel'
- />
- </a>
- </li>
- );
- }
-
- var manageMembersOption;
- var renameChannelOption;
- var deleteChannelOption;
- if (!isDirect && isAdmin) {
- if (!ChannelStore.isDefault(channel)) {
- manageMembersOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={() => this.setState({showMembersModal: true})}
- >
- <FormattedMessage
- id='navbar.manageMembers'
- defaultMessage='Manage Members'
- />
- </a>
- </li>
- );
-
- deleteChannelOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={DeleteChannelModal}
- dialogProps={{channel}}
- >
- <FormattedMessage
- id='navbar.delete'
- defaultMessage='Delete Channel...'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- renameChannelOption = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.showRenameChannelModal}
- >
- <FormattedMessage
- id='navbar.rename'
- defaultMessage='Rename Channel...'
- />
- </a>
- </li>
- );
- }
-
- var notificationPreferenceOption;
- if (!isDirect) {
- notificationPreferenceOption = (
- <li role='presentation'>
- <ToggleModalButton
- role='menuitem'
- dialogType={ChannelNotificationsModal}
- dialogProps={{
- channel,
- channelMember: this.state.member,
- currentUser: this.state.currentUser
- }}
- >
- <FormattedMessage
- id='navbar.preferences'
- defaultMessage='Notification Preferences'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- return (
- <div className='navbar-brand'>
- <div className='dropdown'>
- <OverlayTrigger
- ref='headerOverlay'
- trigger='click'
- placement='bottom'
- overlay={popoverContent}
- className='description'
- rootClose={true}
- >
- <div className='description info-popover'/>
- </OverlayTrigger>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span className='heading'>{channelTitle} </span>
- <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {viewInfoOption}
- {addMembersOption}
- {manageMembersOption}
- {setChannelHeaderOption}
- {setChannelPurposeOption}
- {notificationPreferenceOption}
- {renameChannelOption}
- {deleteChannelOption}
- {leaveChannelOption}
- </ul>
- </div>
- </div>
- );
- }
-
- return (
- <div className='navbar-brand'>
- <a
- href={TeamStore.getCurrentTeamUrl() + '/channels/town-square'}
- className='heading'
- >
- {channelTitle}
- </a>
- </div>
- );
- }
- createCollapseButtons(currentId) {
- var buttons = [];
- if (currentId == null) {
- buttons.push(
- <button
- key='navbar-toggle-collapse'
- type='button'
- className='navbar-toggle'
- data-toggle='collapse'
- data-target='#navbar-collapse-1'
- >
- <span className='sr-only'>
- <FormattedMessage
- id='navbar.toggle1'
- defaultMessage='Toggle sidebar'
- />
- </span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- </button>
- );
- } else {
- buttons.push(
- <button
- key='navbar-toggle-sidebar'
- type='button'
- className='navbar-toggle'
- data-toggle='collapse'
- data-target='#sidebar-nav'
- onClick={this.toggleLeftSidebar}
- >
- <span className='sr-only'>
- <FormattedMessage
- id='navbar.toggle2'
- defaultMessage='Toggle sidebar'
- />
- </span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <span className='icon-bar'></span>
- <NotifyCounts/>
- </button>
- );
-
- buttons.push(
- <button
- key='navbar-toggle-menu'
- type='button'
- className='navbar-toggle menu-toggle pull-right'
- data-toggle='collapse'
- data-target='#sidebar-nav'
- onClick={this.toggleRightSidebar}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}/>
- </button>
- );
- }
-
- return buttons;
- }
- render() {
- if (!this.stateValid()) {
- return null;
- }
-
- var currentId = this.state.currentUser.id;
- var channel = this.state.channel;
- var channelTitle = this.props.teamDisplayName;
- var popoverContent;
- var isAdmin = false;
- var isDirect = false;
-
- var editChannelHeaderModal = null;
- var editChannelPurposeModal = null;
- let renameChannelModal = null;
-
- if (channel) {
- popoverContent = (
- <Popover
- bsStyle='info'
- placement='bottom'
- id='header-popover'
- >
- <MessageWrapper
- message={channel.header}
- options={{singleline: true, mentionHighlight: false}}
- />
- </Popover>
- );
- isAdmin = Utils.isAdmin(this.state.member.roles);
-
- if (channel.type === 'O') {
- channelTitle = channel.display_name;
- } else if (channel.type === 'P') {
- channelTitle = channel.display_name;
- } else if (channel.type === 'D') {
- isDirect = true;
- if (this.state.users.length > 1) {
- let p;
- if (this.state.users[0].id === currentId) {
- p = UserStore.getProfile(this.state.users[1].id);
- } else {
- p = UserStore.getProfile(this.state.users[0].id);
- }
- if (p != null) {
- channelTitle = p.username;
- }
- }
- }
-
- if (channel.header.length === 0) {
- const link = (
- <a
- href='#'
- onClick={this.showEditChannelHeaderModal}
- >
- <FormattedMessage
- id='navbar.click'
- defaultMessage='Click here'
- />
- </a>
- );
- popoverContent = (
- <Popover
- bsStyle='info'
- placement='bottom'
- id='header-popover'
- >
- <div>
- <FormattedMessage
- id='navbar.noHeader'
- defaultMessage='No channel header yet.{newline}{link} to add one.'
- values={{
- newline: (<br/>),
- link: (link)
- }}
- />
- </div>
- </Popover>
- );
- }
-
- editChannelHeaderModal = (
- <EditChannelHeaderModal
- show={this.state.showEditChannelHeaderModal}
- onHide={() => this.setState({showEditChannelHeaderModal: false})}
- channel={channel}
- />
- );
-
- editChannelPurposeModal = (
- <EditChannelPurposeModal
- show={this.state.showEditChannelPurposeModal}
- onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
- channel={channel}
- />
- );
-
- renameChannelModal = (
- <RenameChannelModal
- show={this.state.showRenameChannelModal}
- onHide={this.hideRenameChannelModal}
- channel={channel}
- />
- );
- }
-
- var collapseButtons = this.createCollapseButtons(currentId);
-
- const searchButton = (
- <button
- type='button'
- className='navbar-toggle pull-right'
- onClick={this.showSearch}
- >
- <span className='glyphicon glyphicon-search icon--white'/>
- </button>
- );
-
- var channelMenuDropdown = this.createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent);
-
- return (
- <div>
- <nav
- className='navbar navbar-default navbar-fixed-top'
- role='navigation'
- >
- <div className='container-fluid theme'>
- <div className='navbar-header'>
- {collapseButtons}
- {searchButton}
- {channelMenuDropdown}
- </div>
- </div>
- </nav>
- {editChannelHeaderModal}
- {editChannelPurposeModal}
- {renameChannelModal}
- </div>
- );
- }
-}
-
-Navbar.defaultProps = {
- teamDisplayName: ''
-};
-Navbar.propTypes = {
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
deleted file mode 100644
index 12227fd13..000000000
--- a/web/react/components/navbar_dropdown.jsx
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import AboutBuildModal from './about_build_modal.jsx';
-import TeamMembersModal from './team_members_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-import UserSettingsModal from './user_settings/user_settings_modal.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {Link} from 'react-router';
-
-export default class NavbarDropdown extends React.Component {
- constructor(props) {
- super(props);
- this.blockToggle = false;
-
- this.handleAboutModal = this.handleAboutModal.bind(this);
- this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
-
- this.state = {
- showUserSettingsModal: false,
- showAboutModal: false
- };
- }
- handleAboutModal() {
- this.setState({showAboutModal: true});
- }
- aboutModalDismissed() {
- this.setState({showAboutModal: false});
- }
- componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
- $('.sidebar--left .dropdown-menu').scrollTop(0);
- this.blockToggle = true;
- setTimeout(() => {
- this.blockToggle = false;
- }, 100);
- });
- }
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
- }
- render() {
- var teamLink = '';
- var inviteLink = '';
- var manageLink = '';
- var sysAdminLink = '';
- var adminDivider = '';
- var currentUser = this.props.currentUser;
- var isAdmin = false;
- var isSystemAdmin = false;
- var teamSettings = null;
-
- if (currentUser != null) {
- isAdmin = Utils.isAdmin(currentUser.roles);
- isSystemAdmin = Utils.isSystemAdmin(currentUser.roles);
-
- inviteLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showInviteMemberModal}
- >
- <FormattedMessage
- id='navbar_dropdown.inviteMember'
- defaultMessage='Invite New Member'
- />
- </a>
- </li>
- );
-
- if (this.props.teamType === Constants.OPEN_TEAM && global.window.mm_config.EnableUserCreation === 'true') {
- teamLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showGetTeamInviteLinkModal}
- >
- <FormattedMessage
- id='navbar_dropdown.teamLink'
- defaultMessage='Get Team Invite Link'
- />
- </a>
- </li>
- );
- }
- }
-
- if (isAdmin) {
- manageLink = (
- <li>
- <ToggleModalButton dialogType={TeamMembersModal}>
- <FormattedMessage
- id='navbar_dropdown.manageMembers'
- defaultMessage='Manage Members'
- />
- </ToggleModalButton>
- </li>
- );
-
- adminDivider = (<li className='divider'></li>);
-
- teamSettings = (
- <li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_settings'
- >
- <FormattedMessage
- id='navbar_dropdown.teamSettings'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- );
- }
-
- if (isSystemAdmin) {
- sysAdminLink = (
- <li>
- <a
- href={'/admin_console'}
- >
- <FormattedMessage
- id='navbar_dropdown.console'
- defaultMessage='System Console'
- />
- </a>
- </li>
- );
- }
-
- var teams = [];
-
- if (global.window.mm_config.EnableTeamCreation === 'true') {
- teams.push(
- <li key='newTeam_li'>
- <a
- key='newTeam_a'
- target='_blank'
- href={Utils.getWindowLocationOrigin() + '/signup_team'}
- >
- <FormattedMessage
- id='navbar_dropdown.create'
- defaultMessage='Create a New Team'
- />
- </a>
- </li>
- );
- }
-
- let helpLink = null;
- if (global.window.mm_config.HelpLink) {
- helpLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.HelpLink}
- >
- <FormattedMessage
- id='navbar_dropdown.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- );
- }
-
- let reportLink = null;
- if (global.window.mm_config.ReportAProblemLink) {
- reportLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.ReportAProblemLink}
- >
- <FormattedMessage
- id='navbar_dropdown.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- );
- }
-
- return (
- <ul className='nav navbar-nav navbar-right'>
- <li
- ref='dropdown'
- className='dropdown'
- >
- <a
- href='#'
- className='dropdown-toggle'
- data-toggle='dropdown'
- role='button'
- aria-expanded='false'
- >
- <span
- className='dropdown__icon'
- dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}}
- />
- </a>
- <ul
- className='dropdown-menu'
- role='menu'
- >
- <li>
- <a
- href='#'
- onClick={() => this.setState({showUserSettingsModal: true})}
- >
- <FormattedMessage
- id='navbar_dropdown.accountSettings'
- defaultMessage='Account Settings'
- />
- </a>
- </li>
- {inviteLink}
- {teamLink}
- <li>
- <Link to={'/' + this.props.teamName + '/logout'}>
- <FormattedMessage
- id='navbar_dropdown.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- {adminDivider}
- {teamSettings}
- {manageLink}
- {sysAdminLink}
- {teams}
- <li className='divider'></li>
- {helpLink}
- {reportLink}
- <li>
- <a
- href='#'
- onClick={this.handleAboutModal}
- >
- <FormattedMessage
- id='navbar_dropdown.about'
- defaultMessage='About Mattermost'
- />
- </a>
- </li>
- <UserSettingsModal
- show={this.state.showUserSettingsModal}
- onModalDismissed={() => this.setState({showUserSettingsModal: false})}
- />
- <AboutBuildModal
- show={this.state.showAboutModal}
- onModalDismissed={this.aboutModalDismissed}
- />
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-NavbarDropdown.defaultProps = {
- teamType: ''
-};
-NavbarDropdown.propTypes = {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- currentUser: React.PropTypes.object
-};
diff --git a/web/react/components/needs_team.jsx b/web/react/components/needs_team.jsx
deleted file mode 100644
index 33b9cd37e..000000000
--- a/web/react/components/needs_team.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-export default class NeedsTeam extends React.Component {
- componentWillMount() {
- GlobalActions.loadTeamRequiredPage();
- }
- render() {
- return this.props.children;
- }
-}
-
-NeedsTeam.defaultProps = {
-};
-
-NeedsTeam.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/new_channel_flow.jsx b/web/react/components/new_channel_flow.jsx
deleted file mode 100644
index a0bb14e8f..000000000
--- a/web/react/components/new_channel_flow.jsx
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import NewChannelModal from './new_channel_modal.jsx';
-import ChangeURLModal from './change_url_modal.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const SHOW_NEW_CHANNEL = 1;
-const SHOW_EDIT_URL = 2;
-const SHOW_EDIT_URL_THEN_COMPLETE = 3;
-const messages = defineMessages({
- invalidName: {
- id: 'channel_flow.invalidName',
- defaultMessage: 'Invalid Channel Name'
- },
- alreadyExist: {
- id: 'channel_flow.alreadyExist',
- defaultMessage: 'A channel with that URL already exists'
- },
- channel: {
- id: 'channel_flow.channel',
- defaultMessage: 'Channel'
- },
- group: {
- id: 'channel_flow.group',
- defaultMessage: 'Group'
- },
- change: {
- id: 'channel_flow.changeUrlTitle',
- defaultMessage: 'Change {term} URL'
- },
- set: {
- id: 'channel_flow.set_url_title',
- defaultMessage: 'Set {term} URL'
- },
- create: {
- id: 'channel_flow.create',
- defaultMessage: 'Create {term}'
- },
- changeUrlDescription: {
- id: 'channel_flow.changeUrlDescription',
- defaultMessage: 'Some characters are not allowed in URLs and may be removed.'
- }
-});
-
-class NewChannelFlow extends React.Component {
- constructor(props) {
- super(props);
-
- this.doSubmit = this.doSubmit.bind(this);
- this.typeSwitched = this.typeSwitched.bind(this);
- this.urlChangeRequested = this.urlChangeRequested.bind(this);
- this.urlChangeSubmitted = this.urlChangeSubmitted.bind(this);
- this.urlChangeDismissed = this.urlChangeDismissed.bind(this);
- this.channelDataChanged = this.channelDataChanged.bind(this);
-
- this.state = {
- serverError: '',
- channelType: 'O',
- flowState: SHOW_NEW_CHANNEL,
- channelDisplayName: '',
- channelName: '',
- channelPurpose: '',
- nameModified: false
- };
- }
- componentWillReceiveProps(nextProps) {
- // If we are being shown, grab channel type from props and clear
- if (nextProps.show === true && this.props.show === false) {
- this.setState({
- serverError: '',
- channelType: nextProps.channelType,
- flowState: SHOW_NEW_CHANNEL,
- channelDisplayName: '',
- channelName: '',
- channelPurpose: '',
- nameModified: false
- });
- }
- }
- doSubmit() {
- var channel = {};
-
- const {formatMessage} = this.props.intl;
- channel.display_name = this.state.channelDisplayName;
- if (!channel.display_name) {
- this.setState({serverError: formatMessage(messages.invalidName)});
- return;
- }
-
- channel.name = this.state.channelName;
- if (channel.name.length < 2) {
- this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
- return;
- }
-
- const cu = UserStore.getCurrentUser();
- channel.team_id = cu.team_id;
- channel.purpose = this.state.channelPurpose;
- channel.type = this.state.channelType;
-
- Client.createChannel(channel,
- (data) => {
- this.props.onModalDismissed();
- AsyncClient.getChannel(data.id);
- Utils.switchChannel(data);
- },
- (err) => {
- if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
- this.setState({flowState: SHOW_EDIT_URL_THEN_COMPLETE});
- }
- if (err.id === 'store.sql_channel.update.exists.app_error') {
- this.setState({serverError: formatMessage(messages.alreadyExist)});
- return;
- }
- this.setState({serverError: err.message});
- }
- );
- }
- typeSwitched() {
- if (this.state.channelType === 'P') {
- this.setState({channelType: 'O'});
- } else {
- this.setState({channelType: 'P'});
- }
- }
- urlChangeRequested() {
- this.setState({flowState: SHOW_EDIT_URL});
- }
- urlChangeSubmitted(newURL) {
- if (this.state.flowState === SHOW_EDIT_URL_THEN_COMPLETE) {
- this.setState({channelName: newURL, nameModified: true}, this.doSubmit);
- } else {
- this.setState({flowState: SHOW_NEW_CHANNEL, serverError: '', channelName: newURL, nameModified: true});
- }
- }
- urlChangeDismissed() {
- this.setState({flowState: SHOW_NEW_CHANNEL});
- }
- channelDataChanged(data) {
- this.setState({
- channelDisplayName: data.displayName,
- channelPurpose: data.purpose
- });
- if (!this.state.nameModified) {
- this.setState({channelName: Utils.cleanUpUrlable(data.displayName.trim())});
- }
- }
- render() {
- const channelData = {
- name: this.state.channelName,
- displayName: this.state.channelDisplayName,
- purpose: this.state.channelPurpose
- };
-
- let showChannelModal = false;
- let showGroupModal = false;
- let showChangeURLModal = false;
-
- let changeURLTitle = '';
- let changeURLSubmitButtonText = '';
- let channelTerm = '';
-
- const {formatMessage} = this.props.intl;
-
- // Only listen to flow state if we are being shown
- if (this.props.show) {
- switch (this.state.flowState) {
- case SHOW_NEW_CHANNEL:
- if (this.state.channelType === 'O') {
- showChannelModal = true;
- channelTerm = formatMessage(messages.channel);
- } else {
- showGroupModal = true;
- channelTerm = formatMessage(messages.group);
- }
- break;
- case SHOW_EDIT_URL:
- showChangeURLModal = true;
- changeURLTitle = formatMessage(messages.change, {term: channelTerm});
- changeURLSubmitButtonText = formatMessage(messages.change, {term: channelTerm});
- break;
- case SHOW_EDIT_URL_THEN_COMPLETE:
- showChangeURLModal = true;
- changeURLTitle = formatMessage(messages.set, {term: channelTerm});
- changeURLSubmitButtonText = formatMessage(messages.create, {term: channelTerm});
- break;
- }
- }
- return (
- <span>
- <NewChannelModal
- show={showChannelModal}
- channelType={'O'}
- channelData={channelData}
- serverError={this.state.serverError}
- onSubmitChannel={this.doSubmit}
- onModalDismissed={this.props.onModalDismissed}
- onTypeSwitched={this.typeSwitched}
- onChangeURLPressed={this.urlChangeRequested}
- onDataChanged={this.channelDataChanged}
- />
- <NewChannelModal
- show={showGroupModal}
- channelType={'P'}
- channelData={channelData}
- serverError={this.state.serverError}
- onSubmitChannel={this.doSubmit}
- onModalDismissed={this.props.onModalDismissed}
- onTypeSwitched={this.typeSwitched}
- onChangeURLPressed={this.urlChangeRequested}
- onDataChanged={this.channelDataChanged}
- />
- <ChangeURLModal
- show={showChangeURLModal}
- title={changeURLTitle}
- description={formatMessage(messages.changeUrlDescription)}
- urlLabel={channelTerm + ' URL'}
- submitButtonText={changeURLSubmitButtonText}
- currentURL={this.state.channelName}
- serverError={this.state.serverError}
- onModalSubmit={this.urlChangeSubmitted}
- onModalDismissed={this.urlChangeDismissed}
- />
- </span>
- );
- }
-}
-
-NewChannelFlow.defaultProps = {
- show: false,
- channelType: 'O'
-};
-
-NewChannelFlow.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channelType: React.PropTypes.string.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(NewChannelFlow); \ No newline at end of file
diff --git a/web/react/components/new_channel_modal.jsx b/web/react/components/new_channel_modal.jsx
deleted file mode 100644
index 788e6dc1b..000000000
--- a/web/react/components/new_channel_modal.jsx
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-var Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- nameEx: {
- id: 'channel_modal.nameEx',
- defaultMessage: 'E.g.: "Bugs", "Marketing", "åŠžå…¬å®¤ę‹ęƒ…"'
- }
-});
-
-class NewChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleChange = this.handleChange.bind(this);
-
- this.state = {
- displayNameError: ''
- };
- }
- componentWillReceiveProps(nextProps) {
- if (nextProps.show === true && this.props.show === false) {
- this.setState({
- displayNameError: ''
- });
- }
- }
- componentDidMount() {
- if (Utils.isBrowserIE()) {
- $('body').addClass('browser--IE');
- }
- }
- handleSubmit(e) {
- e.preventDefault();
-
- const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
- if (displayName.length < 1) {
- this.setState({displayNameError: true});
- return;
- }
-
- this.props.onSubmitChannel();
- }
- handleChange() {
- const newData = {
- displayName: ReactDOM.findDOMNode(this.refs.display_name).value,
- purpose: ReactDOM.findDOMNode(this.refs.channel_purpose).value
- };
- this.props.onDataChanged(newData);
- }
- render() {
- var displayNameError = null;
- var serverError = null;
- var displayNameClass = 'form-group';
-
- if (this.state.displayNameError) {
- displayNameError = (
- <p className='input__help error'>
- <FormattedMessage
- id='channel_modal.displayNameError'
- defaultMessage='This field is required'
- />
- {this.state.displayNameError}
- </p>
- );
- displayNameClass += ' has-error';
- }
-
- if (this.props.serverError) {
- serverError = <div className='form-group has-error'><p className='input__help error'>{this.props.serverError}</p></div>;
- }
-
- var channelTerm = '';
- var channelSwitchText = '';
- switch (this.props.channelType) {
- case 'P':
- channelTerm = (
- <FormattedMessage
- id='channel_modal.group'
- defaultMessage='Group'
- />
- );
- channelSwitchText = (
- <div className='modal-intro'>
- <FormattedMessage
- id='channel_modal.privateGroup1'
- defaultMessage='Create a new private group with restricted membership. '
- />
- <a
- href='#'
- onClick={this.props.onTypeSwitched}
- >
- <FormattedMessage
- id='channel_modal.publicChannel1'
- defaultMessage='Create a public channel'
- />
- </a>
- </div>
- );
- break;
- case 'O':
- channelTerm = (
- <FormattedMessage
- id='channel_modal.channel'
- defaultMessage='Channel'
- />
- );
- channelSwitchText = (
- <div className='modal-intro'>
- <FormattedMessage
- id='channel_modal.publicChannel2'
- defaultMessage='Create a new public channel anyone can join. '
- />
- <a
- href='#'
- onClick={this.props.onTypeSwitched}
- >
- <FormattedMessage
- id='channel_modal.privateGroup2'
- defaultMessage='Create a private group'
- />
- </a>
- </div>
- );
- break;
- }
-
- const prettyTeamURL = Utils.getShortenedTeamURL();
-
- return (
- <span>
- <Modal
- show={this.props.show}
- bsSize='large'
- onHide={this.props.onModalDismissed}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='channel_modal.modalTitle'
- defaultMessage='New '
- />
- {channelTerm}
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <div>
- {channelSwitchText}
- </div>
- <div className={displayNameClass}>
- <label className='col-sm-3 form__label control-label'>
- <FormattedMessage
- id='channel_modal.name'
- defaultMessage='Name'
- />
- </label>
- <div className='col-sm-9'>
- <input
- onChange={this.handleChange}
- type='text'
- ref='display_name'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.nameEx)}
- maxLength='22'
- value={this.props.channelData.displayName}
- autoFocus={true}
- tabIndex='1'
- />
- {displayNameError}
- <p className='input__help dark'>
- {'URL: ' + prettyTeamURL + this.props.channelData.name + ' ('}
- <a
- href='#'
- onClick={this.props.onChangeURLPressed}
- >
- <FormattedMessage
- id='channel_modal.edit'
- defaultMessage='Edit'
- />
- </a>
- {')'}
- </p>
- </div>
- </div>
- <div className='form-group less'>
- <div className='col-sm-3'>
- <label className='form__label control-label'>
- <FormattedMessage
- id='channel_modal.purpose'
- defaultMessage='Purpose'
- />
- </label>
- <label className='form__label light'>
- <FormattedMessage
- id='channel_modal.optional'
- defaultMessage='(optional)'
- />
- </label>
- </div>
- <div className='col-sm-9'>
- <textarea
- className='form-control no-resize'
- ref='channel_purpose'
- rows='4'
- placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})}
- maxLength='128'
- value={this.props.channelData.purpose}
- onChange={this.handleChange}
- tabIndex='2'
- />
- <p className='input__help'>
- <FormattedMessage
- id='channel_modal.descriptionHelp'
- defaultMessage='Describe how this {term} should be used.'
- values={{
- term: (channelTerm)
- }}
- />
- </p>
- {serverError}
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onModalDismissed}
- >
- <FormattedMessage
- id='channel_modal.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='3'
- >
- <FormattedMessage
- id='channel_modal.createNew'
- defaultMessage='Create New '
- />
- {channelTerm}
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- </span>
- );
- }
-}
-
-NewChannelModal.defaultProps = {
- show: false,
- channelType: 'O',
- serverError: ''
-};
-NewChannelModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- channelType: React.PropTypes.string.isRequired,
- channelData: React.PropTypes.object.isRequired,
- serverError: React.PropTypes.string,
- onSubmitChannel: React.PropTypes.func.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- onTypeSwitched: React.PropTypes.func.isRequired,
- onChangeURLPressed: React.PropTypes.func.isRequired,
- onDataChanged: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(NewChannelModal); \ No newline at end of file
diff --git a/web/react/components/not_logged_in.jsx b/web/react/components/not_logged_in.jsx
deleted file mode 100644
index cf3effcd5..000000000
--- a/web/react/components/not_logged_in.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class NotLoggedIn extends React.Component {
- componentDidMount() {
- $('body').attr('class', 'sticky');
- $('#root').attr('class', 'container-fluid');
- }
- componentWillUnmount() {
- $('body').attr('class', '');
- $('#root').attr('class', '');
- }
- render() {
- return (
- <div className='inner-wrap'>
- <div className='row content'>
- {this.props.children}
- <div className='footer-push'></div>
- </div>
- <div className='row footer'>
- <div className='footer-pane col-xs-12'>
- <div className='col-xs-12'>
- <span className='pull-right footer-site-name'>{global.window.mm_config.SiteName}</span>
- </div>
- <div className='col-xs-12'>
- <span className='pull-right footer-link copyright'>{'Ā© 2015 Mattermost, Inc.'}</span>
- <a
- id='help_link'
- className='pull-right footer-link'
- href={global.window.mm_config.HelpLink}
- >
- <FormattedMessage id='web.footer.help'/>
- </a>
- <a
- id='terms_link'
- className='pull-right footer-link'
- href={global.window.mm_config.TermsOfServiceLink}
- >
- <FormattedMessage id='web.footer.terms'/>
- </a>
- <a
- id='privacy_link'
- className='pull-right footer-link'
- href={global.window.mm_config.PrivacyPolicyLink}
- >
- <FormattedMessage id='web.footer.privacy'/>
- </a>
- <a
- id='about_link'
- className='pull-right footer-link'
- href={global.window.mm_config.AboutLink}
- >
- <FormattedMessage id='web.footer.about'/>
- </a>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-NotLoggedIn.defaultProps = {
-};
-
-NotLoggedIn.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/notify_counts.jsx b/web/react/components/notify_counts.jsx
deleted file mode 100644
index 19b81556b..000000000
--- a/web/react/components/notify_counts.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-function getCountsStateFromStores() {
- var count = 0;
- var channels = ChannelStore.getAll();
- var members = ChannelStore.getAllMembers();
-
- channels.forEach(function setChannelInfo(channel) {
- var channelMember = members[channel.id];
- if (channel.type === 'D') {
- count += channel.total_msg_count - channelMember.msg_count;
- } else if (channelMember.mention_count > 0) {
- count += channelMember.mention_count;
- } else if (channelMember.notify_props.mark_unread !== 'mention' && channel.total_msg_count - channelMember.msg_count > 0) {
- count += 1;
- }
- });
-
- return {count: count};
-}
-
-export default class NotifyCounts extends React.Component {
- constructor(props) {
- super(props);
-
- this.onListenerChange = this.onListenerChange.bind(this);
-
- this.state = getCountsStateFromStores();
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onListenerChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- var newState = getCountsStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- render() {
- if (this.state.count) {
- return <span className='badge badge-notify'>{this.state.count}</span>;
- }
- return null;
- }
-}
diff --git a/web/react/components/password_reset_form.jsx b/web/react/components/password_reset_form.jsx
deleted file mode 100644
index cfd39e440..000000000
--- a/web/react/components/password_reset_form.jsx
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-class PasswordResetForm extends React.Component {
- constructor(props) {
- super(props);
-
- this.handlePasswordReset = this.handlePasswordReset.bind(this);
-
- this.state = {};
- }
- handlePasswordReset(e) {
- e.preventDefault();
-
- const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({
- error: (
- <FormattedMessage
- id='password_form.error'
- defaultMessage='Please enter at least {chars} characters.'
- chars={Constants.MIN_PASSWORD_LENGTH}
- />
- )
- });
- return;
- }
-
- this.setState({
- error: null
- });
-
- const data = {};
- data.new_password = password;
- data.hash = this.props.location.query.h;
- data.data = this.props.location.query.d;
- data.name = this.props.params.team;
-
- Client.resetPassword(data,
- () => {
- this.setState({error: null});
- browserHistory.push('/' + this.props.params.team + '/login');
- },
- (err) => {
- this.setState({error: err.message});
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = (
- <div className='form-group has-error'>
- <label className='control-label'>
- {this.state.error}
- </label>
- </div>
- );
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- return (
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='password_form.title'
- defaultMessage='Password Reset'
- />
- </h3>
- <form onSubmit={this.handlePasswordReset}>
- <p>
- <FormattedMessage
- id='password_form.enter'
- defaultMessage='Enter a new password for your {siteName} account.'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className={formClass}>
- <input
- type='password'
- className='form-control'
- name='password'
- ref='password'
- placeholder={Utils.localizeMessage(
- 'password_form.pwd',
- 'Password'
- )}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='password_form.change'
- defaultMessage='Change my password'
- />
- </button>
- </form>
- </div>
- </div>
- );
- }
-}
-
-PasswordResetForm.defaultProps = {
-};
-PasswordResetForm.propTypes = {
- params: React.PropTypes.object.isRequired,
- location: React.PropTypes.object.isRequired
-};
-
-export default PasswordResetForm;
diff --git a/web/react/components/password_reset_send_link.jsx b/web/react/components/password_reset_send_link.jsx
deleted file mode 100644
index ce6253e16..000000000
--- a/web/react/components/password_reset_send_link.jsx
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-class PasswordResetSendLink extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSendLink = this.handleSendLink.bind(this);
-
- this.state = {
- error: '',
- updateText: ''
- };
- }
- handleSendLink(e) {
- e.preventDefault();
-
- var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!email || !Utils.isEmail(email)) {
- this.setState({
- error: (
- <FormattedMessage
- id={'password_send.error'}
- defaultMessage={'Please enter a valid email address.'}
- />
- )
- });
- return;
- }
-
- // End of error checking clear error
- this.setState({
- error: ''
- });
-
- var data = {};
- data.email = email;
- data.name = this.props.params.team;
- client.sendPasswordReset(data,
- () => {
- this.setState({
- error: null,
- updateText: (
- <div className='reset-form alert alert-success'>
- <FormattedHTMLMessage
- id='password_send.link'
- defaultMessage='<p>A password reset link has been sent to <b>{email}</b></p>'
- email={email}
- />
- <FormattedMessage
- id={'password_send.checkInbox'}
- defaultMessage={'Please check your inbox.'}
- />
- </div>
- )
- });
- $(ReactDOM.findDOMNode(this.refs.reset_form)).hide();
- },
- (err) => {
- this.setState({
- error: err.message,
- update_text: null
- });
- }
- );
- }
- render() {
- var error = null;
- if (this.state.error) {
- error = <div className='form-group has-error'><label className='control-label'>{this.state.error}</label></div>;
- }
-
- var formClass = 'form-group';
- if (error) {
- formClass += ' has-error';
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='password_send.title'
- defaultMessage='Password Reset'
- />
- </h3>
- {this.state.updateText}
- <form
- onSubmit={this.handleSendLink}
- ref='reset_form'
- >
- <p>
- <FormattedMessage
- id='password_send.description'
- defaultMessage='To reset your password, enter the email address you used to sign up'
- />
- </p>
- <div className={formClass}>
- <input
- type='email'
- className='form-control'
- name='email'
- ref='email'
- placeholder={Utils.localizeMessage(
- 'password_send.email',
- 'Email'
- )}
- spellCheck='false'
- />
- </div>
- {error}
- <button
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='password_send.reset'
- defaultMessage='Reset my password'
- />
- </button>
- </form>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PasswordResetSendLink.defaultProps = {
-};
-PasswordResetSendLink.propTypes = {
- params: React.PropTypes.object.isRequired
-};
-
-export default PasswordResetSendLink;
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
deleted file mode 100644
index 8f6948ee4..000000000
--- a/web/react/components/popover_list_members.jsx
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-var Popover = ReactBootstrap.Popover;
-var Overlay = ReactBootstrap.Overlay;
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PopoverListMembers extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
- this.closePopover = this.closePopover.bind(this);
- }
-
- componentWillMount() {
- this.setState({showPopover: false});
- }
-
- componentDidMount() {
- const originalLeave = $.fn.popover.Constructor.prototype.leave;
- $.fn.popover.Constructor.prototype.leave = function onLeave(obj) {
- let selfObj;
- if (obj instanceof this.constructor) {
- selfObj = obj;
- } else {
- selfObj = $(obj.currentTarget)[this.type](this.getDelegateOptions()).data(`bs.${this.type}`);
- }
- originalLeave.call(this, obj);
-
- if (obj.currentTarget && selfObj.$tip) {
- selfObj.$tip.one('mouseenter', function onMouseEnter() {
- clearTimeout(selfObj.timeout);
- selfObj.$tip.one('mouseleave', function onMouseLeave() {
- $.fn.popover.Constructor.prototype.leave.call(selfObj, selfObj);
- });
- });
- }
- };
- }
-
- componentDidUpdate() {
- $(ReactDOM.findDOMNode(this.refs.memebersPopover)).find('.popover-content').perfectScrollbar();
- }
-
- handleShowDirectChannel(teammate, e) {
- e.preventDefault();
-
- Utils.openDirectChannelToUser(
- teammate,
- (channel, channelAlreadyExisted) => {
- Utils.switchChannel(channel);
- if (channelAlreadyExisted) {
- this.closePopover();
- }
- },
- () => {
- this.closePopover();
- }
- );
- }
-
- closePopover() {
- this.setState({showPopover: false});
- }
-
- render() {
- const popoverHtml = [];
- const members = this.props.members;
- const teamMembers = UserStore.getProfilesUsernameMap();
- const currentUserId = UserStore.getCurrentId();
- const ch = ChannelStore.getCurrent();
-
- if (members && teamMembers) {
- members.sort((a, b) => {
- const aName = Utils.displayUsername(a.id);
- const bName = Utils.displayUsername(b.id);
-
- return aName.localeCompare(bName);
- });
-
- members.forEach((m, i) => {
- let button = '';
- if (currentUserId !== m.id && ch.type !== 'D') {
- button = (
- <a
- href='#'
- className='btn-message'
- onClick={(e) => this.handleShowDirectChannel(m, e)}
- >
- <FormattedMessage
- id='members_popover.msg'
- defaultMessage='Message'
- />
- </a>
- );
- }
-
- let name = '';
- if (teamMembers[m.username]) {
- name = Utils.displayUsername(teamMembers[m.username].id);
- }
-
- if (name) {
- popoverHtml.push(
- <div
- className='more-modal__row'
- key={'popover-member-' + i}
- >
-
- <img
- className='more-modal__image'
- width='26px'
- height='26px'
- src={`/api/v1/users/${m.id}/image?time=${m.update_at}`}
- />
- <div className='more-modal__details'>
- <div
- className='more-modal__name'
- >
- {name}
- </div>
- </div>
- <div
- className='more-modal__actions'
- >
- {button}
- </div>
- </div>
- );
- }
- });
- }
-
- let count = this.props.memberCount;
- let countText = '-';
-
- // fall back to checking the length of the member list if the count isn't set
- if (!count && members) {
- count = members.length;
- }
-
- if (count > Constants.MAX_CHANNEL_POPOVER_COUNT) {
- countText = Constants.MAX_CHANNEL_POPOVER_COUNT + '+';
- } else if (count > 0) {
- countText = count.toString();
- }
-
- const title = (
- <FormattedMessage
- id='members_popover.title'
- defaultMessage='Members'
- />
- );
- return (
- <div>
- <div
- id='member_popover'
- ref='member_popover_target'
- onClick={(e) => this.setState({popoverTarget: e.target, showPopover: !this.state.showPopover})}
- >
- <div>
- {countText}
- <span
- className='fa fa-user'
- aria-hidden='true'
- />
- </div>
- </div>
- <Overlay
- rootClose={true}
- onHide={this.closePopover}
- show={this.state.showPopover}
- target={() => this.state.popoverTarget}
- placement='bottom'
- >
- <Popover
- ref='memebersPopover'
- title={title}
- id='member-list-popover'
- >
- <div className='more-modal__list'>{popoverHtml}</div>
- </Popover>
- </Overlay>
- </div>
- );
- }
-}
-
-PopoverListMembers.propTypes = {
- members: React.PropTypes.array.isRequired,
- memberCount: React.PropTypes.number,
- channelId: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
deleted file mode 100644
index 3a855edf2..000000000
--- a/web/react/components/post.jsx
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostHeader from './post_header.jsx';
-import PostBody from './post_body.jsx';
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-
-export default class Post extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleCommentClick = this.handleCommentClick.bind(this);
- this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
- this.retryPost = this.retryPost.bind(this);
-
- this.state = {};
- }
- handleCommentClick(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: Utils.getRootId(this.props.post)
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
- }
- forceUpdateInfo() {
- this.refs.info.forceUpdate();
- this.refs.header.forceUpdate();
- }
- retryPost(e) {
- e.preventDefault();
-
- var post = this.props.post;
- Client.createPost(post, post.channel_id,
- (data) => {
- AsyncClient.getPosts();
-
- var channel = ChannelStore.get(post.channel_id);
- var member = ChannelStore.getMember(post.channel_id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = Utils.getTimestamp();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- () => {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- );
-
- post.state = Constants.POST_LOADING;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- if (nextProps.sameRoot !== this.props.sameRoot) {
- return true;
- }
-
- if (nextProps.sameUser !== this.props.sameUser) {
- return true;
- }
-
- if (nextProps.displayNameType !== this.props.displayNameType) {
- return true;
- }
-
- if (this.getCommentCount(nextProps) !== this.getCommentCount(this.props)) {
- return true;
- }
-
- if (nextProps.shouldHighlight !== this.props.shouldHighlight) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
- return true;
- }
-
- return false;
- }
- getCommentCount(props) {
- const post = props.post;
- const parentPost = props.parentPost;
- const posts = props.posts;
-
- let commentCount = 0;
- let commentRootId;
- if (parentPost) {
- commentRootId = post.root_id;
- } else {
- commentRootId = post.id;
- }
- for (const postId in posts) {
- if (posts[postId].root_id === commentRootId) {
- commentCount += 1;
- }
- }
-
- return commentCount;
- }
- render() {
- const post = this.props.post;
- const parentPost = this.props.parentPost;
- const posts = this.props.posts;
-
- if (!post.props) {
- post.props = {};
- }
-
- let type = 'Post';
- if (post.root_id && post.root_id.length > 0) {
- type = 'Comment';
- }
-
- const commentCount = this.getCommentCount(this.props);
-
- let rootUser;
- if (this.props.sameRoot) {
- rootUser = 'same--root';
- } else {
- rootUser = 'other--root';
- }
-
- let postType = '';
- if (type !== 'Post') {
- postType = 'post--comment';
- } else if (commentCount > 0) {
- postType = 'post--root';
- }
-
- let currentUserCss = '';
- if (this.props.currentUser.id === post.user_id && !post.props.from_webhook && !Utils.isSystemMessage(post)) {
- currentUserCss = 'current--user';
- }
-
- let timestamp = 0;
- if (!this.props.user || this.props.user.update_at == null) {
- timestamp = this.props.currentUser.update_at;
- } else {
- timestamp = this.props.user.update_at;
- }
-
- let sameUserClass = '';
- if (this.props.sameUser) {
- sameUserClass = 'same--user';
- }
-
- let shouldHighlightClass = '';
- if (this.props.shouldHighlight) {
- shouldHighlightClass = 'post--highlight';
- }
-
- let systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
- systemMessageClass = 'post--system';
- }
-
- 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 if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
- profilePic = (
- <img
- src={src}
- height='36'
- width='36'
- />
- );
- }
-
- return (
- <div>
- <div
- id={'post_' + post.id}
- className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass}
- >
- <div className='post__content'>
- <div className='post__img'>{profilePic}</div>
- <div>
- <PostHeader
- ref='header'
- post={post}
- sameRoot={this.props.sameRoot}
- commentCount={commentCount}
- handleCommentClick={this.handleCommentClick}
- isLastComment={this.props.isLastComment}
- sameUser={this.props.sameUser}
- user={this.props.user}
- currentUser={this.props.currentUser}
- />
- <PostBody
- post={post}
- sameRoot={this.props.sameRoot}
- parentPost={parentPost}
- posts={posts}
- handleCommentClick={this.handleCommentClick}
- retryPost={this.retryPost}
- />
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-Post.propTypes = {
- post: React.PropTypes.object.isRequired,
- posts: React.PropTypes.object,
- parentPost: React.PropTypes.object,
- user: React.PropTypes.object,
- sameUser: React.PropTypes.bool,
- sameRoot: React.PropTypes.bool,
- hideProfilePic: React.PropTypes.bool,
- isLastComment: React.PropTypes.bool,
- shouldHighlight: React.PropTypes.bool,
- displayNameType: React.PropTypes.string,
- hasProfiles: React.PropTypes.bool,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_attachment.jsx b/web/react/components/post_attachment.jsx
deleted file mode 100644
index 2eedfb7c1..000000000
--- a/web/react/components/post_attachment.jsx
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- collapse: {
- id: 'post_attachment.collapse',
- defaultMessage: 'ā–² collapse text'
- },
- more: {
- id: 'post_attachment.more',
- defaultMessage: 'ā–¼ read more'
- }
-});
-
-class PostAttachment extends React.Component {
- constructor(props) {
- super(props);
-
- this.getFieldsTable = this.getFieldsTable.bind(this);
- this.getInitState = this.getInitState.bind(this);
- this.shouldCollapse = this.shouldCollapse.bind(this);
- this.toggleCollapseState = this.toggleCollapseState.bind(this);
- }
-
- componentDidMount() {
- $(this.refs.attachment).on('click', '.attachment-link-more', this.toggleCollapseState);
- }
-
- componentWillUnmount() {
- $(this.refs.attachment).off('click', '.attachment-link-more', this.toggleCollapseState);
- }
-
- componentWillMount() {
- this.setState(this.getInitState());
- }
-
- getInitState() {
- const shouldCollapse = this.shouldCollapse();
- const text = TextFormatting.formatText(this.props.attachment.text || '');
- const uncollapsedText = text + (shouldCollapse ? `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.collapse)}</a>` : '');
- const collapsedText = shouldCollapse ? this.getCollapsedText() : text;
-
- return {
- shouldCollapse,
- collapsedText,
- uncollapsedText,
- text: shouldCollapse ? collapsedText : uncollapsedText,
- collapsed: shouldCollapse
- };
- }
-
- toggleCollapseState(e) {
- e.preventDefault();
-
- let state = this.state;
- state.text = state.collapsed ? state.uncollapsedText : state.collapsedText;
- state.collapsed = !state.collapsed;
- this.setState(state);
- }
-
- shouldCollapse() {
- const text = this.props.attachment.text || '';
- return (text.match(/\n/g) || []).length >= 5 || text.length > 700;
- }
-
- getCollapsedText() {
- let text = this.props.attachment.text || '';
- if ((text.match(/\n/g) || []).length >= 5) {
- text = text.split('\n').splice(0, 5).join('\n');
- } else if (text.length > 700) {
- text = text.substr(0, 700);
- }
-
- return TextFormatting.formatText(text) + `<a class="attachment-link-more" href="#">${this.props.intl.formatMessage(holders.more)}</a>`;
- }
-
- getFieldsTable() {
- const fields = this.props.attachment.fields;
- if (!fields || !fields.length) {
- return '';
- }
-
- const compactTable = fields.filter((field) => field.short).length > 0;
- let tHead;
- let tBody;
-
- if (compactTable) {
- let headerCols = [];
- let bodyCols = [];
-
- fields.forEach((field, i) => {
- headerCols.push(
- <th
- className='attachment___field-caption'
- key={'attachment__field-caption-' + i}
- >
- {field.title}
- </th>
- );
- bodyCols.push(
- <td
- className='attachment___field'
- key={'attachment__field-' + i}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
- >
- </td>
- );
- });
-
- tHead = (
- <tr>
- {headerCols}
- </tr>
- );
- tBody = (
- <tr>
- {bodyCols}
- </tr>
- );
- } else {
- tBody = [];
-
- fields.forEach((field, i) => {
- tBody.push(
- <tr key={'attachment__field-' + i}>
- <td
- className='attachment___field-caption'
- >
- {field.title}
- </td>
- <td
- className='attachment___field'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
- >
- </td>
- </tr>
- );
- });
- }
-
- return (
- <table
- className='attachment___fields'
- >
- <thead>
- {tHead}
- </thead>
- <tbody>
- {tBody}
- </tbody>
- </table>
- );
- }
-
- render() {
- const data = this.props.attachment;
-
- let preText;
- if (data.pretext) {
- preText = (
- <div
- className='attachment__thumb-pretext'
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(data.pretext)}}
- >
- </div>
- );
- }
-
- let author = [];
- if (data.author_name || data.author_icon) {
- if (data.author_icon) {
- author.push(
- <img
- className='attachment__author-icon'
- src={data.author_icon}
- key={'attachment__author-icon'}
- height='14'
- width='14'
- />
- );
- }
- if (data.author_name) {
- author.push(
- <span
- className='attachment__author-name'
- key={'attachment__author-name'}
- >
- {data.author_name}
- </span>
- );
- }
- }
- if (data.author_link) {
- author = (
- <a
- href={data.author_link}
- target='_blank'
- >
- {author}
- </a>
- );
- }
-
- let title;
- if (data.title) {
- if (data.title_link) {
- title = (
- <h1
- className='attachment__title'
- >
- <a
- className='attachment__title-link'
- href={data.title_link}
- target='_blank'
- >
- {data.title}
- </a>
- </h1>
- );
- } else {
- title = (
- <h1
- className='attachment__title'
- >
- {data.title}
- </h1>
- );
- }
- }
-
- let text;
- if (data.text) {
- text = (
- <div
- className='attachment__text'
- dangerouslySetInnerHTML={{__html: this.state.text}}
- >
- </div>
- );
- }
-
- let image;
- if (data.image_url) {
- image = (
- <img
- className='attachment__image'
- src={data.image_url}
- />
- );
- }
-
- let thumb;
- if (data.thumb_url) {
- thumb = (
- <div
- className='attachment__thumb-container'
- >
- <img
- src={data.thumb_url}
- />
- </div>
- );
- }
-
- const fields = this.getFieldsTable();
-
- let useBorderStyle;
- if (data.color && data.color[0] === '#') {
- useBorderStyle = {borderLeftColor: data.color};
- }
-
- return (
- <div
- className='attachment'
- ref='attachment'
- >
- {preText}
- <div className='attachment__content'>
- <div
- className={useBorderStyle ? 'clearfix attachment__container' : 'clearfix attachment__container attachment__container--' + data.color}
- style={useBorderStyle}
- >
- {author}
- {title}
- <div>
- <div
- className={thumb ? 'attachment__body' : 'attachment__body attachment__body--no_thumb'}
- >
- {text}
- {image}
- {fields}
- </div>
- {thumb}
- <div style={{clear: 'both'}}></div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PostAttachment.propTypes = {
- intl: intlShape.isRequired,
- attachment: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(PostAttachment);
diff --git a/web/react/components/post_attachment_list.jsx b/web/react/components/post_attachment_list.jsx
deleted file mode 100644
index ae03e15d3..000000000
--- a/web/react/components/post_attachment_list.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostAttachment from './post_attachment.jsx';
-
-export default class PostAttachmentList extends React.Component {
- render() {
- let content = [];
- this.props.attachments.forEach((attachment, i) => {
- content.push(
- <PostAttachment
- attachment={attachment}
- key={'att_' + i}
- />
- );
- });
-
- return (
- <div className='attachment_list'>
- {content}
- </div>
- );
- }
-}
-
-PostAttachmentList.propTypes = {
- attachments: React.PropTypes.array.isRequired
-};
diff --git a/web/react/components/post_attachment_oembed.jsx b/web/react/components/post_attachment_oembed.jsx
deleted file mode 100644
index d76e5f02e..000000000
--- a/web/react/components/post_attachment_oembed.jsx
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class PostAttachmentOEmbed extends React.Component {
- constructor(props) {
- super(props);
- this.fetchData = this.fetchData.bind(this);
-
- this.isLoading = false;
- }
-
- componentWillMount() {
- this.setState({data: {}});
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.link !== this.props.link) {
- this.isLoading = false;
- this.fetchData(nextProps.link);
- }
- }
-
- componentDidMount() {
- this.fetchData(this.props.link);
- }
-
- fetchData(link) {
- if (!this.isLoading) {
- this.isLoading = true;
- let url = 'https://noembed.com/embed?nowrap=on';
- url += '&url=' + encodeURIComponent(link);
- url += '&maxheight=' + this.props.provider.height;
- return $.ajax({
- url,
- dataType: 'jsonp',
- success: (result) => {
- this.isLoading = false;
- if (result.error) {
- this.setState({data: {}});
- } else {
- this.setState({data: result});
- }
- },
- error: () => {
- this.setState({data: {}});
- }
- });
- }
- return null;
- }
-
- render() {
- let data = {};
- let content;
- if ($.isEmptyObject(this.state.data)) {
- content = <div style={{height: this.props.provider.height}}/>;
- } else {
- data = this.state.data;
- content = (
- <div
- style={{height: this.props.provider.height}}
- dangerouslySetInnerHTML={{__html: data.html}}
- />
- );
- }
-
- return (
- <div
- className='attachment attachment--oembed'
- ref='attachment'
- >
- <div className='attachment__content'>
- <div
- className={'clearfix attachment__container'}
- >
- <h1
- className='attachment__title'
- >
- <a
- className='attachment__title-link'
- href={data.url}
- target='_blank'
- >
- {data.title}
- </a>
- </h1>
- <div >
- <div
- className={'attachment__body attachment__body--no_thumb'}
- >
- {content}
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-PostAttachmentOEmbed.propTypes = {
- link: React.PropTypes.string.isRequired,
- provider: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
deleted file mode 100644
index 2fa4cebfe..000000000
--- a/web/react/components/post_body.jsx
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import FileAttachmentList from './file_attachment_list.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as Emoji from '../utils/emoticons.jsx';
-import Constants from '../utils/constants.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import twemoji from 'twemoji';
-import PostBodyAdditionalContent from './post_body_additional_content.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- plusOne: {
- id: 'post_body.plusOne',
- defaultMessage: ' plus 1 other file'
- },
- plusMore: {
- id: 'post_body.plusMore',
- defaultMessage: ' plus {count} other files'
- }
-});
-
-class PostBody extends React.Component {
- constructor(props) {
- super(props);
-
- this.parseEmojis = this.parseEmojis.bind(this);
- }
-
- getAllChildNodes(nodeIn) {
- var textNodes = [];
-
- function getTextNodes(node) {
- textNodes.push(node);
-
- for (var i = 0, len = node.childNodes.length; i < len; ++i) {
- getTextNodes(node.childNodes[i]);
- }
- }
-
- getTextNodes(nodeIn);
- return textNodes;
- }
-
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {
- className: 'emoji twemoji',
- base: '',
- folder: Emoji.getImagePathForEmoticon()
- });
- }
-
- componentDidMount() {
- this.parseEmojis();
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- const post = this.props.post;
- const filenames = this.props.post.filenames;
- const parentPost = this.props.parentPost;
-
- let comment = '';
- let postClass = '';
-
- if (parentPost) {
- const profile = UserStore.getProfile(parentPost.user_id);
-
- let apostrophe = '';
- let name = '...';
- if (profile != null) {
- let username = profile.username;
- if (parentPost.props &&
- parentPost.props.from_webhook &&
- parentPost.props.override_username &&
- global.window.mm_config.EnablePostUsernameOverride === 'true') {
- username = parentPost.props.override_username;
- }
-
- if (username.slice(-1) === 's') {
- apostrophe = '\'';
- } else {
- apostrophe = '\'s';
- }
- name = (
- <a
- className='theme'
- onClick={Utils.searchForTerm.bind(null, username)}
- >
- {username}
- </a>
- );
- }
-
- let message = '';
- if (parentPost.message) {
- message = Utils.replaceHtmlEntities(parentPost.message);
- } else if (parentPost.filenames.length) {
- message = parentPost.filenames[0].split('/').pop();
-
- if (parentPost.filenames.length === 2) {
- message += formatMessage(holders.plusOne);
- } else if (parentPost.filenames.length > 2) {
- message += formatMessage(holders.plusMore, {count: (parentPost.filenames.length - 1)});
- }
- }
-
- comment = (
- <div className='post__link'>
- <span>
- <FormattedMessage
- id='post_body.commentedOn'
- defaultMessage='Commented on {name}{apostrophe} message: '
- values={{
- name: (name),
- apostrophe: apostrophe
- }}
- />
- <a
- className='theme'
- onClick={this.props.handleCommentClick}
- >
- {message}
- </a>
- </span>
- </div>
- );
- }
-
- let loading;
- if (post.state === Constants.POST_FAILED) {
- postClass += ' post--fail';
- loading = (
- <a
- className='theme post-retry pull-right'
- href='#'
- onClick={this.props.retryPost}
- >
- <FormattedMessage
- id='post_body.retry'
- defaultMessage='Retry'
- />
- </a>
- );
- } else if (post.state === Constants.POST_LOADING) {
- postClass += ' post-waiting';
- loading = (
- <img
- className='post-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- }
-
- let fileAttachmentHolder = '';
- if (filenames && filenames.length > 0) {
- fileAttachmentHolder = (
- <FileAttachmentList
- filenames={filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- let message;
- let additionalContent = null;
- if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- );
- } else {
- message = (
- <span
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message)}}
- />
- );
-
- additionalContent = (
- <PostBodyAdditionalContent post={this.props.post}/>
- );
- }
-
- return (
- <div>
- {comment}
- <div className='post__body'>
- <div
- key={`${post.id}_message`}
- id={`${post.id}_message`}
- className={postClass}
- >
- {loading}
- {message}
- </div>
- {fileAttachmentHolder}
- {additionalContent}
- </div>
- </div>
- );
- }
-}
-
-PostBody.propTypes = {
- intl: intlShape.isRequired,
- post: React.PropTypes.object.isRequired,
- parentPost: React.PropTypes.object,
- retryPost: React.PropTypes.func.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(PostBody);
diff --git a/web/react/components/post_body_additional_content.jsx b/web/react/components/post_body_additional_content.jsx
deleted file mode 100644
index 70b3c8dbf..000000000
--- a/web/react/components/post_body_additional_content.jsx
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostAttachmentList from './post_attachment_list.jsx';
-import PostAttachmentOEmbed from './post_attachment_oembed.jsx';
-import PostImage from './post_image.jsx';
-import YoutubeVideo from './youtube_video.jsx';
-
-import Constants from '../utils/constants.jsx';
-import OEmbedProviders from './providers.json';
-import * as Utils from '../utils/utils.jsx';
-
-export default class PostBodyAdditionalContent extends React.Component {
- constructor(props) {
- super(props);
-
- this.getSlackAttachment = this.getSlackAttachment.bind(this);
- this.getOEmbedProvider = this.getOEmbedProvider.bind(this);
- this.generateEmbed = this.generateEmbed.bind(this);
- this.toggleEmbedVisibility = this.toggleEmbedVisibility.bind(this);
-
- this.state = {
- embedVisible: true
- };
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
- if (nextState.embedVisible !== this.state.embedVisible) {
- return true;
- }
- return false;
- }
-
- toggleEmbedVisibility() {
- this.setState({embedVisible: !this.state.embedVisible});
- }
-
- getSlackAttachment() {
- let attachments = [];
- if (this.props.post.props && this.props.post.props.attachments) {
- attachments = this.props.post.props.attachments;
- }
-
- return (
- <PostAttachmentList
- attachments={attachments}
- />
- );
- }
-
- getOEmbedProvider(link) {
- for (let i = 0; i < OEmbedProviders.length; i++) {
- for (let j = 0; j < OEmbedProviders[i].patterns.length; j++) {
- if (link.match(OEmbedProviders[i].patterns[j])) {
- return OEmbedProviders[i];
- }
- }
- }
-
- return null;
- }
-
- generateEmbed() {
- if (this.props.post.type === 'slack_attachment') {
- return this.getSlackAttachment();
- }
-
- const link = Utils.extractLinks(this.props.post.message)[0];
- if (!link) {
- return null;
- }
-
- if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_PREVIEW)) {
- const provider = this.getOEmbedProvider(link);
-
- if (provider) {
- return (
- <PostAttachmentOEmbed
- provider={provider}
- link={link}
- />
- );
- }
- }
-
- if (YoutubeVideo.isYoutubeLink(link)) {
- return (
- <YoutubeVideo
- channelId={this.props.post.channel_id}
- link={link}
- />
- );
- }
-
- for (let i = 0; i < Constants.IMAGE_TYPES.length; i++) {
- const imageType = Constants.IMAGE_TYPES[i];
- const suffix = link.substring(link.length - (imageType.length + 1));
- if (suffix === '.' + imageType || suffix === '=' + imageType) {
- return (
- <PostImage
- channelId={this.props.post.channel_id}
- link={link}
- />
- );
- }
- }
-
- return null;
- }
-
- render() {
- const generateEmbed = this.generateEmbed();
-
- if (generateEmbed) {
- let toggle;
- if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_TOGGLE)) {
- toggle = (
- <a className='post__embed-visibility'
- data-expanded={this.state.embedVisible}
- aria-label='Toggle Embed Visibility'
- onClick={this.toggleEmbedVisibility}
- />
- );
- }
-
- return (
- <div>
- {toggle}
- <div className='post__embed-container'
- hidden={!this.state.embedVisible}
- >
- {generateEmbed}
- </div>
- </div>
- );
- }
-
- return null;
- }
-}
-
-PostBodyAdditionalContent.propTypes = {
- post: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/post_deleted_modal.jsx b/web/react/components/post_deleted_modal.jsx
deleted file mode 100644
index 1f5c15aa9..000000000
--- a/web/react/components/post_deleted_modal.jsx
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-const Modal = ReactBootstrap.Modal;
-
-export default class PostDeletedModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleHide = this.handleHide.bind(this);
- }
-
- shouldComponentUpdate(nextProps) {
- return nextProps.show !== this.props.show;
- }
-
- handleHide(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
-
- this.props.onHide();
- }
-
- render() {
- return (
- <Modal
- show={this.props.show}
- onHide={this.handleHide}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='post_delete.notPosted'
- defaultMessage='Comment could not be posted'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>
- <p>
- <FormattedMessage
- id='post_delete.someone'
- defaultMessage='Someone deleted the message on which you tried to post a comment.'
- />
- </p>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-primary'
- onClick={this.handleHide}
- >
- <FormattedMessage
- id='post_delete.okay'
- defaultMessage='Okay'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-PostDeletedModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/post_focus_view.jsx b/web/react/components/post_focus_view.jsx
deleted file mode 100644
index fd654f502..000000000
--- a/web/react/components/post_focus_view.jsx
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostsView from './posts_view.jsx';
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PostFocusView extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChannelChange = this.onChannelChange.bind(this);
- this.onPostsChange = this.onPostsChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
-
- const focusedPostId = PostStore.getFocusedPostId();
-
- this.state = {
- scrollType: PostsView.SCROLL_TYPE_POST,
- scrollPostId: focusedPostId,
- postList: PostStore.getVisiblePosts(focusedPostId),
- atTop: PostStore.getVisibilityAtTop(focusedPostId),
- atBottom: PostStore.getVisibilityAtBottom(focusedPostId),
- currentUser: UserStore.getCurrentUser()
- };
- }
-
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChannelChange);
- PostStore.addChangeListener(this.onPostsChange);
- UserStore.addChangeListener(this.onUserChange);
- }
-
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChannelChange);
- PostStore.removeChangeListener(this.onPostsChange);
- UserStore.removeChangeListener(this.onUserChange);
- }
-
- onChannelChange() {
- this.setState({
- scrollType: PostsView.SCROLL_TYPE_POST
- });
- }
-
- onUserChange() {
- this.setState({currentUser: UserStore.getCurrentUser()});
- }
-
- onPostsChange() {
- const focusedPostId = PostStore.getFocusedPostId();
- if (focusedPostId == null) {
- return;
- }
-
- this.setState({
- scrollPostId: focusedPostId,
- postList: PostStore.getVisiblePosts(focusedPostId),
- atTop: PostStore.getVisibilityAtTop(focusedPostId),
- atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
- });
- }
-
- handlePostsViewScroll() {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
-
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsFocusedTopEvent();
- }
-
- loadMorePostsBottom() {
- GlobalActions.emitLoadMorePostsFocusedBottomEvent();
- }
-
- getIntroMessage() {
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>
- <FormattedMessage
- id='post_focus_view.beginning'
- defaultMessage='Beginning of Channel Archives'
- />
- </h4>
- </div>
- );
- }
-
- render() {
- const postsToHighlight = {};
- postsToHighlight[this.state.scrollPostId] = true;
-
- if (!this.state.currentUser || !this.state.postList) {
- return null;
- }
-
- return (
- <div id='post-list'>
- <PostsView
- key={'postfocusview'}
- isActive={true}
- postList={this.state.postList}
- scrollType={this.state.scrollType}
- scrollPostId={this.state.scrollPostId}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={this.loadMorePostsBottom}
- showMoreMessagesTop={!this.state.atTop}
- showMoreMessagesBottom={!this.state.atBottom}
- introText={this.getIntroMessage()}
- messageSeparatorTime={0}
- postsToHighlight={postsToHighlight}
- profiles={this.props.profiles}
- currentUser={this.state.currentUser}
- />
- </div>
- );
- }
-}
-PostFocusView.defaultProps = {
-};
-
-PostFocusView.propTypes = {
- profiles: React.PropTypes.object
-};
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx
deleted file mode 100644
index 966775dad..000000000
--- a/web/react/components/post_header.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserProfile from './user_profile.jsx';
-import PostInfo from './post_info.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-export default class PostHeader extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- const post = this.props.post;
-
- let userProfile = <UserProfile user={this.props.user}/>;
- let botIndicator;
-
- if (post.props && post.props.from_webhook) {
- if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
- userProfile = (
- <UserProfile
- user={this.props.user}
- overwriteName={post.props.override_username}
- disablePopover={true}
- />
- );
- }
-
- botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
- } else if (Utils.isSystemMessage(post)) {
- userProfile = (
- <UserProfile
- user={{}}
- overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
- overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
- disablePopover={true}
- />
- );
- }
-
- return (
- <ul className='post__header'>
- <li className='col col__name'>{userProfile}</li>
- {botIndicator}
- <li className='col'>
- <PostInfo
- post={post}
- commentCount={this.props.commentCount}
- handleCommentClick={this.props.handleCommentClick}
- allowReply='true'
- isLastComment={this.props.isLastComment}
- sameUser={this.props.sameUser}
- currentUser={this.props.currentUser}
- />
- </li>
- </ul>
- );
- }
-}
-
-PostHeader.defaultProps = {
- post: null,
- commentCount: 0,
- isLastComment: false,
- sameUser: false
-};
-PostHeader.propTypes = {
- post: React.PropTypes.object.isRequired,
- user: React.PropTypes.object,
- currentUser: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number.isRequired,
- isLastComment: React.PropTypes.bool.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired,
- sameUser: React.PropTypes.bool.isRequired
-};
diff --git a/web/react/components/post_image.jsx b/web/react/components/post_image.jsx
deleted file mode 100644
index da4a25794..000000000
--- a/web/react/components/post_image.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class PostImageEmbed extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleLoadComplete = this.handleLoadComplete.bind(this);
- this.handleLoadError = this.handleLoadError.bind(this);
-
- this.state = {
- loaded: false,
- errored: false
- };
- }
-
- componentWillMount() {
- this.loadImg(this.props.link);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.link !== this.props.link) {
- this.setState({
- loaded: false,
- errored: false
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if (!this.state.loaded && prevProps.link !== this.props.link) {
- this.loadImg(this.props.link);
- }
- }
-
- loadImg(src) {
- const img = new Image();
- img.onload = this.handleLoadComplete;
- img.onerror = this.handleLoadError;
- img.src = src;
- }
-
- handleLoadComplete() {
- this.setState({
- loaded: true
- });
- }
-
- handleLoadError() {
- this.setState({
- errored: true,
- loaded: true
- });
- }
-
- render() {
- if (this.state.errored) {
- return null;
- }
-
- if (!this.state.loaded) {
- return (
- <img
- className='img-div placeholder'
- height='500px'
- />
- );
- }
-
- return (
- <img
- className='img-div'
- src={this.props.link}
- />
- );
- }
-}
-
-PostImageEmbed.propTypes = {
- link: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
deleted file mode 100644
index d0a4c828e..000000000
--- a/web/react/components/post_info.jsx
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import TimeSince from './time_since.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class PostInfo extends React.Component {
- constructor(props) {
- super(props);
-
- this.dropdownPosition = this.dropdownPosition.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
- this.removePost = this.removePost.bind(this);
- }
- dropdownPosition(e) {
- var position = $('#post-list').height() - $(e.target).offset().top;
- var dropdown = $(e.target).next('.dropdown-menu');
- if (position < dropdown.height()) {
- dropdown.addClass('bottom');
- }
- }
- createDropdown() {
- var post = this.props.post;
- var isOwner = this.props.currentUser.id === post.user_id;
- var isAdmin = Utils.isAdmin(this.props.currentUser.roles);
-
- if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || Utils.isPostEphemeral(post)) {
- return '';
- }
-
- var type = 'Post';
- if (post.root_id && post.root_id.length > 0) {
- type = 'Comment';
- }
-
- var dropdownContents = [];
- var dataComments = 0;
- if (type === 'Post') {
- dataComments = this.props.commentCount;
- }
-
- if (this.props.allowReply === 'true') {
- dropdownContents.push(
- <li
- key='replyLink'
- role='presentation'
- >
- <a
- className='link__reply theme'
- href='#'
- onClick={this.props.handleCommentClick}
- >
- <FormattedMessage
- id='post_info.reply'
- defaultMessage='Reply'
- />
- </a>
- </li>
- );
- }
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='copyLink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='post_info.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- key='deletePost'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, dataComments)}
- >
- <FormattedMessage
- id='post_info.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- key='editPost'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#post_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- data-comments={dataComments}
- >
- <FormattedMessage
- id='post_info.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <div>
- <a
- href='#'
- className='dropdown-toggle post__dropdown theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- onClick={this.dropdownPosition}
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
-
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
-
- removePost() {
- GlobalActions.emitRemovePost(this.props.post);
- }
- createRemovePostButton(post) {
- if (!Utils.isPostEphemeral(post)) {
- return null;
- }
-
- return (
- <a
- href='#'
- className='post__remove theme'
- type='button'
- onClick={this.removePost}
- >
- {'Ɨ'}
- </a>
- );
- }
- render() {
- var post = this.props.post;
- var comments = '';
- var showCommentClass = '';
- var commentCountText = this.props.commentCount;
-
- if (this.props.commentCount >= 1) {
- showCommentClass = ' icon--show';
- } else {
- commentCountText = '';
- }
-
- if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && !Utils.isPostEphemeral(post)) {
- comments = (
- <a
- href='#'
- className={'comment-icon__container' + showCommentClass}
- onClick={this.props.handleCommentClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- {commentCountText}
- </a>
- );
- }
-
- var dropdown = this.createDropdown();
-
- return (
- <ul className='post__header post__header--info'>
- <li className='col'>
- <TimeSince
- eventTime={post.create_at}
- sameUser={this.props.sameUser}
- />
- </li>
- <li className='col col__reply'>
- <div
- className='dropdown'
- ref='dotMenu'
- >
- {dropdown}
- </div>
- {comments}
- {this.createRemovePostButton(post)}
- </li>
- </ul>
- );
- }
-}
-
-PostInfo.defaultProps = {
- post: null,
- commentCount: 0,
- isLastComment: false,
- allowReply: false,
- sameUser: false
-};
-PostInfo.propTypes = {
- post: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number.isRequired,
- isLastComment: React.PropTypes.bool.isRequired,
- allowReply: React.PropTypes.string.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired,
- sameUser: React.PropTypes.bool.isRequired,
- currentUser: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
deleted file mode 100644
index 0a9232850..000000000
--- a/web/react/components/posts_view.jsx
+++ /dev/null
@@ -1,604 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Post from './post.jsx';
-import Constants from '../utils/constants.jsx';
-import DelayedAction from '../utils/delayed_action.jsx';
-
-import {FormattedDate, FormattedMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-export default class PostsView extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateState = this.updateState.bind(this);
- this.handleScroll = this.handleScroll.bind(this);
- this.handleScrollStop = this.handleScrollStop.bind(this);
- this.isAtBottom = this.isAtBottom.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
- this.createPosts = this.createPosts.bind(this);
- this.updateScrolling = this.updateScrolling.bind(this);
- this.handleResize = this.handleResize.bind(this);
- this.scrollToBottom = this.scrollToBottom.bind(this);
- this.scrollToBottomAnimated = this.scrollToBottomAnimated.bind(this);
-
- this.jumpToPostNode = null;
- this.wasAtBottom = true;
- this.scrollHeight = 0;
-
- this.scrollStopAction = new DelayedAction(this.handleScrollStop);
-
- this.state = {
- displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
- isScrolling: false,
- topPostId: null
- };
- }
- static get SCROLL_TYPE_FREE() {
- return 1;
- }
- static get SCROLL_TYPE_BOTTOM() {
- return 2;
- }
- static get SCROLL_TYPE_SIDEBAR_OPEN() {
- return 3;
- }
- static get SCROLL_TYPE_NEW_MESSAGE() {
- return 4;
- }
- static get SCROLL_TYPE_POST() {
- return 5;
- }
- updateState() {
- this.setState({displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false')});
- }
- isAtBottom() {
- // consider the view to be at the bottom if it's within this many pixels of the bottom
- const atBottomMargin = 10;
-
- return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - atBottomMargin;
- }
- handleScroll() {
- // HACK FOR RHS -- REMOVE WHEN RHS DIES
- const childNodes = this.refs.postlistcontent.childNodes;
- for (let i = 0; i < childNodes.length; i++) {
- // If the node is 1/3 down the page
- if (childNodes[i].offsetTop > (this.refs.postlist.scrollTop + (this.refs.postlist.offsetHeight / 3))) {
- this.jumpToPostNode = childNodes[i];
- break;
- }
- }
- this.wasAtBottom = this.isAtBottom();
- if (!this.jumpToPostNode && childNodes.length > 0) {
- this.jumpToPostNode = childNodes[childNodes.length - 1];
- }
-
- // --- --------
-
- this.props.postViewScrolled(this.isAtBottom());
- this.prevScrollHeight = this.refs.postlist.scrollHeight;
- this.prevOffsetTop = this.jumpToPostNode.offsetTop;
-
- this.updateFloatingTimestamp();
-
- if (!this.state.isScrolling) {
- this.setState({
- isScrolling: true
- });
- }
-
- this.scrollStopAction.fireAfter(2000);
- }
- handleScrollStop() {
- this.setState({
- isScrolling: false
- });
- }
- updateFloatingTimestamp() {
- // skip this in non-mobile view since that's when the timestamp is visible
- if ($(window).width() > 768) {
- return;
- }
-
- if (this.props.postList) {
- // iterate through posts starting at the bottom since users are more likely to be viewing newer posts
- for (let i = 0; i < this.props.postList.order.length; i++) {
- const id = this.props.postList.order[i];
- const element = ReactDOM.findDOMNode(this.refs[id]);
-
- if (!element || element.offsetTop + element.clientHeight <= this.refs.postlist.scrollTop) {
- // this post is off the top of the screen so the last one is at the top of the screen
- let topPostId;
-
- if (i > 0) {
- topPostId = this.props.postList.order[i - 1];
- } else {
- // the first post we look at should always be on the screen, but handle that case anyway
- topPostId = id;
- }
-
- if (topPostId !== this.state.topPostId) {
- this.setState({
- topPostId
- });
- }
-
- break;
- }
- }
- }
- }
- loadMorePostsTop() {
- this.props.loadMorePostsTopClicked();
- }
- loadMorePostsBottom() {
- this.props.loadMorePostsBottomClicked();
- }
- createPosts(posts, order) {
- const postCtls = [];
- let previousPostDay = new Date(0);
- const userId = this.props.currentUser.id;
- const profiles = this.props.profiles || {};
-
- let renderedLastViewed = false;
-
- for (let i = order.length - 1; i >= 0; i--) {
- const post = posts[order[i]];
- const parentPost = posts[post.parent_id];
- const prevPost = posts[order[i + 1]];
- const postUserId = Utils.isSystemMessage(post) ? '' : post.user_id;
-
- // If the post is a comment whose parent has been deleted, don't add it to the list.
- if (parentPost && parentPost.state === Constants.POST_DELETED) {
- continue;
- }
-
- let sameUser = false;
- let sameRoot = false;
- let hideProfilePic = false;
-
- if (prevPost) {
- 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);
- const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPost.user_id;
- let prevWebhookName = '';
- if (prevPost.props && prevPost.props.override_username) {
- prevWebhookName = prevPost.props.override_username;
- }
- let curWebhookName = '';
- if (post.props && post.props.override_username) {
- curWebhookName = post.props.override_username;
- }
-
- // consider posts from the same user if:
- // the previous post was made by the same user as the current post,
- // the previous post was made within 5 minutes of the current post,
- // the previous post and current post are both from webhooks or both not,
- // the previous post and current post have the same webhook usernames
- if (prevPostUserId === postUserId &&
- post.create_at - prevPost.create_at <= 1000 * 60 * 5 &&
- postFromWebhook === prevPostFromWebhook &&
- prevWebhookName === curWebhookName) {
- sameUser = true;
- }
-
- // consider posts from the same root if:
- // the current post is a comment,
- // the current post has the same root as the previous post
- if (postIsComment && (prevPost.id === post.root_id || prevPost.root_id === post.root_id)) {
- sameRoot = true;
- }
-
- // consider posts from the same root if:
- // the current post is not a comment,
- // the previous post is not a comment,
- // the previous post is from the same user
- if (!postIsComment && !prevPostIsComment && sameUser) {
- sameRoot = true;
- }
-
- // hide the profile pic if:
- // 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 and current post are both from webhooks or both not,
- // the previous post and current post have the same webhook usernames
- if (prevPostUserId === postUserId &&
- !prevPostIsComment &&
- !postIsComment &&
- postFromWebhook === prevPostFromWebhook &&
- prevWebhookName === curWebhookName) {
- hideProfilePic = true;
- }
- }
-
- // check if it's the last comment in a consecutive string of comments on the same post
- // it is the last comment if it is last post in the channel or the next post has a different root post
- const isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
-
- const keyPrefix = post.id ? post.id : i;
-
- const shouldHighlight = this.props.postsToHighlight && this.props.postsToHighlight.hasOwnProperty(post.id);
-
- let profile;
- if (this.props.currentUser.id === post.user_id) {
- profile = this.props.currentUser;
- } else {
- profile = profiles[post.user_id];
- }
-
- const postCtl = (
- <Post
- key={keyPrefix + 'postKey'}
- ref={post.id}
- sameUser={sameUser}
- sameRoot={sameRoot}
- post={post}
- parentPost={parentPost}
- posts={posts}
- hideProfilePic={hideProfilePic}
- isLastComment={isLastComment}
- shouldHighlight={shouldHighlight}
- onClick={() => GlobalActions.emitPostFocusEvent(post.id)} //eslint-disable-line no-loop-func
- displayNameType={this.state.displayNameType}
- user={profile}
- currentUser={this.props.currentUser}
- />
- );
-
- const currentPostDay = Utils.getDateForUnixTicks(post.create_at);
- if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
- postCtls.push(
- <div
- key={currentPostDay.toDateString()}
- className='date-separator'
- >
- <hr className='separator__hr'/>
- <div className='separator__text'>
- <FormattedDate
- value={currentPostDay}
- weekday='short'
- month='short'
- day='2-digit'
- year='numeric'
- />
- </div>
- </div>
- );
- }
-
- if (postUserId !== userId &&
- this.props.messageSeparatorTime !== 0 &&
- post.create_at > this.props.messageSeparatorTime &&
- !renderedLastViewed) {
- renderedLastViewed = true;
-
- // Temporary fix to solve ie11 rendering issue
- let newSeparatorId = '';
- if (!Utils.isBrowserIE()) {
- newSeparatorId = 'new_message_' + post.id;
- }
- postCtls.push(
- <div
- id={newSeparatorId}
- key='unviewed'
- ref='newMessageSeparator'
- className='new-separator'
- >
- <hr
- className='separator__hr'
- />
- <div className='separator__text'>
- <FormattedMessage
- id='posts_view.newMsg'
- defaultMessage='New Messages'
- />
- </div>
- </div>
- );
- }
- postCtls.push(postCtl);
- previousPostDay = currentPostDay;
- }
-
- return postCtls;
- }
- updateScrolling() {
- if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) {
- this.scrollToBottom();
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_NEW_MESSAGE) {
- window.requestAnimationFrame(() => {
- // If separator exists scroll to it. Otherwise scroll to bottom.
- if (this.refs.newMessageSeparator) {
- var objDiv = this.refs.postlist;
- objDiv.scrollTop = this.refs.newMessageSeparator.offsetTop; //scrolls node to top of Div
- } else if (this.refs.postlist) {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- }
- });
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) {
- window.requestAnimationFrame(() => {
- const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]);
- if (postNode == null) {
- return;
- }
- postNode.scrollIntoView();
- if (this.refs.postlist.scrollTop === postNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
- } else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
- }
- });
- } else if (this.props.scrollType === PostsView.SCROLL_TYPE_SIDEBAR_OPEN) {
- // If we are at the bottom then stay there
- if (this.wasAtBottom) {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- } else {
- window.requestAnimationFrame(() => {
- this.jumpToPostNode.scrollIntoView();
- if (this.refs.postlist.scrollTop === this.jumpToPostNode.offsetTop) {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
- } else {
- this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop);
- }
- });
- }
- } else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) {
- window.requestAnimationFrame(() => {
- // Only need to jump if we added posts to the top.
- if (this.jumpToPostNode && (this.jumpToPostNode.offsetTop !== this.prevOffsetTop)) {
- this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight);
- }
- });
- }
- }
- handleResize() {
- this.updateScrolling();
- }
- scrollToBottom() {
- window.requestAnimationFrame(() => {
- this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
- });
- }
- scrollToBottomAnimated() {
- var postList = $(this.refs.postlist);
- postList.animate({scrollTop: this.refs.postlist.scrollHeight}, '500');
- }
- componentDidMount() {
- if (this.props.postList != null) {
- this.updateScrolling();
- }
- window.addEventListener('resize', this.handleResize);
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.handleResize);
- }
- componentDidUpdate() {
- if (this.props.postList != null) {
- this.updateScrolling();
- }
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.isActive && nextProps.isActive) {
- this.updateState();
- PreferenceStore.addChangeListener(this.updateState);
- } else if (this.props.isActive && !nextProps.isActive) {
- PreferenceStore.removeChangeListener(this.updateState);
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (this.props.isActive !== nextProps.isActive) {
- return true;
- }
- if (this.props.postList !== nextProps.postList) {
- return true;
- }
- if (this.props.scrollPostId !== nextProps.scrollPostId) {
- return true;
- }
- if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
- return true;
- }
- if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.props.postList, nextProps.postList)) {
- return true;
- }
- if (nextState.displayNameType !== this.state.displayNameType) {
- return true;
- }
- if (this.state.topPostId !== nextState.topPostId) {
- return true;
- }
- if (this.state.isScrolling !== nextState.isScrolling) {
- return true;
- }
- if (!Utils.areObjectsEqual(this.props.profiles, nextProps.profiles)) {
- return true;
- }
-
- return false;
- }
- render() {
- let posts = [];
- let order = [];
- let moreMessagesTop;
- let moreMessagesBottom;
- let postElements;
- let activeClass = 'inactive';
- if (this.props.postList != null) {
- posts = this.props.postList.posts;
- order = this.props.postList.order;
-
- // Create intro message or top loadmore link
- if (this.props.showMoreMessagesTop) {
- moreMessagesTop = (
- <a
- ref='loadmoretop'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsTop}
- >
- <FormattedMessage
- id='posts_view.loadMore'
- defaultMessage='Load more messages'
- />
- </a>
- );
- } else {
- moreMessagesTop = this.props.introText;
- }
-
- // Give option to load more posts at bottom if nessisary
- if (this.props.showMoreMessagesBottom) {
- moreMessagesBottom = (
- <a
- ref='loadmorebottom'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePostsBottom}
- >
- <FormattedMessage id='posts_view.loadMore'/>
- </a>
- );
- } else {
- moreMessagesBottom = null;
- }
-
- // Create post elements
- postElements = this.createPosts(posts, order);
-
- // Show ourselves if we are marked active
- if (this.props.isActive) {
- activeClass = '';
- }
- }
-
- let topPost = null;
- if (this.state.topPostId) {
- topPost = this.props.postList.posts[this.state.topPostId];
- }
-
- return (
- <div className={activeClass}>
- <FloatingTimestamp
- isScrolling={this.state.isScrolling}
- post={topPost}
- />
- <ScrollToBottomArrows
- isScrolling={this.state.isScrolling}
- atBottom={this.wasAtBottom}
- onClick={this.scrollToBottomAnimated}
- />
- <div
- ref='postlist'
- className='post-list-holder-by-time'
- onScroll={this.handleScroll}
- >
- <div className='post-list__table'>
- <div
- ref='postlistcontent'
- className='post-list__content'
- >
- {moreMessagesTop}
- {postElements}
- {moreMessagesBottom}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-PostsView.defaultProps = {
-};
-
-PostsView.propTypes = {
- isActive: React.PropTypes.bool,
- postList: React.PropTypes.object,
- profiles: React.PropTypes.object.isRequired,
- scrollPostId: React.PropTypes.string,
- scrollType: React.PropTypes.number,
- postViewScrolled: React.PropTypes.func.isRequired,
- loadMorePostsTopClicked: React.PropTypes.func.isRequired,
- loadMorePostsBottomClicked: React.PropTypes.func.isRequired,
- showMoreMessagesTop: React.PropTypes.bool,
- showMoreMessagesBottom: React.PropTypes.bool,
- introText: React.PropTypes.element,
- messageSeparatorTime: React.PropTypes.number,
- postsToHighlight: React.PropTypes.object,
- currentUser: React.PropTypes.object.isRequired
-};
-
-function FloatingTimestamp({isScrolling, post}) {
- // only show on mobile
- if ($(window).width() > 768) {
- return <noscript/>;
- }
-
- if (!post) {
- return <noscript/>;
- }
-
- const dateString = (
- <FormattedDate
- value={post.create_at}
- weekday='short'
- day='2-digit'
- month='short'
- year='numeric'
- />
- );
-
- let className = 'post-list__timestamp';
- if (isScrolling) {
- className += ' scrolling';
- }
-
- return (
- <div className={className}>
- <span>{dateString}</span>
- </div>
- );
-}
-
-FloatingTimestamp.propTypes = {
- isScrolling: React.PropTypes.bool.isRequired,
- post: React.PropTypes.object
-};
-
-function ScrollToBottomArrows({isScrolling, atBottom, onClick}) {
- // only show on mobile
- if ($(window).width() > 768) {
- return <noscript/>;
- }
-
- let className = 'post-list__arrows';
- if (isScrolling && !atBottom) {
- className += ' scrolling';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.SCROLL_BOTTOM_ICON}}/>
- </div>
- );
-}
-
-ScrollToBottomArrows.propTypes = {
- isScrolling: React.PropTypes.bool.isRequired,
- atBottom: React.PropTypes.bool.isRequired,
- onClick: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/posts_view_container.jsx b/web/react/components/posts_view_container.jsx
deleted file mode 100644
index b361779d2..000000000
--- a/web/react/components/posts_view_container.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostsView from './posts_view.jsx';
-import LoadingScreen from './loading_screen.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import * as Utils from '../utils/utils.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {createChannelIntroMessage} from '../utils/channel_intro_messages.jsx';
-
-export default class PostsViewContainer extends React.Component {
- constructor() {
- super();
-
- this.onChannelChange = this.onChannelChange.bind(this);
- this.onChannelLeave = this.onChannelLeave.bind(this);
- this.onPostsChange = this.onPostsChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
- this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
- this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
-
- const currentChannelId = ChannelStore.getCurrentId();
- const state = {
- scrollType: PostsView.SCROLL_TYPE_BOTTOM,
- scrollPost: null,
- currentUser: UserStore.getCurrentUser()
- };
- if (currentChannelId) {
- Object.assign(state, {
- currentChannelIndex: 0,
- channels: [currentChannelId],
- postLists: [this.getChannelPosts(currentChannelId)],
- atTop: [PostStore.getVisibilityAtTop(currentChannelId)]
- });
- } else {
- Object.assign(state, {
- currentChannelIndex: null,
- channels: [],
- postLists: [],
- atTop: []
- });
- }
-
- state.showInviteModal = false;
- this.state = state;
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChannelChange);
- ChannelStore.addLeaveListener(this.onChannelLeave);
- PostStore.addChangeListener(this.onPostsChange);
- PostStore.addPostsViewJumpListener(this.handlePostsViewJumpRequest);
- UserStore.addChangeListener(this.onUserChange);
- }
- componentWillUnmount() {
- ChannelStore.removeChangeListener(this.onChannelChange);
- ChannelStore.removeLeaveListener(this.onChannelLeave);
- PostStore.removeChangeListener(this.onPostsChange);
- PostStore.removePostsViewJumpListener(this.handlePostsViewJumpRequest);
- UserStore.removeChangeListener(this.onUserChange);
- }
- onUserChange() {
- this.setState({currentUser: UserStore.getCurrentUser()});
- }
- handlePostsViewJumpRequest(type, post) {
- switch (type) {
- case Constants.PostsViewJumpTypes.BOTTOM:
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- break;
- case Constants.PostsViewJumpTypes.POST:
- this.setState({
- scrollType: PostsView.SCROLL_TYPE_POST,
- scrollPost: post
- });
- break;
- case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
- this.setState({scrollType: PostsView.SCROLL_TYPE_SIDEBAR_OPEN});
- break;
- }
- }
- onChannelChange() {
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const channels = this.state.channels.slice();
- const channelId = ChannelStore.getCurrentId();
-
- // Has the channel really changed?
- if (channelId === channels[this.state.currentChannelIndex]) {
- return;
- }
-
- let lastViewed = Number.MAX_VALUE;
- const member = ChannelStore.getMember(channelId);
- if (member != null) {
- lastViewed = member.last_viewed_at;
- }
-
- let newIndex = channels.indexOf(channelId);
- if (newIndex === -1) {
- newIndex = channels.length;
- channels.push(channelId);
- atTop[newIndex] = PostStore.getVisibilityAtTop(channelId);
- }
-
- // make sure we have the latest posts from the store
- postLists[newIndex] = this.getChannelPosts(channelId);
-
- this.setState({
- currentChannelIndex: newIndex,
- currentLastViewed: lastViewed,
- scrollType: PostsView.SCROLL_TYPE_NEW_MESSAGE,
- channels,
- postLists,
- atTop});
- }
- onChannelLeave(id) {
- const postLists = this.state.postLists.slice();
- const channels = this.state.channels.slice();
- const atTop = this.state.atTop.slice();
- const index = channels.indexOf(id);
- if (index !== -1) {
- postLists.splice(index, 1);
- channels.splice(index, 1);
- atTop.splice(index, 1);
- }
- this.setState({channels, postLists, atTop});
- }
- onPostsChange() {
- const channels = this.state.channels;
- const postLists = this.state.postLists.slice();
- const atTop = this.state.atTop.slice();
- const currentChannelId = channels[this.state.currentChannelIndex];
- const newPostsView = this.getChannelPosts(currentChannelId);
-
- postLists[this.state.currentChannelIndex] = newPostsView;
- atTop[this.state.currentChannelIndex] = PostStore.getVisibilityAtTop(currentChannelId);
- this.setState({postLists, atTop});
- }
- getChannelPosts(id) {
- return PostStore.getVisiblePosts(id);
- }
- loadMorePostsTop() {
- GlobalActions.emitLoadMorePostsEvent();
- }
- handlePostsViewScroll(atBottom) {
- if (atBottom) {
- this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
- } else {
- this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- return false;
- }
- render() {
- const postLists = this.state.postLists;
- const channels = this.state.channels;
- const currentChannelId = channels[this.state.currentChannelIndex];
- const channel = ChannelStore.get(currentChannelId);
-
- if (!this.state.currentUser || !channel) {
- return null;
- }
-
- const postListCtls = [];
- for (let i = 0; i < channels.length; i++) {
- const isActive = (channels[i] === currentChannelId);
- postListCtls.push(
- <PostsView
- key={'postsviewkey' + i}
- isActive={isActive}
- postList={postLists[i]}
- scrollType={this.state.scrollType}
- scrollPostId={this.state.scrollPost}
- postViewScrolled={this.handlePostsViewScroll}
- loadMorePostsTopClicked={this.loadMorePostsTop}
- loadMorePostsBottomClicked={() => {
- // Do Nothing
- }}
- showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]}
- showMoreMessagesBottom={false}
- introText={channel ? createChannelIntroMessage(channel) : null}
- messageSeparatorTime={this.state.currentLastViewed}
- profiles={this.props.profiles}
- currentUser={this.state.currentUser}
- />
- );
- if (!postLists[i] && isActive) {
- postListCtls.push(
- <LoadingScreen
- position='absolute'
- key='loading'
- />
- );
- }
- }
-
- return (
- <div id='post-list'>
- {postListCtls}
- </div>
- );
- }
-}
-
-PostsViewContainer.propTypes = {
- profiles: React.PropTypes.object
-};
diff --git a/web/react/components/providers.json b/web/react/components/providers.json
deleted file mode 100644
index b5899c225..000000000
--- a/web/react/components/providers.json
+++ /dev/null
@@ -1,376 +0,0 @@
-[
- {
- "patterns": [
- "http://(?:www\\.)?xkcd\\.com/\\d+/?"
- ],
- "name": "XKCD",
- "height": 110
- },
- {
- "patterns": [
- "https?://soundcloud.com/.*/.*"
- ],
- "name": "SoundCloud",
- "height": 140
- },
- {
- "patterns": [
- "https?://(?:www\\.)?flickr\\.com/.*",
- "https?://flic\\.kr/p/[a-zA-Z0-9]+"
- ],
- "name": "Flickr",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.ted\\.com/talks/.+\\.html"
- ],
- "name": "TED",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?theverge\\.com/\\d{4}/\\d{1,2}/\\d{1,2}/\\d+/[^/]+/?$"
- ],
- "name": "The Verge",
- "height": 110
- },
- {
- "patterns": [
- "http://.*\\.viddler\\.com/.*"
- ],
- "name": "Viddler",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?avclub\\.com/article/[^/]+/?$"
- ],
- "name": "The AV Club",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?wired\\.com/([^/]+/)?\\d+/\\d+/[^/]+/?$"
- ],
- "name": "Wired",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.theonion\\.com/articles/[^/]+/?"
- ],
- "name": "The Onion",
- "height": 110
- },
- {
- "patterns": [
- "http://yfrog\\.com/[0-9a-zA-Z]+/?$"
- ],
- "name": "YFrog",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.duffelblog\\.com/\\d{4}/\\d{1,2}/[^/]+/?$"
- ],
- "name": "The Duffel Blog",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.clickhole\\.com/article/[^/]+/?"
- ],
- "name": "Clickhole",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www.)?skitch.com/([^/]+)/[^/]+/.+",
- "http://skit.ch/[^/]+"
- ],
- "name": "Skitch",
- "height": 110
- },
- {
- "patterns": [
- "https?://(alpha|posts|photos)\\.app\\.net/.*"
- ],
- "name": "ADN",
- "height": 110
- },
- {
- "patterns": [
- "https?://gist\\.github\\.com/(?:[-0-9a-zA-Z]+/)?([0-9a-fA-f]+)"
- ],
- "name": "Gist",
- "height": 110
- },
- {
- "patterns": [
- "https?://www\\.(dropbox\\.com/s/.+\\.(?:jpg|png|gif))",
- "https?://db\\.tt/[a-zA-Z0-9]+"
- ],
- "name": "Dropbox",
- "height": 110
- },
- {
- "patterns": [
- "https?://[^\\.]+\\.wikipedia\\.org/wiki/(?!Talk:)[^#]+(?:#(.+))?"
- ],
- "name": "Wikipedia",
- "height": 110
- },
- {
- "patterns": [
- "http://www.traileraddict.com/trailer/[^/]+/trailer"
- ],
- "name": "TrailerAddict",
- "height": 110
- },
- {
- "patterns": [
- "http://lockerz\\.com/[sd]/\\d+"
- ],
- "name": "Lockerz",
- "height": 110
- },
- {
- "patterns": [
- "http://gifuk\\.com/s/[0-9a-f]{16}"
- ],
- "name": "GIFUK",
- "height": 110
- },
- {
- "patterns": [
- "http://trailers\\.apple\\.com/trailers/[^/]+/[^/]+"
- ],
- "name": "iTunes Movie Trailers",
- "height": 110
- },
- {
- "patterns": [
- "http://gfycat\\.com/([a-zA-Z]+)"
- ],
- "name": "Gfycat",
- "height": 110
- },
- {
- "patterns": [
- "http://bash\\.org/\\?(\\d+)"
- ],
- "name": "Bash.org",
- "height": 110
- },
- {
- "patterns": [
- "http://arstechnica\\.com/[^/]+/\\d+/\\d+/[^/]+/?$"
- ],
- "name": "Ars Technica",
- "height": 110
- },
- {
- "patterns": [
- "http://imgur\\.com/gallery/[0-9a-zA-Z]+"
- ],
- "name": "Imgur",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.asciiartfarts\\.com/[0-9]+\\.html"
- ],
- "name": "ASCII Art Farts",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.monoprice\\.com/products/product\\.asp\\?.*p_id=\\d+"
- ],
- "name": "Monoprice",
- "height": 110
- },
- {
- "patterns": [
- "http://boingboing\\.net/\\d{4}/\\d{2}/\\d{2}/[^/]+\\.html"
- ],
- "name": "Boing Boing",
- "height": 110
- },
- {
- "patterns": [
- "https?://github\\.com/([^/]+)/([^/]+)/commit/(.+)",
- "http://git\\.io/[_0-9a-zA-Z]+"
- ],
- "name": "Github Commit",
- "height": 110
- },
- {
- "patterns": [
- "https?://open\\.spotify\\.com/(track|album)/([0-9a-zA-Z]{22})"
- ],
- "name": "Spotify",
- "height": 110
- },
- {
- "patterns": [
- "https?://path\\.com/p/([0-9a-zA-Z]+)$"
- ],
- "name": "Path",
- "height": 110
- },
- {
- "patterns": [
- "http://www.funnyordie.com/videos/[^/]+/.+"
- ],
- "name": "Funny or Die",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?twitpic\\.com/([^/]+)"
- ],
- "name": "Twitpic",
- "height": 110
- },
- {
- "patterns": [
- "https?://www\\.giantbomb\\.com/videos/[^/]+/\\d+-\\d+/?"
- ],
- "name": "GiantBomb",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?beeradvocate\\.com/beer/profile/\\d+/\\d+"
- ],
- "name": "Beer Advocate",
- "height": 110
- },
- {
- "patterns": [
- "http://(?:www\\.)?imdb.com/title/(tt\\d+)"
- ],
- "name": "IMDB",
- "height": 110
- },
- {
- "patterns": [
- "http://cl\\.ly/(?:image/)?[0-9a-zA-Z]+/?$"
- ],
- "name": "CloudApp",
- "height": 110
- },
- {
- "patterns": [
- "http://clyp\\.it/.*"
- ],
- "name": "Clyp",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.hulu\\.com/watch/.*"
- ],
- "name": "Hulu",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www|mobile\\.)?twitter\\.com/(?:#!/)?[^/]+/status(?:es)?/(\\d+)/?$",
- "https?://t\\.co/[a-zA-Z0-9]+"
- ],
- "name": "Twitter",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?vimeo\\.com/.+"
- ],
- "name": "Vimeo",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.amazon\\.com/(?:.+/)?[gd]p/(?:product/)?(?:tags-on-product/)?([a-zA-Z0-9]+)",
- "http://amzn\\.com/([^/]+)"
- ],
- "name": "Amazon",
- "height": 110
- },
- {
- "patterns": [
- "http://qik\\.com/video/.*"
- ],
- "name": "Qik",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.rdio\\.com/artist/[^/]+/album/[^/]+/?",
- "http://www\\.rdio\\.com/artist/[^/]+/album/[^/]+/track/[^/]+/?",
- "http://www\\.rdio\\.com/people/[^/]+/playlists/\\d+/[^/]+"
- ],
- "name": "Rdio",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.slideshare\\.net/.*/.*"
- ],
- "name": "SlideShare",
- "height": 110
- },
- {
- "patterns": [
- "http://imgur\\.com/([0-9a-zA-Z]+)$"
- ],
- "name": "Imgur",
- "height": 110
- },
- {
- "patterns": [
- "https?://instagr(?:\\.am|am\\.com)/p/.+"
- ],
- "name": "Instagram",
- "height": 110
- },
- {
- "patterns": [
- "http://www\\.twitlonger\\.com/show/[a-zA-Z0-9]+",
- "http://tl\\.gd/[^/]+"
- ],
- "name": "Twitlonger",
- "height": 110
- },
- {
- "patterns": [
- "https?://vine.co/v/[a-zA-Z0-9]+"
- ],
- "name": "Vine",
- "height": 490
- },
- {
- "patterns": [
- "http://www\\.urbandictionary\\.com/define\\.php\\?term=.+"
- ],
- "name": "Urban Dictionary",
- "height": 110
- },
- {
- "patterns": [
- "http://picplz\\.com/user/[^/]+/pic/[^/]+"
- ],
- "name": "Picplz",
- "height": 110
- },
- {
- "patterns": [
- "https?://(?:www\\.)?twitter\\.com/(?:#!/)?[^/]+/status(?:es)?/(\\d+)/photo/\\d+(?:/large|/)?$",
- "https?://pic\\.twitter\\.com/.+"
- ],
- "name": "Twitter",
- "height": 110
- }
-]
diff --git a/web/react/components/register_app_modal.jsx b/web/react/components/register_app_modal.jsx
deleted file mode 100644
index e6d13863b..000000000
--- a/web/react/components/register_app_modal.jsx
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-import ModalStore from '../stores/modal_store.jsx';
-
-const Modal = ReactBootstrap.Modal;
-
-import Constants from '../utils/constants.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-
-const holders = defineMessages({
- required: {
- id: 'register_app.required',
- defaultMessage: 'Required'
- },
- optional: {
- id: 'register_app.optional',
- defaultMessage: 'Optional'
- }
-});
-
-class RegisterAppModal extends React.Component {
- constructor() {
- super();
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.onHide = this.onHide.bind(this);
- this.save = this.save.bind(this);
- this.updateShow = this.updateShow.bind(this);
-
- this.state = {
- clientId: '',
- clientSecret: '',
- saved: false,
- show: false
- };
- }
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_REGISTER_APP_MODAL, this.updateShow);
- }
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_REGISTER_APP_MODAL, this.updateShow);
- }
- updateShow(show) {
- if (!show) {
- if (this.state.clientId !== '' && !this.state.saved) {
- return;
- }
-
- this.setState({
- clientId: '',
- clientSecret: '',
- saved: false,
- homepageError: null,
- callbackError: null,
- serverError: null,
- nameError: null
- });
- }
-
- this.setState({show});
- }
- handleSubmit(e) {
- e.preventDefault();
-
- var state = this.state;
- state.serverError = null;
-
- var app = {};
-
- var name = this.refs.name.value;
- if (!name || name.length === 0) {
- state.nameError = true;
- this.setState(state);
- return;
- }
- state.nameError = null;
- app.name = name;
-
- var homepage = this.refs.homepage.value;
- if (!homepage || homepage.length === 0) {
- state.homepageError = true;
- this.setState(state);
- return;
- }
- state.homepageError = null;
- app.homepage = homepage;
-
- var desc = this.refs.desc.value;
- app.description = desc;
-
- var rawCallbacks = this.refs.callback.value.trim();
- if (!rawCallbacks || rawCallbacks.length === 0) {
- state.callbackError = true;
- this.setState(state);
- return;
- }
- state.callbackError = null;
- app.callback_urls = rawCallbacks.split('\n');
-
- Client.registerOAuthApp(app,
- (data) => {
- state.clientId = data.id;
- state.clientSecret = data.client_secret;
- this.setState(state);
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- onHide(e) {
- if (!this.state.saved && this.state.clientId !== '') {
- e.preventDefault();
- return;
- }
-
- this.setState({clientId: '', clientSecret: '', saved: false});
- }
- save() {
- this.setState({saved: this.refs.save.checked});
- }
- render() {
- const {formatMessage} = this.props.intl;
- var nameError;
- if (this.state.nameError) {
- nameError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.nameError'
- defaultMessage='Application name must be filled in.'
- />
- </label>
- </div>
- );
- }
- var homepageError;
- if (this.state.homepageError) {
- homepageError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.homepageError'
- defaultMessage='Homepage must be filled in.'
- />
- </label>
- </div>
- );
- }
- var callbackError;
- if (this.state.callbackError) {
- callbackError = (
- <div className='form-group has-error'>
- <label className='control-label'>
- <FormattedMessage
- id='register_app.callbackError'
- defaultMessage='At least one callback URL must be filled in.'
- />
- </label>
- </div>
- );
- }
- var serverError;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var body = '';
- var footer = '';
- if (this.state.clientId === '') {
- body = (
- <div className='settings-modal'>
- <div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>
- <FormattedMessage
- id='register_app.title'
- defaultMessage='Register a New Application'
- />
- </h4>
- <div className='row'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.name'
- defaultMessage='Application Name'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='name'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- />
- {nameError}
- </div>
- </div>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.homepage'
- defaultMessage='Homepage URL'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='homepage'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- />
- {homepageError}
- </div>
- </div>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.description'
- defaultMessage='Description'
- />
- </label>
- <div className='col-sm-7'>
- <input
- ref='desc'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.optional)}
- />
- </div>
- </div>
- <div className='row padding-top padding-bottom x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.callback'
- defaultMessage='Callback URL'
- />
- </label>
- <div className='col-sm-7'>
- <textarea
- ref='callback'
- className='form-control'
- type='text'
- placeholder={formatMessage(holders.required)}
- rows='5'
- />
- {callbackError}
- </div>
- </div>
- {serverError}
- </div>
- </div>
- );
-
- footer = (
- <div>
- <button
- type='button'
- className='btn btn-default'
- onClick={() => this.updateShow(false)}
- >
- <FormattedMessage
- id='register_app.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='3'
- >
- <FormattedMessage
- id='register_app.register'
- defaultMessage='Register'
- />
- </button>
- </div>
- );
- } else {
- var btnClass = ' disabled';
- if (this.state.saved) {
- btnClass = '';
- }
-
- body = (
- <div className='form-horizontal user-settings'>
- <h4 className='padding-bottom x3'>
- <FormattedMessage
- id='register_app.credentialsTitle'
- defaultMessage='Your Application Credentials'
- />
- </h4>
- <br/>
- <div className='row'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.clientId'
- defaultMessage='Client ID'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- value={this.state.clientId}
- readOnly='true'
- />
- </div>
- </div>
- <br/>
- <div className='row padding-top x2'>
- <label className='col-sm-4 control-label'>
- <FormattedMessage
- id='register_app.clientSecret'
- defaultMessage='Client Secret'
- /></label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- value={this.state.clientSecret}
- readOnly='true'
- />
- </div>
- </div>
- <br/>
- <br/>
- <strong>
- <FormattedMessage
- id='register_app.credentialsDescription'
- defaultMessage="Save these somewhere SAFE and SECURE. Treat your Client ID as your app's username and your Client Secret as the app's password."
- />
- </strong>
- <br/>
- <br/>
- <div className='checkbox'>
- <label>
- <input
- ref='save'
- type='checkbox'
- checked={this.state.saved}
- onChange={this.save}
- />
- <FormattedMessage
- id='register_app.credentialsSave'
- defaultMessage='I have saved both my Client Id and Client Secret somewhere safe'
- />
- </label>
- </div>
- </div>
- );
-
- footer = (
- <a
- className={'btn btn-sm btn-primary pull-right' + btnClass}
- href='#'
- onClick={(e) => {
- e.preventDefault();
- this.updateShow(false);
- }}
- >
- <FormattedMessage
- id='register_app.close'
- defaultMessage='Close'
- />
- </a>
- );
- }
-
- return (
- <span>
- <Modal
- show={this.state.show}
- onHide={() => this.updateShow(false)}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='register_app.dev'
- defaultMessage='Developer Applications'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- {body}
- </Modal.Body>
- <Modal.Footer>
- {footer}
- </Modal.Footer>
- </form>
- </Modal>
- </span>
- );
- }
-}
-
-RegisterAppModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(RegisterAppModal);
diff --git a/web/react/components/removed_from_channel_modal.jsx b/web/react/components/removed_from_channel_modal.jsx
deleted file mode 100644
index 748baa32b..000000000
--- a/web/react/components/removed_from_channel_modal.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import * as utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class RemovedFromChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShow = this.handleShow.bind(this);
- this.handleClose = this.handleClose.bind(this);
-
- this.state = {
- channelName: '',
- remover: ''
- };
- }
-
- handleShow() {
- var newState = {};
- if (BrowserStore.getItem('channel-removed-state')) {
- newState = BrowserStore.getItem('channel-removed-state');
- BrowserStore.removeItem('channel-removed-state');
- }
-
- var townSquare = ChannelStore.getByName('town-square');
- setTimeout(() => utils.switchChannel(townSquare), 1);
-
- this.setState(newState);
- }
-
- handleClose() {
- this.setState({channelName: '', remover: ''});
- }
-
- componentDidMount() {
- $(ReactDOM.findDOMNode(this)).on('show.bs.modal', this.handleShow);
- $(ReactDOM.findDOMNode(this)).on('hidden.bs.modal', this.handleClose);
- }
-
- componentWillUnmount() {
- $(ReactDOM.findDOMNode(this)).off('show.bs.modal', this.handleShow);
- $(ReactDOM.findDOMNode(this)).off('hidden.bs.modal', this.handleClose);
- }
-
- render() {
- var currentUser = UserStore.getCurrentUser();
-
- var channelName = (
- <FormattedMessage
- id='removed_channel.channelName'
- defaultMessage='the channel'
- />
- );
- if (this.state.channelName) {
- channelName = this.state.channelName;
- }
-
- var remover = (
- <FormattedMessage
- id='removed_channel.someone'
- defaultMessage='Someone'
- />
- );
- if (this.state.remover) {
- remover = this.state.remover;
- }
-
- if (currentUser != null) {
- return (
- <div
- className='modal fade'
- ref='modal'
- id='removed_from_channel'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- ><span aria-hidden='true'>&times;</span></button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='removed_channel.from'
- defaultMessage='Removed from '
- />
- <span className='name'>{channelName}</span></h4>
- </div>
- <div className='modal-body'>
- <p>
- <FormattedMessage
- id='removed_channel.remover'
- defaultMessage='{remover} removed you from {channel}'
- values={{
- remover: (remover),
- channel: (channelName)
- }}
- />
- </p>
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-primary'
- data-dismiss='modal'
- >
- <FormattedMessage
- id='removed_channel.okay'
- defaultMessage='Okay'
- />
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-
- return <div/>;
- }
-}
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx
deleted file mode 100644
index e96ff0db2..000000000
--- a/web/react/components/rename_channel_modal.jsx
+++ /dev/null
@@ -1,321 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-const holders = defineMessages({
- required: {
- id: 'rename_channel.required',
- defaultMessage: 'This field is required'
- },
- maxLength: {
- id: 'rename_channel.maxLength',
- defaultMessage: 'This field must be less than 22 characters'
- },
- lowercase: {
- id: 'rename_channel.lowercase',
- defaultMessage: 'Must be lowercase alphanumeric characters'
- },
- handle: {
- id: 'rename_channel.handle',
- defaultMessage: 'Handle'
- },
- defaultError: {
- id: 'rename_channel.defaultError',
- defaultMessage: ' - Cannot be changed for the default channel'
- },
- displayNameHolder: {
- id: 'rename_channel.displayNameHolder',
- defaultMessage: 'Enter display name'
- },
- handleHolder: {
- id: 'rename_channel.handleHolder',
- defaultMessage: 'lowercase alphanumeric&#39;s only'
- }
-});
-
-export default class RenameChannelModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShow = this.handleShow.bind(this);
- this.handleHide = this.handleHide.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleCancel = this.handleCancel.bind(this);
-
- this.onNameChange = this.onNameChange.bind(this);
- this.onDisplayNameChange = this.onDisplayNameChange.bind(this);
- this.displayNameKeyUp = this.displayNameKeyUp.bind(this);
-
- this.state = {
- displayName: props.channel.display_name,
- channelName: props.channel.name,
- serverError: '',
- nameError: '',
- displayNameError: '',
- invalid: false
- };
- }
-
- componentWillReceiveProps(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.channel, this.props.channel)) {
- this.setState({
- displayName: nextProps.channel.display_name,
- channelName: nextProps.channel.name
- });
- }
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!nextProps.show && !this.props.show) {
- return false;
- }
-
- if (!Utils.areObjectsEqual(nextState, this.state)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextProps, this.props)) {
- return true;
- }
-
- return false;
- }
-
- componentDidUpdate(prevProps) {
- if (!prevProps.show && this.props.show) {
- this.handleShow();
- }
- }
-
- handleShow() {
- const textbox = ReactDOM.findDOMNode(this.refs.displayName);
- textbox.focus();
- Utils.placeCaretAtEnd(textbox);
- }
-
- handleHide(e) {
- if (e) {
- e.preventDefault();
- }
-
- this.props.onHide();
-
- this.setState({
- serverError: '',
- nameError: '',
- displayNameError: '',
- invalid: false
- });
- }
-
- handleSubmit(e) {
- e.preventDefault();
-
- const channel = Object.assign({}, this.props.channel);
- const oldName = channel.name;
- const oldDisplayName = channel.displayName;
- const state = {serverError: ''};
- const {formatMessage} = this.props.intl;
-
- channel.display_name = this.state.displayName.trim();
- if (!channel.display_name) {
- state.displayNameError = formatMessage(holders.required);
- state.invalid = true;
- } else if (channel.display_name.length > 22) {
- state.displayNameError = formatMessage(holders.maxLength);
- state.invalid = true;
- } else {
- state.displayNameError = '';
- }
-
- channel.name = this.state.channelName.trim();
- if (!channel.name) {
- state.nameError = formatMessage(holders.required);
- state.invalid = true;
- } else if (channel.name.length > 22) {
- state.nameError = formatMessage(holders.maxLength);
- state.invalid = true;
- } else {
- const cleanedName = Utils.cleanUpUrlable(channel.name);
- if (cleanedName === channel.name) {
- state.nameError = '';
- } else {
- state.nameError = formatMessage(holders.lowercase);
- state.invalid = true;
- }
- }
-
- this.setState(state);
-
- if (state.invalid || (oldName === channel.name && oldDisplayName === channel.display_name)) {
- return;
- }
-
- Client.updateChannel(
- channel,
- () => {
- AsyncClient.getChannel(channel.id);
- Utils.updateAddressBar(channel.name);
-
- this.handleHide();
- },
- (err) => {
- this.setState({
- serverError: err.message,
- invalid: true
- });
- }
- );
- }
-
- handleCancel(e) {
- this.setState({
- displayName: this.props.channel.display_name,
- channelName: this.props.channel.name
- });
-
- this.handleHide(e);
- }
-
- onNameChange() {
- this.setState({channelName: ReactDOM.findDOMNode(this.refs.channelName).value});
- }
-
- onDisplayNameChange() {
- this.setState({displayName: ReactDOM.findDOMNode(this.refs.displayName).value});
- }
-
- displayNameKeyUp() {
- if (this.state.channelName !== Constants.DEFAULT_CHANNEL) {
- const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim();
- const channelName = Utils.cleanUpUrlable(displayName);
- ReactDOM.findDOMNode(this.refs.channelName).value = channelName;
- this.setState({channelName: channelName});
- }
- }
-
- render() {
- let displayNameError = null;
- let displayNameClass = 'form-group';
- if (this.state.displayNameError) {
- displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
- displayNameClass += ' has-error';
- }
-
- let nameError = null;
- let nameClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameClass += ' has-error';
- }
-
- let serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- const {formatMessage} = this.props.intl;
-
- let handleInputLabel = formatMessage(holders.handle);
- let handleInputClass = 'form-control';
- let readOnlyHandleInput = false;
- if (this.state.channelName === Constants.DEFAULT_CHANNEL) {
- handleInputLabel += formatMessage(holders.defaultError);
- readOnlyHandleInput = true;
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.handleCancel}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='rename_channel.title'
- defaultMessage='Rename Channel'
- />
- </Modal.Title>
- </Modal.Header>
- <form role='form'>
- <Modal.Body>
- <div className={displayNameClass}>
- <label className='control-label'>
- <FormattedMessage
- id='rename_channel.displayName'
- defaultMessage='Display Name'
- />
- </label>
- <input
- onKeyUp={this.displayNameKeyUp}
- onChange={this.onDisplayNameChange}
- type='text'
- ref='displayName'
- id='display_name'
- className='form-control'
- placeholder={formatMessage(holders.displayNameHolder)}
- value={this.state.displayName}
- maxLength='64'
- />
- {displayNameError}
- </div>
- <div className={nameClass}>
- <label className='control-label'>{handleInputLabel}</label>
- <input
- onChange={this.onNameChange}
- type='text'
- className={handleInputClass}
- ref='channelName'
- placeholder={formatMessage(holders.handleHolder)}
- value={this.state.channelName}
- maxLength='64'
- readOnly={readOnlyHandleInput}
- />
- {nameError}
- </div>
- {serverError}
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.handleCancel}
- >
- <FormattedMessage
- id='rename_channel.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='rename_channel.save'
- defaultMessage='Save'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- );
- }
-}
-
-RenameChannelModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired,
- channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(RenameChannelModal);
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
deleted file mode 100644
index 9183b761f..000000000
--- a/web/react/components/rhs_comment.jsx
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostStore from '../stores/post_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserProfile from './user_profile.jsx';
-import UserStore from '../stores/user_store.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-import FileAttachmentList from './file_attachment_list.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-var ActionTypes = Constants.ActionTypes;
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import twemoji from 'twemoji';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl';
-
-const holders = defineMessages({
- comment: {
- id: 'rhs_comment.comment',
- defaultMessage: 'Comment'
- }
-});
-
-class RhsComment extends React.Component {
- constructor(props) {
- super(props);
-
- this.retryComment = this.retryComment.bind(this);
- this.parseEmojis = this.parseEmojis.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
-
- this.state = {};
- }
- retryComment(e) {
- e.preventDefault();
-
- var post = this.props.post;
- Client.createPost(post, post.channel_id,
- (data) => {
- AsyncClient.getPosts(post.channel_id);
-
- var channel = ChannelStore.get(post.channel_id);
- var member = ChannelStore.getMember(post.channel_id);
- member.msg_count = channel.total_msg_count;
- member.last_viewed_at = (new Date()).getTime();
- ChannelStore.setChannelMember(member);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST,
- post: data
- });
- },
- () => {
- post.state = Constants.POST_FAILED;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- );
-
- post.state = Constants.POST_LOADING;
- PostStore.updatePendingPost(post);
- this.forceUpdate();
- }
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
- }
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
- componentDidMount() {
- this.parseEmojis();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- return false;
- }
- componentDidUpdate() {
- this.parseEmojis();
- }
- createDropdown() {
- var post = this.props.post;
-
- if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || post.state === Constants.POST_DELETED) {
- return '';
- }
-
- var isOwner = UserStore.getCurrentId() === post.user_id;
- var isAdmin = Utils.isAdmin(UserStore.getCurrentUser().roles);
-
- var dropdownContents = [];
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_comment.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- role='presentation'
- key='edit-button'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={this.props.intl.formatMessage(holders.comment)}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_comment.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- role='presentation'
- key='delete-button'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, 0)}
- >
- <FormattedMessage
- id='rhs_comment.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- if (dropdownContents.length === 0) {
- return '';
- }
-
- return (
- <div className='dropdown'>
- <a
- href='#'
- className='post__dropdown dropdown-toggle'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
- render() {
- var post = this.props.post;
-
- var currentUserCss = '';
- if (UserStore.getCurrentId() === post.user_id) {
- currentUserCss = 'current--user';
- }
-
- var timestamp = UserStore.getCurrentUser().update_at;
-
- let loading;
- let postClass = '';
- let message = (
- <div
- ref='message_holder'
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}}
- />
- );
-
- if (post.state === Constants.POST_FAILED) {
- postClass += ' post-fail';
- loading = (
- <a
- className='theme post-retry pull-right'
- href='#'
- onClick={this.retryComment}
- >
- <FormattedMessage
- id='rhs_comment.retry'
- defaultMessage='Retry'
- />
- </a>
- );
- } else if (post.state === Constants.POST_LOADING) {
- postClass += ' post-waiting';
- loading = (
- <img
- className='post-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- } else if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- );
- }
-
- var dropdown = this.createDropdown();
-
- var fileAttachment;
- if (post.filenames && post.filenames.length > 0) {
- fileAttachment = (
- <FileAttachmentList
- filenames={post.filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- return (
- <div className={'post ' + currentUserCss}>
- <div className='post__content'>
- <div className='post__img'>
- <img
- src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
- height='36'
- width='36'
- />
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'>
- <strong><UserProfile user={this.props.user}/></strong>
- </li>
- <li className='col'>
- <time className='post__time'>
- <FormattedDate
- value={post.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li className='col col__reply'>
- {dropdown}
- </li>
- </ul>
- <div className='post__body'>
- <div className={postClass}>
- {loading}
- {message}
- </div>
- {fileAttachment}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsComment.defaultProps = {
- post: null
-};
-RhsComment.propTypes = {
- intl: intlShape.isRequired,
- post: React.PropTypes.object,
- user: React.PropTypes.object
-};
-
-export default injectIntl(RhsComment);
diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx
deleted file mode 100644
index 4c9f6f3f6..000000000
--- a/web/react/components/rhs_header_post.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-
-export default class RhsHeaderPost extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClose = this.handleClose.bind(this);
- this.handleBack = this.handleBack.bind(this);
-
- this.state = {};
- }
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- handleBack(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: this.props.fromSearch,
- do_search: true,
- is_mention_search: this.props.isMentionSearch
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- render() {
- let back;
- if (this.props.fromSearch) {
- back = (
- <a
- href='#'
- onClick={this.handleBack}
- className='sidebar--right__back'
- >
- <i className='fa fa-chevron-left'></i>
- </a>
- );
- }
-
- return (
- <div className='sidebar--right__header'>
- <span className='sidebar--right__title'>
- {back}
- <FormattedMessage
- id='rhs_header.details'
- defaultMessage='Message Details'
- />
- </span>
- <button
- type='button'
- className='sidebar--right__close'
- aria-label='Close'
- onClick={this.handleClose}
- >
- <i className='fa fa-sign-out'/>
- </button>
- </div>
- );
- }
-}
-
-RhsHeaderPost.defaultProps = {
- isMentionSearch: false,
- fromSearch: ''
-};
-RhsHeaderPost.propTypes = {
- isMentionSearch: React.PropTypes.bool,
- fromSearch: React.PropTypes.string
-};
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
deleted file mode 100644
index fc1cd0b41..000000000
--- a/web/react/components/rhs_root_post.jsx
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserProfile from './user_profile.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as Utils from '../utils/utils.jsx';
-import * as Emoji from '../utils/emoticons.jsx';
-import FileAttachmentList from './file_attachment_list.jsx';
-import twemoji from 'twemoji';
-import PostBodyAdditionalContent from './post_body_additional_content.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedDate} from 'mm-intl';
-
-export default class RhsRootPost extends React.Component {
- constructor(props) {
- super(props);
-
- this.parseEmojis = this.parseEmojis.bind(this);
- this.handlePermalink = this.handlePermalink.bind(this);
-
- this.state = {};
- }
- parseEmojis() {
- twemoji.parse(ReactDOM.findDOMNode(this), {
- className: 'emoji twemoji',
- base: '',
- folder: Emoji.getImagePathForEmoticon()
- });
- }
- handlePermalink(e) {
- e.preventDefault();
- GlobalActions.showGetPostLinkModal(this.props.post);
- }
- componentDidMount() {
- this.parseEmojis();
- }
- shouldComponentUpdate(nextProps) {
- if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
- return true;
- }
-
- return false;
- }
- componentDidUpdate() {
- this.parseEmojis();
- }
- render() {
- const post = this.props.post;
- const user = this.props.user;
- var isOwner = user.id === post.user_id;
- var isAdmin = Utils.isAdmin(user.roles);
- var timestamp = UserStore.getProfile(post.user_id).update_at;
- var channel = ChannelStore.get(post.channel_id);
-
- var type = 'Post';
- if (post.root_id.length > 0) {
- type = 'Comment';
- }
-
- var userCss = '';
- if (UserStore.getCurrentId() === post.user_id) {
- userCss = 'current--user';
- }
-
- var systemMessageClass = '';
- if (Utils.isSystemMessage(post)) {
- systemMessageClass = 'post--system';
- }
-
- var channelName;
- if (channel) {
- if (channel.type === 'D') {
- channelName = (
- <FormattedMessage
- id='rhs_root.direct'
- defaultMessage='Direct Message'
- />
- );
- } else {
- channelName = channel.display_name;
- }
- }
-
- var dropdownContents = [];
-
- if (!Utils.isMobile()) {
- dropdownContents.push(
- <li
- key='rhs-root-permalink'
- role='presentation'
- >
- <a
- href='#'
- onClick={this.handlePermalink}
- >
- <FormattedMessage
- id='rhs_root.permalink'
- defaultMessage='Permalink'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner) {
- dropdownContents.push(
- <li
- key='rhs-root-edit'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- <FormattedMessage
- id='rhs_root.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- if (isOwner || isAdmin) {
- dropdownContents.push(
- <li
- key='rhs-root-delete'
- role='presentation'
- >
- <a
- href='#'
- role='menuitem'
- onClick={() => GlobalActions.showDeletePostModal(post, this.props.commentCount)}
- >
- <FormattedMessage
- id='rhs_root.del'
- defaultMessage='Delete'
- />
- </a>
- </li>
- );
- }
-
- var rootOptions = '';
- if (dropdownContents.length > 0) {
- rootOptions = (
- <div className='dropdown'>
- <a
- href='#'
- className='post__dropdown dropdown-toggle'
- type='button'
- data-toggle='dropdown'
- aria-expanded='false'
- />
- <ul
- className='dropdown-menu'
- role='menu'
- >
- {dropdownContents}
- </ul>
- </div>
- );
- }
-
- var fileAttachment;
- if (post.filenames && post.filenames.length > 0) {
- fileAttachment = (
- <FileAttachmentList
- filenames={post.filenames}
- channelId={post.channel_id}
- userId={post.user_id}
- />
- );
- }
-
- let userProfile = <UserProfile user={user}/>;
- let botIndicator;
-
- if (post.props && post.props.from_webhook) {
- if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
- userProfile = (
- <UserProfile
- user={user}
- overwriteName={post.props.override_username}
- disablePopover={true}
- />
- );
- }
-
- botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
- } else if (Utils.isSystemMessage(post)) {
- userProfile = (
- <UserProfile
- user={{}}
- overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
- overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
- disablePopover={true}
- />
- );
- }
-
- 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 if (Utils.isSystemMessage(post)) {
- src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
- }
-
- const profilePic = (
- <img
- className='post-profile-img'
- src={src}
- height='36'
- width='36'
- />
- );
-
- return (
- <div className={'post post--root ' + userCss + ' ' + systemMessageClass}>
- <div className='post-right-channel__name'>{channelName}</div>
- <div className='post__content'>
- <div className='post__img'>
- {profilePic}
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'>{userProfile}</li>
- {botIndicator}
- <li className='col'>
- <time className='post__time'>
- <FormattedDate
- value={post.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li className='col col__reply'>
- <div>
- {rootOptions}
- </div>
- </li>
- </ul>
- <div className='post__body'>
- <div
- ref='message_holder'
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}}
- />
- <PostBodyAdditionalContent
- post={post}
- />
- {fileAttachment}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsRootPost.defaultProps = {
- commentCount: 0
-};
-RhsRootPost.propTypes = {
- post: React.PropTypes.object.isRequired,
- user: React.PropTypes.object.isRequired,
- commentCount: React.PropTypes.number
-};
diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx
deleted file mode 100644
index 292624846..000000000
--- a/web/react/components/rhs_thread.jsx
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-import SearchBox from './search_bar.jsx';
-import CreateComment from './create_comment.jsx';
-import RhsHeaderPost from './rhs_header_post.jsx';
-import RootPost from './rhs_root_post.jsx';
-import Comment from './rhs_comment.jsx';
-import Constants from '../utils/constants.jsx';
-import FileUploadOverlay from './file_upload_overlay.jsx';
-
-export default class RhsThread extends React.Component {
- constructor(props) {
- super(props);
-
- this.mounted = false;
-
- this.onPostChange = this.onPostChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- const state = {};
- state.windowWidth = Utils.windowWidth();
- state.windowHeight = Utils.windowHeight();
- state.selected = PostStore.getSelectedPost();
- state.posts = PostStore.getSelectedPostThread();
- state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
-
- this.state = state;
- }
- componentDidMount() {
- PostStore.addSelectedPostChangeListener(this.onPostChange);
- PostStore.addChangeListener(this.onPostChange);
- PreferenceStore.addChangeListener(this.forceUpdateInfo);
- UserStore.addChangeListener(this.onUserChange);
-
- this.resize();
- window.addEventListener('resize', this.handleResize);
-
- this.mounted = true;
- }
- componentDidUpdate() {
- if ($('.post-right__scroll')[0]) {
- $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
- }
- this.resize();
- }
- componentWillUnmount() {
- PostStore.removeSelectedPostChangeListener(this.onPostChange);
- PostStore.removeChangeListener(this.onPostChange);
- PreferenceStore.removeChangeListener(this.forceUpdateInfo);
- UserStore.removeChangeListener(this.onUserChange);
-
- window.removeEventListener('resize', this.handleResize);
-
- this.mounted = false;
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.posts, this.state.posts)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextState.selected, this.state.selected)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) {
- return true;
- }
-
- return false;
- }
- forceUpdateInfo() {
- if (this.state.postList) {
- for (var postId in this.state.postList.posts) {
- if (this.refs[postId]) {
- this.refs[postId].forceUpdate();
- }
- }
- }
- }
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
- onPostChange() {
- if (this.mounted) {
- const selected = PostStore.getSelectedPost();
- const posts = PostStore.getSelectedPostThread();
- this.setState({posts, selected});
- }
- }
- onUserChange() {
- const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
- this.setState({profiles});
- }
- resize() {
- $('.post-right__scroll').scrollTop(100000);
- if (this.state.windowWidth > 768) {
- $('.post-right__scroll').perfectScrollbar();
- $('.post-right__scroll').perfectScrollbar('update');
- }
- }
- render() {
- const posts = this.state.posts;
- const selected = this.state.selected;
- const profiles = this.state.profiles || {};
-
- if (posts == null || selected == null) {
- return (
- <div></div>
- );
- }
-
- var postsArray = [];
-
- for (const id in posts) {
- if (posts.hasOwnProperty(id)) {
- const cpost = posts[id];
- if (cpost.root_id === selected.id) {
- postsArray.push(cpost);
- }
- }
- }
-
- // sort failed posts to bottom, followed by pending, and then regular posts
- postsArray.sort(function postSort(a, b) {
- if ((a.state === Constants.POST_LOADING || a.state === Constants.POST_FAILED) && (b.state !== Constants.POST_LOADING && b.state !== Constants.POST_FAILED)) {
- return 1;
- }
- if ((a.state !== Constants.POST_LOADING && a.state !== Constants.POST_FAILED) && (b.state === Constants.POST_LOADING || b.state === Constants.POST_FAILED)) {
- return -1;
- }
-
- if (a.state === Constants.POST_LOADING && b.state === Constants.POST_FAILED) {
- return -1;
- }
- if (a.state === Constants.POST_FAILED && b.state === Constants.POST_LOADING) {
- return 1;
- }
-
- if (a.create_at < b.create_at) {
- return -1;
- }
- if (a.create_at > b.create_at) {
- return 1;
- }
- return 0;
- });
-
- var currentId = UserStore.getCurrentId();
- var searchForm;
- if (currentId != null) {
- searchForm = <SearchBox/>;
- }
-
- let profile;
- if (UserStore.getCurrentId() === selected.user_id) {
- profile = UserStore.getCurrentUser();
- } else {
- profile = profiles[selected.user_id];
- }
-
- return (
- <div className='post-right__container'>
- <FileUploadOverlay overlayType='right'/>
- <div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
- <div className='sidebar-right__body'>
- <RhsHeaderPost
- fromSearch={this.props.fromSearch}
- isMentionSearch={this.props.isMentionSearch}
- />
- <div className='post-right__scroll'>
- <RootPost
- ref={selected.id}
- post={selected}
- commentCount={postsArray.length}
- user={profile}
- />
- <div className='post-right-comments-container'>
- {postsArray.map(function mapPosts(comPost) {
- let p;
- if (UserStore.getCurrentId() === comPost.user_id) {
- p = UserStore.getCurrentUser();
- } else {
- p = profiles[comPost.user_id];
- }
- return (
- <Comment
- ref={comPost.id}
- key={comPost.id + 'commentKey'}
- post={comPost}
- user={p}
- />
- );
- })}
- </div>
- <div className='post-create__container'>
- <CreateComment
- channelId={selected.channel_id}
- rootId={selected.id}
- />
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-RhsThread.defaultProps = {
- fromSearch: '',
- isMentionSearch: false
-};
-
-RhsThread.propTypes = {
- fromSearch: React.PropTypes.string,
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/root.jsx b/web/react/components/root.jsx
deleted file mode 100644
index 70038203b..000000000
--- a/web/react/components/root.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import LocalizationStore from '../stores/localization_store.jsx';
-
-var IntlProvider = ReactIntl.IntlProvider;
-
-export default class Root extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- locale: 'en',
- translations: null
- };
-
- this.localizationChanged = this.localizationChanged.bind(this);
- }
- localizationChanged() {
- this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()});
- }
- componentWillMount() {
- // Setup localization listener
- LocalizationStore.addChangeListener(this.localizationChanged);
-
- // Browser store check version
- BrowserStore.checkVersion();
-
- window.onerror = (msg, url, line, column, stack) => {
- var l = {};
- l.level = 'ERROR';
- l.message = 'msg: ' + msg + ' row: ' + line + ' col: ' + column + ' stack: ' + stack + ' url: ' + url;
-
- $.ajax({
- url: '/api/v1/admin/log_client',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(l)
- });
-
- if (window.mm_config.EnableDeveloper === 'true') {
- window.ErrorStore.storeLastError({message: 'DEVELOPER MODE: A javascript error has occured. Please use the javascript console to capture and report the error (row: ' + line + ' col: ' + column + ').'});
- window.ErrorStore.emitChange();
- }
- };
-
- // Ya....
- /*eslint-disable */
- if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
- !function(){var analytics=global.window.analytics=global.window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.0.1";
- analytics.load(window.mm_config.SegmentDeveloperKey);
- analytics.page();
- }}();
- } else {
- global.window.analytics = {};
- global.window.analytics.page = function(){};
- global.window.analytics.track = function(){};
- }
- /*eslint-enable */
-
- // Get our localizaiton
- GlobalActions.newLocalizationSelected('en');
- }
- componentWillUnmount() {
- LocalizationStore.removeChangeListener(this.localizationChanged);
- }
- render() {
- if (this.state.translations == null) {
- return <div/>;
- }
-
- return (
- <IntlProvider
- locale={this.state.locale}
- messages={this.state.translations}
- key={this.state.locale}
- >
- {this.props.children}
- </IntlProvider>
- );
- }
-}
-Root.defaultProps = {
-};
-
-Root.propTypes = {
- children: React.PropTypes.object
-};
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
deleted file mode 100644
index 3afd0e840..000000000
--- a/web/react/components/search_bar.jsx
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import SuggestionBox from './suggestion/suggestion_box.jsx';
-import SearchChannelProvider from './suggestion/search_channel_provider.jsx';
-import SearchSuggestionList from './suggestion/search_suggestion_list.jsx';
-import SearchUserProvider from './suggestion/search_user_provider.jsx';
-import * as utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-var Popover = ReactBootstrap.Popover;
-
-const holders = defineMessages({
- search: {
- id: 'search_bar.search',
- defaultMessage: 'Search'
- }
-});
-
-class SearchBar extends React.Component {
- constructor() {
- super();
- this.mounted = false;
-
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
- this.handleUserFocus = this.handleUserFocus.bind(this);
- this.handleUserBlur = this.handleUserBlur.bind(this);
- this.performSearch = this.performSearch.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
-
- const state = this.getSearchTermStateFromStores();
- state.focused = false;
- this.state = state;
-
- this.suggestionProviders = [new SearchChannelProvider(), new SearchUserProvider()];
- }
- getSearchTermStateFromStores() {
- var term = SearchStore.getSearchTerm() || '';
- return {
- searchTerm: term
- };
- }
- componentDidMount() {
- SearchStore.addSearchTermChangeListener(this.onListenerChange);
- this.mounted = true;
- }
- componentWillUnmount() {
- SearchStore.removeSearchTermChangeListener(this.onListenerChange);
- this.mounted = false;
- }
- onListenerChange(doSearch, isMentionSearch) {
- if (this.mounted) {
- var newState = this.getSearchTermStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- if (doSearch) {
- this.performSearch(newState.searchTerm, isMentionSearch);
- }
- }
- }
- clearFocus() {
- $('.search-bar__container').removeClass('focused');
- }
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
- handleUserInput(text) {
- var term = text;
- SearchStore.storeSearchTerm(term);
- SearchStore.emitSearchTermChange(false);
- this.setState({searchTerm: term});
- }
- handleUserBlur() {
- this.setState({focused: false});
- }
- handleUserFocus() {
- $('.search-bar__container').addClass('focused');
-
- this.setState({focused: true});
- }
- performSearch(terms, isMentionSearch) {
- if (terms.length) {
- this.setState({isSearching: true});
-
- client.search(
- terms,
- (data) => {
- this.setState({isSearching: false});
- if (utils.isMobile()) {
- ReactDOM.findDOMNode(this.refs.search).value = '';
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: data,
- is_mention_search: isMentionSearch
- });
- },
- (err) => {
- this.setState({isSearching: false});
- AsyncClient.dispatchError(err, 'search');
- }
- );
- }
- }
- handleSubmit(e) {
- e.preventDefault();
- this.performSearch(this.state.searchTerm.trim());
- }
-
- render() {
- var isSearching = null;
- if (this.state.isSearching) {
- isSearching = <span className={'glyphicon glyphicon-refresh glyphicon-refresh-animate'}></span>;
- }
-
- let helpClass = 'search-help-popover';
- if (!this.state.searchTerm && this.state.focused) {
- helpClass += ' visible';
- }
-
- return (
- <div>
- <div
- className='sidebar__collapse'
- onClick={this.handleClose}
- >
- <span className='fa fa-angle-left'></span>
- </div>
- <span
- className='search__clear'
- onClick={this.clearFocus}
- >
- <FormattedMessage
- id='search_bar.cancel'
- defaultMessage='Cancel'
- />
- </span>
- <form
- role='form'
- className='search__form'
- onSubmit={this.handleSubmit}
- style={{overflow: 'visible'}}
- autoComplete='off'
- >
- <span className='glyphicon glyphicon-search sidebar__search-icon'/>
- <SuggestionBox
- ref='search'
- className='form-control search-bar'
- placeholder={this.props.intl.formatMessage(holders.search)}
- value={this.state.searchTerm}
- onFocus={this.handleUserFocus}
- onBlur={this.handleUserBlur}
- onUserInput={this.handleUserInput}
- listComponent={SearchSuggestionList}
- providers={this.suggestionProviders}
- />
- {isSearching}
- <Popover
- id='searchbar-help-popup'
- placement='bottom'
- className={helpClass}
- >
- <FormattedHTMLMessage
- id='search_bar.usage'
- defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>'
- />
- </Popover>
- </form>
- </div>
- );
- }
-}
-
-SearchBar.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(SearchBar);
-
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
deleted file mode 100644
index 8985063a0..000000000
--- a/web/react/components/search_results.jsx
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import SearchBox from './search_bar.jsx';
-import * as Utils from '../utils/utils.jsx';
-import SearchResultsHeader from './search_results_header.jsx';
-import SearchResultsItem from './search_results_item.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-function getStateFromStores() {
- const results = SearchStore.getSearchResults();
-
- const channels = new Map();
-
- if (results && results.order) {
- const channelIds = results.order.map((postId) => results.posts[postId].channel_id);
- for (const id of channelIds) {
- if (channels.has(id)) {
- continue;
- }
-
- channels.set(id, ChannelStore.get(id));
- }
- }
-
- return {
- results,
- channels
- };
-}
-
-export default class SearchResults extends React.Component {
- constructor(props) {
- super(props);
-
- this.mounted = false;
-
- this.onChange = this.onChange.bind(this);
- this.onUserChange = this.onUserChange.bind(this);
- this.resize = this.resize.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- const state = getStateFromStores();
- state.windowWidth = Utils.windowWidth();
- state.windowHeight = Utils.windowHeight();
- state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
- this.state = state;
- }
-
- componentDidMount() {
- this.mounted = true;
- SearchStore.addSearchChangeListener(this.onChange);
- ChannelStore.addChangeListener(this.onChange);
- UserStore.addChangeListener(this.onUserChange);
- this.resize();
- window.addEventListener('resize', this.handleResize);
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(this.props, nextProps)) {
- return true;
- }
-
- if (!Utils.areObjectsEqual(this.state, nextState)) {
- return true;
- }
-
- return false;
- }
-
- componentDidUpdate() {
- this.resize();
- }
-
- componentWillUnmount() {
- SearchStore.removeSearchChangeListener(this.onChange);
- ChannelStore.removeChangeListener(this.onChange);
- UserStore.removeChangeListener(this.onUserChange);
- this.mounted = false;
- window.removeEventListener('resize', this.handleResize);
- }
-
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
-
- onChange() {
- if (this.mounted) {
- this.setState(getStateFromStores());
- }
- }
-
- onUserChange() {
- this.setState({profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))});
- }
-
- resize() {
- $('#search-items-container').scrollTop(0);
- if (this.state.windowWidth > 768) {
- $('#search-items-container').perfectScrollbar();
- }
- }
-
- render() {
- var results = this.state.results;
- var currentId = UserStore.getCurrentId();
- var searchForm = null;
- if (currentId) {
- searchForm = <SearchBox/>;
- }
- var noResults = (!results || !results.order || !results.order.length);
- var searchTerm = SearchStore.getSearchTerm();
- const profiles = this.state.profiles || {};
-
- var ctls = null;
-
- if (!searchTerm && noResults) {
- ctls = (
- <div className='sidebar--right__subheader'>
- <FormattedHTMLMessage
- id='search_results.usage'
- defaultMessage='<ul><li>Use <b>"quotation marks"</b> to search for phrases</li><li>Use <b>from:</b> to find posts from specific users and <b>in:</b> to find posts in specific channels</li></ul>'
- />
- </div>
- );
- } else if (noResults) {
- ctls =
- (
- <div className='sidebar--right__subheader'>
- <h4>
- <FormattedMessage
- id='search_results.noResults'
- defaultMessage='NO RESULTS'
- />
- </h4>
- <FormattedHTMLMessage
- id='search_results.because'
- defaultMessage='<ul>
- <li>If you&#39;re searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li>
- <li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won&#39;t appear in search results</li>
- </ul>'
- />
- </div>
- );
- } else {
- ctls = results.order.map(function mymap(id) {
- const post = results.posts[id];
- let profile;
- if (UserStore.getCurrentId() === post.user_id) {
- profile = UserStore.getCurrentUser();
- } else {
- profile = profiles[post.user_id];
- }
- return (
- <SearchResultsItem
- key={post.id}
- channel={this.state.channels.get(post.channel_id)}
- post={post}
- user={profile}
- term={searchTerm}
- isMentionSearch={this.props.isMentionSearch}
- />
- );
- }, this);
- }
-
- return (
- <div className='sidebar--right__content'>
- <div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
- <div className='sidebar-right__body'>
- <SearchResultsHeader isMentionSearch={this.props.isMentionSearch}/>
- <div
- id='search-items-container'
- className='search-items-container'
- >
- {ctls}
- </div>
- </div>
- </div>
- );
- }
-}
-
-SearchResults.propTypes = {
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/search_results_header.jsx b/web/react/components/search_results_header.jsx
deleted file mode 100644
index 20fe342dc..000000000
--- a/web/react/components/search_results_header.jsx
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var ActionTypes = Constants.ActionTypes;
-
-export default class SearchResultsHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClose = this.handleClose.bind(this);
- }
-
- handleClose(e) {
- e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: null,
- do_search: false,
- is_mention_search: false
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
- }
-
- render() {
- var title = (
- <FormattedMessage
- id='search_header.results'
- defaultMessage='Search Results'
- />
- );
-
- if (this.props.isMentionSearch) {
- title = (
- <FormattedMessage
- id='search_header.title2'
- defaultMessage='Recent Mentions'
- />
- );
- }
-
- return (
- <div className='sidebar--right__header'>
- <span className='sidebar--right__title'>{title}</span>
- <button
- type='button'
- className='sidebar--right__close'
- aria-label='Close'
- title='Close'
- onClick={this.handleClose}
- >
- <i className='fa fa-sign-out'/>
- </button>
- </div>
- );
- }
-}
-
-SearchResultsHeader.propTypes = {
- isMentionSearch: React.PropTypes.bool
-};
diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx
deleted file mode 100644
index 9c3b0336b..000000000
--- a/web/react/components/search_results_item.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-// 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 * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as TextFormatting from '../utils/text_formatting.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedDate} from 'mm-intl';
-
-export default class SearchResultsItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClick = this.handleClick.bind(this);
- this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
- }
-
- handleClick(e) {
- e.preventDefault();
-
- GlobalActions.emitPostFocusEvent(this.props.post.id);
-
- if ($(window).width() < 768) {
- $('.sidebar--right').removeClass('move--left');
- $('.inner-wrap').removeClass('move--left');
- }
- }
-
- handleFocusRHSClick(e) {
- e.preventDefault();
- GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
- }
-
- render() {
- let channelName = null;
- const channel = this.props.channel;
- const timestamp = UserStore.getCurrentUser().update_at;
- const user = this.props.user || {};
-
- if (channel) {
- channelName = channel.display_name;
- if (channel.type === 'D') {
- channelName = (
- <FormattedMessage
- id='search_item.direct'
- defaultMessage='Direct Message'
- />
- );
- }
- }
-
- const formattingOptions = {
- searchTerm: this.props.term,
- mentionHighlight: this.props.isMentionSearch
- };
-
- 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}
- day='numeric'
- month='long'
- year='numeric'
- />
- </div>
- </div>
- <div
- className='post'
- >
- <div className='search-channel__name'>{channelName}</div>
- <div className='post__content'>
- <div className='post__img'>
- <img
- src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
- height='36'
- width='36'
- />
- </div>
- <div>
- <ul className='post__header'>
- <li className='col__name'><strong><UserProfile user={user}/></strong></li>
- <li className='col'>
- <time className='search-item-time'>
- <FormattedDate
- value={this.props.post.create_at}
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- </time>
- </li>
- <li>
- <a
- href='#'
- className='search-item__jump'
- onClick={this.handleClick}
- >
- <FormattedMessage
- id='search_item.jump'
- defaultMessage='Jump'
- />
- </a>
- </li>
- <li>
- <a
- href='#'
- className='comment-icon__container search-item__comment'
- onClick={this.handleFocusRHSClick}
- >
- <span
- className='comment-icon'
- dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
- />
- </a>
- </li>
- </ul>
- <div className='search-item-snippet'>
- <span
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SearchResultsItem.propTypes = {
- post: React.PropTypes.object,
- user: React.PropTypes.object,
- channel: React.PropTypes.object,
- isMentionSearch: React.PropTypes.bool,
- term: React.PropTypes.string
-};
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
deleted file mode 100644
index 70f011d26..000000000
--- a/web/react/components/setting_item_max.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingItemMax extends React.Component {
- render() {
- var clientError = null;
- if (this.props.client_error) {
- clientError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.client_error}</label></div>);
- }
-
- var serverError = null;
- if (this.props.server_error) {
- serverError = (<div className='form-group'><label className='col-sm-12 has-error'>{this.props.server_error}</label></div>);
- }
-
- var extraInfo = null;
- if (this.props.extraInfo) {
- extraInfo = (<div className='setting-list__hint'>{this.props.extraInfo}</div>);
- }
-
- var submit = '';
- if (this.props.submit) {
- submit = (
- <a
- className='btn btn-sm btn-primary'
- href='#'
- onClick={this.props.submit}
- >
- <FormattedMessage
- id='setting_item_max.save'
- defaultMessage='Save'
- />
- </a>
- );
- }
-
- var inputs = this.props.inputs;
- var widthClass;
- if (this.props.width === 'full') {
- widthClass = 'col-sm-12';
- } else if (this.props.width === 'medium') {
- widthClass = 'col-sm-10 col-sm-offset-2';
- } else {
- widthClass = 'col-sm-9 col-sm-offset-3';
- }
-
- let title;
- if (this.props.title) {
- title = <li className='col-sm-12 section-title'>{this.props.title}</li>;
- }
-
- return (
- <ul className='section-max form-horizontal'>
- {title}
- <li className={widthClass}>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- {inputs}
- {extraInfo}
- </li>
- <li className='setting-list-item'>
- <hr/>
- {serverError}
- {clientError}
- {submit}
- <a
- className='btn btn-sm theme'
- href='#'
- onClick={this.props.updateSection}
- >
- <FormattedMessage
- id='setting_item_max.cancel'
- defaultMessage='Cancel'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingItemMax.propTypes = {
- inputs: React.PropTypes.array,
- client_error: React.PropTypes.string,
- server_error: React.PropTypes.string,
- extraInfo: React.PropTypes.element,
- updateSection: React.PropTypes.func,
- submit: React.PropTypes.func,
- title: React.PropTypes.node,
- width: React.PropTypes.string
-};
diff --git a/web/react/components/setting_item_min.jsx b/web/react/components/setting_item_min.jsx
deleted file mode 100644
index cb2ee0d8f..000000000
--- a/web/react/components/setting_item_min.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingItemMin extends React.Component {
- render() {
- let editButton = null;
- if (!this.props.disableOpen) {
- editButton = (
- <li className='col-sm-3 section-edit'>
- <a
- className='theme'
- href='#'
- onClick={this.props.updateSection}
- >
- <i className='fa fa-pencil'/>
- <FormattedMessage
- id='setting_item_min.edit'
- defaultMessage='Edit'
- />
- </a>
- </li>
- );
- }
-
- return (
- <ul
- className='section-min'
- onClick={this.props.updateSection}
- >
- <li className='col-sm-9 section-title'>{this.props.title}</li>
- {editButton}
- <li className='col-sm-9 section-describe'>{this.props.describe}</li>
- </ul>
- );
- }
-}
-
-SettingItemMin.propTypes = {
- title: React.PropTypes.node,
- disableOpen: React.PropTypes.bool,
- updateSection: React.PropTypes.func,
- describe: React.PropTypes.node
-};
diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx
deleted file mode 100644
index 70e0e6755..000000000
--- a/web/react/components/setting_picture.jsx
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingPicture extends React.Component {
- constructor(props) {
- super(props);
-
- this.setPicture = this.setPicture.bind(this);
- }
-
- setPicture(file) {
- if (file) {
- var reader = new FileReader();
-
- var img = ReactDOM.findDOMNode(this.refs.image);
- reader.onload = function load(e) {
- $(img).attr('src', e.target.result);
- };
-
- reader.readAsDataURL(file);
- }
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.picture) {
- this.setPicture(nextProps.picture);
- }
- }
-
- render() {
- var clientError = null;
- if (this.props.client_error) {
- clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>;
- }
- var serverError = null;
- if (this.props.server_error) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.props.server_error}</label></div>;
- }
-
- var img = null;
- if (this.props.picture) {
- img = (
- <img
- ref='image'
- className='profile-img rounded'
- src=''
- />
- );
- } else {
- img = (
- <img
- ref='image'
- className='profile-img rounded'
- src={this.props.src}
- />
- );
- }
-
- var confirmButton;
- if (this.props.loadingPicture) {
- confirmButton = (
- <img
- className='spinner'
- src='/static/images/load.gif'
- />
- );
- } else {
- var confirmButtonClass = 'btn btn-sm';
- if (this.props.submitActive) {
- confirmButtonClass += ' btn-primary';
- } else {
- confirmButtonClass += ' btn-inactive disabled';
- }
-
- confirmButton = (
- <a
- className={confirmButtonClass}
- onClick={this.props.submit}
- >
- <FormattedMessage
- id='setting_picture.save'
- defaultMessage='Save'
- />
- </a>
- );
- }
- var helpText = (
- <FormattedMessage
- id='setting_picture.help'
- defaultMessage='Upload a profile picture in either JPG or PNG format, at least {width}px in width and {height}px height.'
- values={{
- width: global.window.mm_config.ProfileWidth,
- height: global.window.mm_config.ProfileHeight
- }}
- />
- );
-
- var self = this;
- return (
- <ul className='section-max'>
- <li className='col-xs-12 section-title'>{this.props.title}</li>
- <li className='col-xs-offset-3 col-xs-8'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- {img}
- </li>
- <li className='setting-list-item'>
- {helpText}
- </li>
- <li className='setting-list-item'>
- {serverError}
- {clientError}
- <span className='btn btn-sm btn-primary btn-file sel-btn'>
- <FormattedMessage
- id='setting_picture.select'
- defaultMessage='Select'
- />
- <input
- ref='input'
- accept='.jpg,.png,.bmp'
- type='file'
- onChange={this.props.pictureChange}
- />
- </span>
- {confirmButton}
- <a
- className='btn btn-sm theme'
- href='#'
- onClick={self.props.updateSection}
- >
- <FormattedMessage
- id='setting_picture.cancel'
- defaultMessage='Cancel'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingPicture.propTypes = {
- client_error: React.PropTypes.string,
- server_error: React.PropTypes.string,
- src: React.PropTypes.string,
- picture: React.PropTypes.object,
- loadingPicture: React.PropTypes.bool,
- submitActive: React.PropTypes.bool,
- submit: React.PropTypes.func,
- title: React.PropTypes.string,
- pictureChange: React.PropTypes.func
-};
diff --git a/web/react/components/setting_upload.jsx b/web/react/components/setting_upload.jsx
deleted file mode 100644
index 5d5cdfdf7..000000000
--- a/web/react/components/setting_upload.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SettingsUpload extends React.Component {
- constructor(props) {
- super(props);
-
- this.doFileSelect = this.doFileSelect.bind(this);
- this.doSubmit = this.doSubmit.bind(this);
-
- this.state = {
- clientError: this.props.clientError,
- serverError: this.props.serverError,
- filename: ''
- };
- }
-
- componentWillReceiveProps() {
- this.setState({
- clientError: this.props.clientError,
- serverError: this.props.serverError
- });
- }
-
- doFileSelect(e) {
- e.preventDefault();
- var filename = $(e.target).val();
- if (filename.substring(3, 11) === 'fakepath') {
- filename = filename.substring(12);
- }
- this.setState({
- clientError: '',
- serverError: '',
- filename
- });
- }
-
- doSubmit(e) {
- e.preventDefault();
- var inputnode = ReactDOM.findDOMNode(this.refs.uploadinput);
- if (inputnode.files && inputnode.files[0]) {
- this.props.submit(inputnode.files[0]);
- } else {
- this.setState({clientError: true});
- }
- }
-
- render() {
- let clientError = null;
- if (this.state.clientError) {
- clientError = (
- <div className='file-status'>
- <FormattedMessage
- id='setting_upload.noFile'
- defaultMessage='No file selected.'
- />
- </div>
- );
- }
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='file-status'>{this.state.serverError}</div>
- );
- }
- let fileNameText = null;
- let submitButtonClass = 'btn btn-sm btn-primary disabled';
- if (this.state.filename) {
- fileNameText = (
- <div className='file-status file-name'>{this.state.filename}</div>
- );
- submitButtonClass = 'btn btn-sm btn-primary';
- }
-
- return (
- <ul className='section-max'>
- <li className='col-sm-12 section-title'>{this.props.title}</li>
- <li className='col-sm-offset-3 col-sm-9'>{this.props.helpText}</li>
- <li className='col-sm-offset-3 col-sm-9'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- <span className='btn btn-sm btn-primary btn-file sel-btn'>
- <FormattedMessage
- id='setting_upload.select'
- defaultMessage='Select file'
- />
- <input
- ref='uploadinput'
- accept={this.props.fileTypesAccepted}
- type='file'
- onChange={this.doFileSelect}
- />
- </span>
- <a
- className={submitButtonClass}
- onClick={this.doSubmit}
- >
- <FormattedMessage
- id='setting_upload.import'
- defaultMessage='Import'
- />
- </a>
- {fileNameText}
- {serverError}
- {clientError}
- </li>
- </ul>
- </li>
- </ul>
- );
- }
-}
-
-SettingsUpload.propTypes = {
- title: React.PropTypes.string.isRequired,
- submit: React.PropTypes.func.isRequired,
- fileTypesAccepted: React.PropTypes.string.isRequired,
- clientError: React.PropTypes.string,
- serverError: React.PropTypes.string,
- helpText: React.PropTypes.object
-}; \ No newline at end of file
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
deleted file mode 100644
index da8001f6f..000000000
--- a/web/react/components/settings_sidebar.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-export default class SettingsSidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleClick = this.handleClick.bind(this);
- }
- handleClick(tab, e) {
- e.preventDefault();
- this.props.updateTab(tab.name);
- $(e.target).closest('.settings-modal').addClass('display--content');
- }
- componentDidMount() {
- if (Utils.isBrowserFirefox()) {
- $('.settings-modal .settings-table .nav').addClass('position--top');
- }
- }
- render() {
- let tabList = this.props.tabs.map(function makeTab(tab) {
- let key = `${tab.name}_li`;
- let className = '';
- if (this.props.activeTab === tab.name) {
- className = 'active';
- }
-
- return (
- <li
- key={key}
- className={className}
- >
- <a
- href='#'
- onClick={this.handleClick.bind(null, tab)}
- >
- <i className={tab.icon}/>
- {tab.uiName}
- </a>
- </li>
- );
- }.bind(this));
-
- return (
- <div>
- <ul className='nav nav-pills nav-stacked'>
- {tabList}
- </ul>
- </div>
- );
- }
-}
-
-SettingsSidebar.propTypes = {
- tabs: React.PropTypes.arrayOf(React.PropTypes.shape({
- name: React.PropTypes.string.isRequired,
- uiName: React.PropTypes.string.isRequired,
- icon: React.PropTypes.string.isRequired
- })).isRequired,
- activeTab: React.PropTypes.string,
- updateTab: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/should_verify_email.jsx b/web/react/components/should_verify_email.jsx
deleted file mode 100644
index c473fe366..000000000
--- a/web/react/components/should_verify_email.jsx
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import * as Client from '../utils/client.jsx';
-
-export default class ShouldVerifyEmail extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleResend = this.handleResend.bind(this);
-
- this.state = {
- resendStatus: 'none'
- };
- }
- handleResend() {
- const teamName = this.props.location.query.teamname;
- const email = this.props.location.query.email;
-
- this.setState({resendStatus: 'sending'});
-
- Client.resendVerification(() => {
- this.setState({resendStatus: 'success'});
- },
- () => {
- this.setState({resendStatus: 'failure'});
- },
- teamName,
- email);
- }
- render() {
- let resendConfirm = '';
- if (this.state.resendStatus === 'success') {
- resendConfirm = (
- <div>
- <br/>
- <p className='alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='email_verify.sent'
- defaultMessage=' Verification email sent.'
- />
- </p>
- </div>
- );
- }
-
- if (this.state.resendStatus === 'failure') {
- resendConfirm = (
- <div>
- <br/>
- <p className='alert alert-danger'>
- <i className='fa fa-times'/>
- <FormattedMessage id='email_verify.failed'/>
- </p>
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <h3>
- <FormattedMessage
- id='email_verify.almost'
- defaultMessage='{siteName}: You are almost done'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h3>
- <div>
- <p>
- <FormattedMessage
- id='email_verify.notVerifiedBody'
- defaultMessage='Please verify your email address. Check your inbox for an email.'
- />
- </p>
- <button
- onClick={this.handleResend}
- className='btn btn-primary'
- >
- <FormattedMessage
- id='email_verify.resend'
- defaultMessage='Resend Email'
- />
- </button>
- {resendConfirm}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-ShouldVerifyEmail.defaultProps = {
-};
-ShouldVerifyEmail.propTypes = {
- location: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
deleted file mode 100644
index 5c682d64b..000000000
--- a/web/react/components/sidebar.jsx
+++ /dev/null
@@ -1,714 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NewChannelFlow from './new_channel_flow.jsx';
-import MoreDirectChannels from './more_direct_channels.jsx';
-import SidebarHeader from './sidebar_header.jsx';
-import UnreadChannelIndicator from './unread_channel_indicator.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class Sidebar extends React.Component {
- constructor(props) {
- super(props);
-
- this.badgesActive = false;
- this.firstUnreadChannel = null;
- this.lastUnreadChannel = null;
-
- this.getStateFromStores = this.getStateFromStores.bind(this);
-
- this.onChange = this.onChange.bind(this);
- this.onScroll = this.onScroll.bind(this);
- this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
- this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this);
- this.handleResize = this.handleResize.bind(this);
-
- this.showMoreChannelsModal = this.showMoreChannelsModal.bind(this);
- this.showNewChannelModal = this.showNewChannelModal.bind(this);
- this.hideNewChannelModal = this.hideNewChannelModal.bind(this);
- this.showMoreDirectChannelsModal = this.showMoreDirectChannelsModal.bind(this);
- this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this);
-
- this.createChannelElement = this.createChannelElement.bind(this);
- this.updateTitle = this.updateTitle.bind(this);
-
- this.isLeaving = new Map();
-
- const state = this.getStateFromStores();
- state.newChannelModalType = '';
- state.showDirectChannelsModal = false;
- state.loadingDMChannel = -1;
- state.windowWidth = Utils.windowWidth();
- this.state = state;
- }
- getTotalUnreadCount() {
- let msgs = 0;
- let mentions = 0;
- const unreadCounts = this.state.unreadCounts;
-
- Object.keys(unreadCounts).forEach((chId) => {
- msgs += unreadCounts[chId].msgs;
- mentions += unreadCounts[chId].mentions;
- });
-
- return {msgs, mentions};
- }
- getStateFromStores() {
- const members = ChannelStore.getAllMembers();
- const currentChannelId = ChannelStore.getCurrentId();
- const currentUserId = UserStore.getCurrentId();
-
- const channels = Object.assign([], ChannelStore.getAll());
- channels.sort((a, b) => a.display_name.localeCompare(b.display_name));
-
- const publicChannels = channels.filter((channel) => channel.type === Constants.OPEN_CHANNEL);
- const privateChannels = channels.filter((channel) => channel.type === Constants.PRIVATE_CHANNEL);
-
- const preferences = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
-
- const directChannels = [];
- for (const preference of preferences) {
- if (preference.value !== 'true') {
- continue;
- }
-
- const teammateId = preference.name;
-
- let directChannel = channels.find(Utils.isDirectChannelForUser.bind(null, teammateId));
-
- // a direct channel doesn't exist yet so create a fake one
- if (directChannel == null) {
- directChannel = {
- name: Utils.getDirectChannelName(currentUserId, teammateId),
- last_post_at: 0,
- total_msg_count: 0,
- type: Constants.DM_CHANNEL,
- fake: true
- };
- } else {
- directChannel = JSON.parse(JSON.stringify(directChannel));
- }
-
- directChannel.display_name = Utils.displayUsername(teammateId);
- directChannel.teammate_id = teammateId;
- directChannel.status = UserStore.getStatus(teammateId);
-
- directChannels.push(directChannel);
- }
-
- directChannels.sort(this.sortChannelsByDisplayName);
-
- const hiddenDirectChannelCount = UserStore.getActiveOnlyProfileList(true).length - directChannels.length;
-
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
-
- return {
- activeId: currentChannelId,
- members,
- publicChannels,
- privateChannels,
- directChannels,
- hiddenDirectChannelCount,
- unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())),
- showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER,
- currentTeam: TeamStore.getCurrent(),
- currentUser: UserStore.getCurrentUser()
- };
- }
-
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- UserStore.addChangeListener(this.onChange);
- UserStore.addStatusesChangeListener(this.onChange);
- TeamStore.addChangeListener(this.onChange);
- PreferenceStore.addChangeListener(this.onChange);
-
- this.updateTitle();
- this.updateUnreadIndicators();
-
- window.addEventListener('resize', this.handleResize);
-
- if ($(window).width() > 768) {
- $('.nav-pills__container').perfectScrollbar();
- }
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState, this.state)) {
- return true;
- }
- return false;
- }
- componentDidUpdate() {
- this.updateTitle();
- this.updateUnreadIndicators();
- }
- componentWillUnmount() {
- window.removeEventListener('resize', this.handleResize);
-
- ChannelStore.removeChangeListener(this.onChange);
- UserStore.removeChangeListener(this.onChange);
- UserStore.removeStatusesChangeListener(this.onChange);
- TeamStore.removeChangeListener(this.onChange);
- PreferenceStore.removeChangeListener(this.onChange);
- }
- handleResize() {
- this.setState({
- windowWidth: Utils.windowWidth(),
- windowHeight: Utils.windowHeight()
- });
- }
- onChange() {
- this.setState(this.getStateFromStores());
- }
- updateTitle() {
- const channel = ChannelStore.getCurrent();
- if (channel && this.state.currentTeam) {
- let currentSiteName = '';
- if (global.window.mm_config.SiteName != null) {
- currentSiteName = global.window.mm_config.SiteName;
- }
-
- let currentChannelName = channel.display_name;
- if (channel.type === 'D') {
- const teammate = Utils.getDirectTeammate(channel.id);
- if (teammate != null) {
- currentChannelName = teammate.username;
- }
- }
-
- const unread = this.getTotalUnreadCount();
- const mentionTitle = unread.mentions > 0 ? '(' + unread.mentions + ') ' : '';
- const unreadTitle = unread.msgs > 0 ? '* ' : '';
- document.title = mentionTitle + unreadTitle + currentChannelName + ' - ' + this.state.currentTeam.display_name + ' ' + currentSiteName;
- }
- }
- onScroll() {
- this.updateUnreadIndicators();
- }
- updateUnreadIndicators() {
- const container = $(ReactDOM.findDOMNode(this.refs.container));
-
- var showTopUnread = false;
- var showBottomUnread = false;
-
- if (this.firstUnreadChannel) {
- var firstUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.firstUnreadChannel]));
-
- if (firstUnreadElement.position().top + firstUnreadElement.height() < 0) {
- showTopUnread = true;
- }
- }
-
- if (this.lastUnreadChannel) {
- var lastUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.lastUnreadChannel]));
-
- if (lastUnreadElement.position().top > container.height()) {
- showBottomUnread = true;
- }
- }
-
- this.setState({
- showTopUnread,
- showBottomUnread
- });
- }
-
- handleLeaveDirectChannel(channel) {
- if (!this.isLeaving.get(channel.id)) {
- this.isLeaving.set(channel.id, true);
-
- const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'false');
-
- // bypass AsyncClient since we've already saved the updated preferences
- Client.savePreferences(
- [preference],
- () => {
- this.isLeaving.set(channel.id, false);
- },
- () => {
- this.isLeaving.set(channel.id, false);
- }
- );
-
- this.setState(this.getStateFromStores());
- }
-
- if (channel.id === this.state.activeId) {
- Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
- }
- }
-
- sortChannelsByDisplayName(a, b) {
- return a.display_name.localeCompare(b.display_name);
- }
-
- showMoreChannelsModal() {
- // manually show the modal because using data-toggle messes with keyboard focus when the modal is dismissed
- $('#more_channels').modal({'data-channeltype': 'O'}).modal('show');
- }
-
- showNewChannelModal(type) {
- this.setState({newChannelModalType: type});
- }
- hideNewChannelModal() {
- this.setState({newChannelModalType: ''});
- }
-
- showMoreDirectChannelsModal() {
- this.setState({showDirectChannelsModal: true});
- }
- hideMoreDirectChannelsModal() {
- this.setState({showDirectChannelsModal: false});
- }
-
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen1'
- defaultMessage='<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>'
- />
- </div>
- );
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen2'
- defaultMessage='<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>'
- />
- </div>
- );
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar.tutorialScreen3'
- defaultMessage='<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>'
- />
- </div>
- );
-
- return (
- <TutorialTip
- placement='right'
- screens={screens}
- overlayClass='tip-overlay--sidebar'
- />
- );
- }
-
- createChannelElement(channel, index, arr, handleClose) {
- const members = this.state.members;
- const activeId = this.state.activeId;
- const channelMember = members[channel.id];
- const unreadCount = this.state.unreadCounts[channel.id] || {msgs: 0, mentions: 0};
- let msgCount;
-
- let linkClass = '';
- if (channel.id === activeId) {
- linkClass = 'active';
- }
-
- let rowClass = 'sidebar-channel';
-
- var unread = false;
- if (channelMember) {
- msgCount = unreadCount.msgs + unreadCount.mentions;
- unread = msgCount > 0 || channelMember.mention_count > 0;
- }
-
- if (unread) {
- rowClass += ' unread-title';
-
- if (channel.id !== activeId) {
- if (!this.firstUnreadChannel) {
- this.firstUnreadChannel = channel.name;
- }
- this.lastUnreadChannel = channel.name;
- }
- }
-
- var badge = null;
- if (channelMember) {
- if (unreadCount.mentions) {
- badge = <span className='badge pull-right small'>{unreadCount.mentions}</span>;
- this.badgesActive = true;
- }
- } else if (this.state.loadingDMChannel === index && channel.type === 'D') {
- badge = (
- <img
- className='channel-loading-gif pull-right'
- src='/static/images/load.gif'
- />
- );
- }
-
- if (msgCount > 0) {
- rowClass += ' has-badge';
- }
-
- // set up status icon for direct message channels
- var status = null;
- if (channel.type === 'D') {
- var statusIcon = '';
- if (channel.status === 'online') {
- statusIcon = Constants.ONLINE_ICON_SVG;
- } else if (channel.status === 'away') {
- statusIcon = Constants.AWAY_ICON_SVG;
- } else {
- statusIcon = Constants.OFFLINE_ICON_SVG;
- }
- status = (
- <span
- className='status'
- dangerouslySetInnerHTML={{__html: statusIcon}}
- />
- );
- }
-
- var icon = null;
- if (channel.type === 'O') {
- icon = <div className='status'><i className='fa fa-globe'></i></div>;
- } else if (channel.type === 'P') {
- icon = <div className='status'><i className='fa fa-lock'></i></div>;
- }
-
- // set up click handler to switch channels (or create a new channel for non-existant ones)
- var handleClick = null;
- var href = '#';
-
- if (!channel.fake) {
- handleClick = function clickHandler(e) {
- if (e.target.attributes.getNamedItem('data-close')) {
- handleClose(channel);
- } else {
- Utils.switchChannel(channel);
- }
-
- e.preventDefault();
- };
- } else if (channel.fake) {
- // It's a direct message channel that doesn't exist yet so let's create it now
- var otherUserId = Utils.getUserIdFromChannelName(channel);
-
- if (this.state.loadingDMChannel === -1) {
- handleClick = function clickHandler(e) {
- e.preventDefault();
-
- if (e.target.attributes.getNamedItem('data-close')) {
- handleClose(channel);
- } else {
- this.setState({loadingDMChannel: index});
-
- Client.createDirectChannel(channel, otherUserId,
- (data) => {
- this.setState({loadingDMChannel: -1});
- AsyncClient.getChannel(data.id);
- Utils.switchChannel(data);
- },
- () => {
- this.setState({loadingDMChannel: -1});
- window.location.href = '/' + this.state.currentTeam.name;
- }
- );
- }
- }.bind(this);
- }
- }
-
- let closeButton = null;
- const removeTooltip = (
- <Tooltip id='remove-dm-tooltip'>
- <FormattedMessage
- id='sidebar.removeList'
- defaultMessage='Remove from list'
- />
- </Tooltip>
- );
- if (handleClose && !badge) {
- closeButton = (
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={removeTooltip}
- >
- <span
- className='btn-close'
- data-close='true'
- >
- {'Ɨ'}
- </span>
- </OverlayTrigger>
- );
-
- rowClass += ' has-close';
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip && channel.name === Constants.DEFAULT_CHANNEL) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <li
- key={channel.name}
- ref={channel.name}
- className={linkClass}
- >
- <a
- className={rowClass}
- href={href}
- onClick={handleClick}
- >
- {icon}
- {status}
- {channel.display_name}
- {badge}
- {closeButton}
- </a>
- {tutorialTip}
- </li>
- );
- }
- render() {
- // Check if we have all info needed to render
- if (this.state.currentTeam == null || this.state.currentUser == null) {
- return (<div/>);
- }
-
- this.badgesActive = false;
-
- // keep track of the first and last unread channels so we can use them to set the unread indicators
- this.firstUnreadChannel = null;
- this.lastUnreadChannel = null;
-
- // create elements for all 3 types of channels
- const publicChannelItems = this.state.publicChannels.map(this.createChannelElement);
-
- const privateChannelItems = this.state.privateChannels.map(this.createChannelElement);
-
- const directMessageItems = this.state.directChannels.map((channel, index, arr) => {
- return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
- });
-
- // update the favicon to show if there are any notifications
- var link = document.createElement('link');
- link.type = 'image/x-icon';
- link.rel = 'shortcut icon';
- link.id = 'favicon';
- if (this.badgesActive) {
- link.href = '/static/images/favicon/redfavicon-16x16.png';
- } else {
- link.href = '/static/images/favicon/favicon-16x16.png';
- }
- var head = document.getElementsByTagName('head')[0];
- var oldLink = document.getElementById('favicon');
- if (oldLink) {
- head.removeChild(oldLink);
- }
- head.appendChild(link);
-
- var directMessageMore = null;
- if (this.state.hiddenDirectChannelCount > 0) {
- directMessageMore = (
- <li key='more'>
- <a
- href='#'
- onClick={this.showMoreDirectChannelsModal}
- >
- <FormattedMessage
- id='sidebar.more'
- defaultMessage='More ({count})'
- values={{
- count: this.state.hiddenDirectChannelCount
- }}
- />
- </a>
- </li>
- );
- }
-
- let showChannelModal = false;
- if (this.state.newChannelModalType !== '') {
- showChannelModal = true;
- }
-
- const createChannelTootlip = (
- <Tooltip id='new-channel-tooltip' >
- <FormattedMessage
- id='sidebar.createChannel'
- defaultMessage='Create new channel'
- />
- </Tooltip>
- );
- const createGroupTootlip = (
- <Tooltip id='new-group-tooltip'>
- <FormattedMessage
- id='sidebar.createGroup'
- defaultMessage='Create new group'
- />
- </Tooltip>
- );
-
- const above = (
- <FormattedMessage
- id='sidebar.unreadAbove'
- defaultMessage='Unread post(s) above'
- />
- );
-
- const below = (
- <FormattedMessage
- id='sidebar.unreadBelow'
- defaultMessage='Unread post(s) below'
- />
- );
-
- return (
- <div
- className='sidebar--left'
- id='sidebar-left'
- >
- <NewChannelFlow
- show={showChannelModal}
- channelType={this.state.newChannelModalType}
- onModalDismissed={this.hideNewChannelModal}
- />
- <MoreDirectChannels
- show={this.state.showDirectChannelsModal}
- onModalDismissed={this.hideMoreDirectChannelsModal}
- />
-
- <SidebarHeader
- teamDisplayName={this.state.currentTeam.display_name}
- teamName={this.state.currentTeam.name}
- teamType={this.state.currentTeam.type}
- currentUser={this.state.currentUser}
- />
-
- <UnreadChannelIndicator
- show={this.state.showTopUnread}
- extraClass='nav-pills__unread-indicator-top'
- text={above}
- />
- <UnreadChannelIndicator
- show={this.state.showBottomUnread}
- extraClass='nav-pills__unread-indicator-bottom'
- text={below}
- />
-
- <div
- ref='container'
- className='nav-pills__container'
- onScroll={this.onScroll}
- >
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.channels'
- defaultMessage='Channels'
- />
- <OverlayTrigger
- delayShow={500}
- placement='top'
- overlay={createChannelTootlip}
- >
- <a
- className='add-channel-btn'
- href='#'
- onClick={this.showNewChannelModal.bind(this, 'O')}
- >
- {'+'}
- </a>
- </OverlayTrigger>
- </h4>
- </li>
- {publicChannelItems}
- <li>
- <a
- href='#'
- className='nav-more'
- onClick={this.showMoreChannelsModal}
- >
- <FormattedMessage
- id='sidebar.moreElips'
- defaultMessage='More...'
- />
- </a>
- </li>
- </ul>
-
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.pg'
- defaultMessage='Private Groups'
- />
- <OverlayTrigger
- delayShow={500}
- placement='top'
- overlay={createGroupTootlip}
- >
- <a
- className='add-channel-btn'
- href='#'
- onClick={this.showNewChannelModal.bind(this, 'P')}
- >
- {'+'}
- </a>
- </OverlayTrigger>
- </h4>
- </li>
- {privateChannelItems}
- </ul>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <h4>
- <FormattedMessage
- id='sidebar.direct'
- defaultMessage='Direct Messages'
- />
- </h4>
- </li>
- {directMessageItems}
- {directMessageMore}
- </ul>
- </div>
- </div>
- );
- }
-}
-
-Sidebar.defaultProps = {
-};
-Sidebar.propTypes = {
-};
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
deleted file mode 100644
index 00d30948a..000000000
--- a/web/react/components/sidebar_header.jsx
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import NavbarDropdown from './navbar_dropdown.jsx';
-import TutorialTip from './tutorial/tutorial_tip.jsx';
-
-import PreferenceStore from '../stores/preference_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-
-import {FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-const TutorialSteps = Constants.TutorialSteps;
-
-const Tooltip = ReactBootstrap.Tooltip;
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class SidebarHeader extends React.Component {
- constructor(props) {
- super(props);
-
- this.toggleDropdown = this.toggleDropdown.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
-
- this.state = this.getStateFromStores();
- }
- componentDidMount() {
- PreferenceStore.addChangeListener(this.onPreferenceChange);
- }
- componentWillUnmount() {
- PreferenceStore.removeChangeListener(this.onPreferenceChange);
- }
- getStateFromStores() {
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, this.props.currentUser.id, 999);
-
- return {showTutorialTip: tutorialStep === TutorialSteps.MENU_POPOVER};
- }
- onPreferenceChange() {
- this.setState(this.getStateFromStores());
- }
- toggleDropdown(e) {
- e.preventDefault();
- if (this.refs.dropdown.blockToggle) {
- this.refs.dropdown.blockToggle = false;
- return;
- }
- $('.team__header').find('.dropdown-toggle').dropdown('toggle');
- }
- createTutorialTip() {
- const screens = [];
-
- screens.push(
- <div>
- <FormattedHTMLMessage
- id='sidebar_header.tutorial'
- defaultMessage='<h4>Main Menu</h4>
- <p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p>
- <p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>'
- />
- </div>
- );
-
- return (
- <div
- onClick={this.toggleDropdown}
- >
- <TutorialTip
- ref='tip'
- placement='right'
- screens={screens}
- overlayClass='tip-overlay--header'
- />
- </div>
- );
- }
- render() {
- var me = this.props.currentUser;
- var profilePicture = null;
-
- if (!me) {
- return null;
- }
-
- if (me.last_picture_update) {
- profilePicture = (
- <img
- className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
- />
- );
- }
-
- let tutorialTip = null;
- if (this.state.showTutorialTip) {
- tutorialTip = this.createTutorialTip();
- }
-
- return (
- <div className='team__header theme'>
- {tutorialTip}
- <a
- href='#'
- onClick={this.toggleDropdown}
- >
- {profilePicture}
- <div className='header__info'>
- <div className='user__name'>{'@' + me.username}</div>
- <OverlayTrigger
- trigger={['hover', 'focus']}
- delayShow={1000}
- placement='bottom'
- overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDisplayName}</Tooltip>}
- ref='descriptionOverlay'
- >
- <div className='team__name'>{this.props.teamDisplayName}</div>
- </OverlayTrigger>
- </div>
- </a>
- <NavbarDropdown
- ref='dropdown'
- teamType={this.props.teamType}
- teamDisplayName={this.props.teamDisplayName}
- teamName={this.props.teamName}
- currentUser={this.props.currentUser}
- />
- </div>
- );
- }
-}
-
-SidebarHeader.defaultProps = {
- teamDisplayName: '',
- teamType: ''
-};
-SidebarHeader.propTypes = {
- teamDisplayName: React.PropTypes.string,
- teamName: React.PropTypes.string,
- teamType: React.PropTypes.string,
- currentUser: React.PropTypes.object
-};
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
deleted file mode 100644
index fc9888626..000000000
--- a/web/react/components/sidebar_right.jsx
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SearchResults from './search_results.jsx';
-import RhsThread from './rhs_thread.jsx';
-import SearchStore from '../stores/search_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-const SIDEBAR_SCROLL_DELAY = 500;
-
-export default class SidebarRight extends React.Component {
- constructor(props) {
- super(props);
-
- this.plScrolledToBottom = true;
-
- this.onSelectedChange = this.onSelectedChange.bind(this);
- this.onSearchChange = this.onSearchChange.bind(this);
- this.onShowSearch = this.onShowSearch.bind(this);
-
- this.doStrangeThings = this.doStrangeThings.bind(this);
-
- this.state = this.getStateFromStores();
- }
- getStateFromStores() {
- return {
- search_visible: SearchStore.getSearchResults() != null,
- post_right_visible: PostStore.getSelectedPost() != null,
- is_mention_search: SearchStore.getIsMentionSearch()
- };
- }
- componentDidMount() {
- SearchStore.addSearchChangeListener(this.onSearchChange);
- PostStore.addSelectedPostChangeListener(this.onSelectedChange);
- SearchStore.addShowSearchListener(this.onShowSearch);
- this.doStrangeThings();
- }
- componentWillUnmount() {
- SearchStore.removeSearchChangeListener(this.onSearchChange);
- PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
- SearchStore.removeShowSearchListener(this.onShowSearch);
- }
- componentWillUpdate(nextProps, nextState) {
- const isOpen = this.state.search_visible || this.state.post_right_visible;
- const willOpen = nextState.search_visible || nextState.post_right_visible;
-
- if (!isOpen && willOpen) {
- setTimeout(() => PostStore.jumpPostsViewSidebarOpen(), SIDEBAR_SCROLL_DELAY);
- }
- }
- doStrangeThings() {
- // We should have a better way to do this stuff
- // Hence the function name.
- var windowWidth = $(window).outerWidth();
- var sidebarRightWidth = $('.sidebar--right').outerWidth();
-
- $('.inner-wrap').removeClass('.move--right');
- $('.inner-wrap').addClass('move--left');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').addClass('move--left');
-
- //$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
- if (this.state.search_visible || this.state.post_right_visible) {
- if (windowWidth > 960) {
- $('.inner-wrap').velocity({marginRight: sidebarRightWidth}, {duration: 500, easing: 'easeOutSine'});
- $('.sidebar--right').velocity({translateX: 0}, {duration: 500, easing: 'easeOutSine'});
- } else {
- $('.inner-wrap, .sidebar--right').attr('style', '');
- }
- } else {
- if (windowWidth > 960) {
- $('.inner-wrap').velocity({marginRight: 0}, {duration: 500, easing: 'easeOutSine'});
- $('.sidebar--right').velocity({translateX: sidebarRightWidth}, {duration: 500, easing: 'easeOutSine'});
- } else {
- $('.inner-wrap, .sidebar--right').attr('style', '');
- }
- $('.inner-wrap').removeClass('move--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- return (
- <div></div>
- );
- }
-
- /*setTimeout(() => {
- $('.sidebar__overlay').fadeOut('200', () => {
- $('.sidebar__overlay').remove();
- });
- }, 500);*/
- return null;
- }
- componentDidUpdate() {
- this.doStrangeThings();
- }
- onSelectedChange(fromSearch) {
- var newState = this.getStateFromStores(fromSearch);
- newState.from_search = fromSearch;
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- onSearchChange() {
- var newState = this.getStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- onShowSearch() {
- if (!this.state.search_visible) {
- this.setState({
- search_visible: true
- });
- }
- }
- render() {
- var content = '';
-
- if (this.state.search_visible) {
- content = <SearchResults isMentionSearch={this.state.is_mention_search}/>;
- } else if (this.state.post_right_visible) {
- content = (
- <RhsThread
- fromSearch={this.state.from_search}
- isMentionSearch={this.state.is_mention_search}
- />
- );
- }
-
- return (
- <div
- className='sidebar--right'
- id='sidebar-right'
- >
- <div className='sidebar-right-container'>
- {content}
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
deleted file mode 100644
index c7c5bcfd6..000000000
--- a/web/react/components/sidebar_right_menu.jsx
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TeamMembersModal from './team_members_modal.jsx';
-import ToggleModalButton from './toggle_modal_button.jsx';
-import UserSettingsModal from './user_settings/user_settings_modal.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-import {Link} from 'react-router';
-
-export default class SidebarRightMenu extends React.Component {
- componentDidMount() {
- $('.sidebar--left .dropdown-menu').perfectScrollbar();
- }
-
- constructor(props) {
- super(props);
-
- this.state = {
- showUserSettingsModal: false
- };
- }
-
- render() {
- var teamLink = '';
- var inviteLink = '';
- var teamSettingsLink = '';
- var manageLink = '';
- var consoleLink = '';
- var currentUser = UserStore.getCurrentUser();
- var isAdmin = false;
- var isSystemAdmin = false;
-
- if (currentUser != null) {
- isAdmin = Utils.isAdmin(currentUser.roles);
- isSystemAdmin = Utils.isSystemAdmin(currentUser.roles);
-
- inviteLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showInviteMemberModal}
- >
- <i className='fa fa-user'></i>
- <FormattedMessage
- id='sidebar_right_menu.inviteNew'
- defaultMessage='Invite New Member'
- />
- </a>
- </li>
- );
-
- if (this.props.teamType === 'O') {
- teamLink = (
- <li>
- <a
- href='#'
- onClick={GlobalActions.showGetTeamInviteLinkModal}
- >
- <i className='glyphicon glyphicon-link'></i>
- <FormattedMessage
- id='sidebar_right_menu.teamLink'
- defaultMessage='Get Team Invite Link'
- />
- </a>
- </li>
- );
- }
- }
-
- if (isAdmin) {
- teamSettingsLink = (
- <li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_settings'
- >
- <i className='fa fa-globe'></i>
- <FormattedMessage
- id='sidebar_right_menu.teamSettings'
- defaultMessage='Team Settings'
- />
- </a>
- </li>
- );
- manageLink = (
- <li>
- <ToggleModalButton dialogType={TeamMembersModal}>
- <i className='fa fa-users'></i>
- <FormattedMessage
- id='sidebar_right_menu.manageMembers'
- defaultMessage='Manage Members'
- />
- </ToggleModalButton>
- </li>
- );
- }
-
- if (isSystemAdmin && !Utils.isMobile()) {
- consoleLink = (
- <li>
- <a
- href={'/admin_console'}
- >
- <i className='fa fa-wrench'></i>
- <FormattedMessage
- id='sidebar_right_menu.console'
- defaultMessage='System Console'
- />
- </a>
- </li>
- );
- }
-
- var siteName = '';
- if (global.window.mm_config.SiteName != null) {
- siteName = global.window.mm_config.SiteName;
- }
- var teamDisplayName = siteName;
- if (this.props.teamDisplayName) {
- teamDisplayName = this.props.teamDisplayName;
- }
-
- let helpLink = null;
- if (global.window.mm_config.HelpLink) {
- helpLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.HelpLink}
- >
- <i className='fa fa-question'></i>
- <FormattedMessage
- id='sidebar_right_menu.help'
- defaultMessage='Help'
- />
- </a>
- </li>
- );
- }
-
- let reportLink = null;
- if (global.window.mm_config.ReportAProblemLink) {
- reportLink = (
- <li>
- <a
- target='_blank'
- href={global.window.mm_config.ReportAProblemLink}
- >
- <i className='fa fa-phone'></i>
- <FormattedMessage
- id='sidebar_right_menu.report'
- defaultMessage='Report a Problem'
- />
- </a>
- </li>
- );
- }
- return (
- <div
- className='sidebar--menu'
- id='sidebar-menu'
- >
- <div className='team__header theme'>
- <a
- className='team__name'
- href='/channels/town-square'
- >{teamDisplayName}</a>
- </div>
-
- <div className='nav-pills__container'>
- <ul className='nav nav-pills nav-stacked'>
- <li>
- <a
- href='#'
- onClick={() => this.setState({showUserSettingsModal: true})}
- >
- <i className='fa fa-cog'></i>
- <FormattedMessage
- id='sidebar_right_menu.accountSettings'
- defaultMessage='Account Settings'
- />
- </a>
- </li>
- {teamSettingsLink}
- {inviteLink}
- {teamLink}
- {manageLink}
- {consoleLink}
- <li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
- <i className='fa fa-sign-out'></i>
- <FormattedMessage
- id='sidebar_right_menu.logout'
- defaultMessage='Logout'
- />
- </Link>
- </li>
- <li className='divider'></li>
- {helpLink}
- {reportLink}
- </ul>
- </div>
- <UserSettingsModal
- show={this.state.showUserSettingsModal}
- onModalDismissed={() => this.setState({showUserSettingsModal: false})}
- />
- </div>
- );
- }
-}
-
-SidebarRightMenu.propTypes = {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
deleted file mode 100644
index 2adf8d111..000000000
--- a/web/react/components/signup_team.jsx
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChoosePage from './team_signup_choose_auth.jsx';
-import EmailSignUpPage from './team_signup_with_email.jsx';
-import SSOSignupPage from './team_signup_with_sso.jsx';
-import LdapSignUpPage from './team_signup_with_ldap.jsx';
-import Constants from '../utils/constants.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamSignUp extends React.Component {
- constructor(props) {
- super(props);
-
- this.updatePage = this.updatePage.bind(this);
- this.onTeamUpdate = this.onTeamUpdate.bind(this);
-
- var count = 0;
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- count = count + 1;
- }
-
- if (count > 1) {
- this.state = {page: 'choose'};
- } else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- this.state = {page: 'email'};
- } else if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- this.state = {page: 'gitlab'};
- } else if (global.window.mm_config.EnableLdap === 'true') {
- this.state = {page: 'ldap'};
- } else {
- this.state = {page: 'none'};
- }
- }
-
- updatePage(page) {
- this.setState({page});
- }
-
- componentWillMount() {
- if (global.window.mm_config.EnableTeamListing === 'true') {
- AsyncClient.getAllTeams();
- this.onTeamUpdate();
- }
- }
-
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamUpdate);
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamUpdate);
- }
-
- onTeamUpdate() {
- this.setState({
- teams: TeamStore.getAll()
- });
- }
-
- render() {
- let teamListing = null;
-
- if (global.window.mm_config.EnableTeamListing === 'true') {
- if (this.state.teams == null) {
- teamListing = (<div/>);
- } else if (this.state.teams.length === 0) {
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- teamListing = (
- <div>
- <FormattedMessage
- id='signup_team.noTeams'
- defaultMessage='There are no teams include in the Team Directory and team creation has been disabled.'
- />
- </div>
- );
- }
- } else {
- teamListing = (
- <div>
- <h4>
- <FormattedMessage
- id='signup_team.choose'
- defaultMessage='Choose a Team'
- />
- </h4>
- <div className='signup-team-all'>
- {
- Object.values(this.state.teams).map((team) => {
- if (team.allow_team_listing) {
- return (
- <div
- key={'team_' + team.name}
- className='signup-team-dir'
- >
- <a
- href={'/' + team.name}
- >
- <span className='signup-team-dir__name'>{team.display_name}</span>
- <span
- className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
- aria-hidden='true'
- />
- </a>
- </div>
- );
- }
- return null;
- })
- }
- </div>
- <h4>
- <FormattedMessage
- id='signup_team.createTeam'
- defaultMessage='Or Create a Team'
- />
- </h4>
- </div>
- );
- }
- }
-
- let signupMethod = null;
-
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- if (teamListing == null) {
- signupMethod = (
- <FormattedMessage
- id='signup_team.disabled'
- defaultMessage='Team creation has been disabled. Please contact an administrator for access.'
- />
- );
- }
- } else if (this.state.page === 'choose') {
- signupMethod = (
- <ChoosePage
- updatePage={this.updatePage}
- />
- );
- } else if (this.state.page === 'email') {
- signupMethod = (
- <EmailSignUpPage/>
- );
- } else if (this.state.page === 'ldap') {
- return (
- <div>
- {teamListing}
- <LdapSignUpPage/>
- </div>
- );
- } else if (this.state.page === 'gitlab') {
- signupMethod = (
- <SSOSignupPage service={Constants.GITLAB_SERVICE}/>
- );
- } else if (this.state.page === 'google') {
- signupMethod = (
- <SSOSignupPage service={Constants.GOOGLE_SERVICE}/>
- );
- } else if (this.state.page === 'none') {
- signupMethod = (
- <FormattedMessage
- id='signup_team.none'
- defaultMessage='No team creation method has been enabled. Please contact an administrator for access.'
- />
- );
- }
-
- return (
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h1>{global.window.mm_config.SiteName}</h1>
- <h4 className='color--light'>
- <FormattedMessage
- id='web.root.singup_info'
- />
- </h4>
- <div id='signup-team'>
- {teamListing}
- {signupMethod}
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamSignUp.propTypes = {
-};
-
diff --git a/web/react/components/signup_team_complete/components/signup_team_complete.jsx b/web/react/components/signup_team_complete/components/signup_team_complete.jsx
deleted file mode 100644
index 5ad21e941..000000000
--- a/web/react/components/signup_team_complete/components/signup_team_complete.jsx
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import BrowserStore from '../../../stores/browser_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-export default class SignupTeamComplete extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateParent = this.updateParent.bind(this);
- }
- componentWillMount() {
- const data = JSON.parse(this.props.location.query.d);
- this.hash = this.props.location.query.h;
-
- var initialState = BrowserStore.getGlobalItem(this.hash);
-
- if (!initialState) {
- initialState = {};
- initialState.wizard = 'welcome';
- initialState.team = {};
- initialState.team.email = data.email;
- initialState.team.allowed_domains = '';
- initialState.invites = [];
- initialState.invites.push('');
- initialState.invites.push('');
- initialState.invites.push('');
- initialState.user = {};
- initialState.hash = this.hash;
- initialState.data = this.props.location.query.d;
- }
-
- this.setState(initialState);
- }
- componentDidMount() {
- browserHistory.push('/signup_team_complete/welcome');
- }
- updateParent(state, skipSet) {
- BrowserStore.setGlobalItem(this.hash, state);
-
- if (!skipSet) {
- this.setState(state);
- browserHistory.push('/signup_team_complete/' + state.wizard);
- }
- }
- render() {
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span classNameName='fa fa-chevron-left'/>
- <FormattedMessage id='web.header.back'/>
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <div id='signup-team-complete'>
- {React.cloneElement(this.props.children, {
- state: this.state,
- updateParent: this.updateParent
- })}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupTeamComplete.defaultProps = {
-};
-SignupTeamComplete.propTypes = {
- location: React.PropTypes.object,
- children: React.PropTypes.node
-};
diff --git a/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx b/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx
deleted file mode 100644
index 280e53ce4..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_display_name_page.jsx
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../../../utils/utils.jsx';
-import * as client from '../../../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- required: {
- id: 'team_signup_display_name.required',
- defaultMessage: 'This field is required'
- },
- charLength: {
- id: 'team_signup_display_name.charLength',
- defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
- }
-});
-
-class TeamSignupDisplayNamePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'welcome';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var displayName = ReactDOM.findDOMNode(this.refs.name).value.trim();
- if (!displayName) {
- this.setState({nameError: formatMessage(holders.required)});
- return;
- } else if (displayName.length < 4 || displayName.length > 15) {
- this.setState({nameError: formatMessage(holders.charLength)});
- return;
- }
-
- this.props.state.wizard = 'team_url';
- this.props.state.team.display_name = displayName;
- this.props.state.team.name = utils.cleanUpUrlable(displayName);
- this.props.updateParent(this.props.state);
- }
- handleFocus(e) {
- e.preventDefault();
- e.currentTarget.select();
- }
- render() {
- client.track('signup', 'signup_team_02_name');
-
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_display_name.teamName'
- defaultMessage='Team Name'
- />
- </h2>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-9'>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength='128'
- defaultValue={this.props.state.team.display_name}
- autoFocus={true}
- onFocus={this.handleFocus}
- spellCheck='false'
- />
- </div>
- </div>
- {nameError}
- </div>
- <div>
- <FormattedMessage
- id='team_signup_display_name.nameHelp'
- defaultMessage='Name your team in any language. Your team name shows in menus and headings.'
- />
- </div>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_display_name.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_display_name.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupDisplayNamePage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupDisplayNamePage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_email_item.jsx b/web/react/components/signup_team_complete/components/team_signup_email_item.jsx
deleted file mode 100644
index c87d6ec07..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_email_item.jsx
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const holders = defineMessages({
- validEmail: {
- id: 'team_signup_email.validEmail',
- defaultMessage: 'Please enter a valid email address'
- },
- different: {
- id: 'team_signup_email.different',
- defaultMessage: 'Please use a different email than the one used at signup'
- },
- address: {
- id: 'team_signup_email.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class TeamSignupEmailItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.getValue = this.getValue.bind(this);
- this.validate = this.validate.bind(this);
-
- this.state = {};
- }
- getValue() {
- return ReactDOM.findDOMNode(this.refs.email).value.trim();
- }
- validate(teamEmail) {
- const {formatMessage} = this.props.intl;
- const email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
-
- if (!email) {
- return true;
- }
-
- if (!Utils.isEmail(email)) {
- this.setState({emailError: formatMessage(holders.validEmail)});
- return false;
- } else if (email === teamEmail) {
- this.setState({emailError: formatMessage(holders.different)});
- return false;
- }
-
- this.setState({emailError: ''});
- return true;
- }
- render() {
- let emailError = null;
- let emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- return (
- <div className={emailDivClass}>
- <input
- autoFocus={this.props.focus}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- defaultValue={this.props.email}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- );
- }
-}
-
-TeamSignupEmailItem.propTypes = {
- intl: intlShape.isRequired,
- focus: React.PropTypes.bool,
- email: React.PropTypes.string
-};
-
-export default injectIntl(TeamSignupEmailItem, {withRef: true});
diff --git a/web/react/components/signup_team_complete/components/team_signup_finished.jsx b/web/react/components/signup_team_complete/components/team_signup_finished.jsx
deleted file mode 100644
index fc5f756e7..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_finished.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class FinishedPage extends React.Component {
- render() {
- return (
- <FormattedMessage
- id='signup_team_complete.completed'
- defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
- />
- );
- }
-}
diff --git a/web/react/components/signup_team_complete/components/team_signup_password_page.jsx b/web/react/components/signup_team_complete/components/team_signup_password_page.jsx
deleted file mode 100644
index 490a11040..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_password_page.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../../utils/client.jsx';
-import BrowserStore from '../../../stores/browser_store.jsx';
-import UserStore from '../../../stores/user_store.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- passwordError: {
- id: 'team_signup_password.passwordError',
- defaultMessage: 'Please enter at least {chars} characters'
- },
- creating: {
- id: 'team_signup_password.creating',
- defaultMessage: 'Creating team...'
- }
-});
-
-class TeamSignupPasswordPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({passwordError: this.props.intl.formatMessage(holders.passwordError, {chars: Constants.MIN_PASSWORD_LENGTH})});
- return;
- }
-
- this.setState({passwordError: null, serverError: null});
- $('#finish-button').button('loading');
- var teamSignup = JSON.parse(JSON.stringify(this.props.state));
- teamSignup.user.password = password;
- teamSignup.user.allow_marketing = true;
- delete teamSignup.wizard;
-
- Client.createTeamFromSignup(teamSignup,
- () => {
- Client.track('signup', 'signup_team_08_complete');
-
- var props = this.props;
-
- Client.loginByEmail(teamSignup.team.name, teamSignup.team.email, teamSignup.user.password,
- () => {
- UserStore.setLastEmail(teamSignup.team.email);
- if (this.props.hash > 0) {
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
- }
-
- $('#sign-up-button').button('reset');
- props.state.wizard = 'finished';
- props.updateParent(props.state, true);
-
- browserHistory.push('/' + teamSignup.team.name + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name));
- } else {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- }
- );
- },
- (err) => {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- );
- }
- render() {
- Client.track('signup', 'signup_team_07_password');
-
- var passwordError = null;
- var passwordDivStyle = 'form-group';
- if (this.state.passwordError) {
- passwordError = <div className='form-group has-error'><label className='control-label'>{this.state.passwordError}</label></div>;
- passwordDivStyle = ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_password.yourPassword'
- defaultMessage='Your password'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_password.selectPassword'
- defaultMessage="Select a password that you'll use to login with your email address:"
- />
- </h5>
- <div className='inner__content margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.email'
- defaultMessage='Email'
- />
- </strong></h5>
- <div className='block--gray form-group'>{this.props.state.team.email}</div>
- <div className={passwordDivStyle}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.choosePwd'
- defaultMessage='Choose your password'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='password'
- ref='password'
- className='form-control'
- placeholder=''
- maxLength='128'
- spellCheck='false'
- />
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_password.hint'
- defaultMessage='Passwords must contain {min} to {max} characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'
- values={{
- min: Constants.MIN_PASSWORD_LENGTH,
- max: Constants.MAX_PASSWORD_LENGTH
- }}
- />
- </span>
- </div>
- </div>
- {passwordError}
- {serverError}
- </div>
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- id='finish-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.creating)}
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_password.finish'
- defaultMessage='Finish'
- />
- </button>
- </div>
- <p>
- <FormattedHTMLMessage
- id='team_signup_password.agreement'
- defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_password.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupPasswordPage.defaultProps = {
- state: {},
- hash: ''
-};
-TeamSignupPasswordPage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- hash: React.PropTypes.string,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupPasswordPage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx b/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx
deleted file mode 100644
index 5e987ef2c..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_send_invites_page.jsx
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EmailItem from './team_signup_email_item.jsx';
-import * as Client from '../../../utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class TeamSignupSendInvitesPage extends React.Component {
- constructor(props) {
- super(props);
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
- this.submitAddInvite = this.submitAddInvite.bind(this);
- this.submitSkip = this.submitSkip.bind(this);
- this.keySubmit = this.keySubmit.bind(this);
- this.state = {
- emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
- };
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'team_url';
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var valid = true;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (this.refs['email_' + i].getWrappedInstance().validate(this.props.state.team.email)) {
- emails.push(this.refs['email_' + i].getWrappedInstance().getValue());
- } else {
- valid = false;
- }
- }
-
- if (valid) {
- this.props.state.invites = emails;
- }
- }
-
- if (valid) {
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- }
- submitAddInvite(e) {
- e.preventDefault();
- this.props.state.wizard = 'send_invites';
- if (!this.props.state.invites) {
- this.props.state.invites = [];
- }
- this.props.state.invites.push('');
- this.props.updateParent(this.props.state);
- }
- submitSkip(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- keySubmit(e) {
- if (e && e.keyCode === 13) {
- this.submitNext(e);
- }
- }
- componentDidMount() {
- if (!this.state.emailEnabled) {
- // Must use keypress not keyup due to event chain of pressing enter
- $('body').keypress(this.keySubmit);
- }
- }
- componentWillUnmount() {
- if (!this.state.emailEnabled) {
- $('body').off('keypress', this.keySubmit);
- }
- }
- render() {
- Client.track('signup', 'signup_team_05_send_invites');
-
- var content = null;
- var bottomContent = null;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (i === 0) {
- emails.push(
- <EmailItem
- focus={true}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- } else {
- emails.push(
- <EmailItem
- focus={false}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- }
- }
-
- content = (
- <div>
- {emails}
- <div className='form-group text-right'>
- <a
- href='#'
- onClick={this.submitAddInvite}
- >
- <FormattedMessage
- id='team_signup_send_invites.addInvitation'
- defaultMessage='Add Invitation'
- />
- </a>
- </div>
- </div>
- );
-
- bottomContent = (
- <p className='color--light'>
- <FormattedHTMLMessage
- id='team_signup_send_invites.prefer'
- defaultMessage='if you prefer, you can invite team members later<br /> and '
- />
- <a
- href='#'
- onClick={this.submitSkip}
- >
- <FormattedMessage
- id='team_signup_send_invites.skip'
- defaultMessage='skip this step '
- />
- </a>
- <FormattedMessage
- id='team_signup_send_invites.forNow'
- defaultMessage='for now.'
- />
- </p>
- );
- } else {
- content = (
- <div className='form-group color--light'>
- <FormattedMessage
- id='team_signup_send_invites.disabled'
- defaultMessage='Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.'
- />
- </div>
- );
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_send_invites.title'
- defaultMessage='Invite Team Members'
- />
- </h2>
- {content}
- <div className='form-group'>
- <button
- type='submit'
- className='btn-primary btn'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_send_invites.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'/>
- </button>
- </div>
- </form>
- {bottomContent}
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_send_invites.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-
-TeamSignupSendInvitesPage.propTypes = {
- state: React.PropTypes.object.isRequired,
- updateParent: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/signup_team_complete/components/team_signup_url_page.jsx b/web/react/components/signup_team_complete/components/team_signup_url_page.jsx
deleted file mode 100644
index ec50e2d25..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_url_page.jsx
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- required: {
- id: 'team_signup_url.required',
- defaultMessage: 'This field is required'
- },
- regex: {
- id: 'team_signup_url.regex',
- defaultMessage: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
- },
- charLength: {
- id: 'team_signup_url.charLength',
- defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
- },
- taken: {
- id: 'team_signup_url.taken',
- defaultMessage: 'URL is taken or contains a reserved word'
- },
- unavailable: {
- id: 'team_signup_url.unavailable',
- defaultMessage: 'This URL is unavailable. Please try another.'
- }
-});
-
-class TeamSignupUrlPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
-
- this.state = {nameError: ''};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'team_display_name';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- const name = ReactDOM.findDOMNode(this.refs.name).value.trim();
- if (!name) {
- this.setState({nameError: formatMessage(holders.required)});
- return;
- }
-
- const cleanedName = Utils.cleanUpUrlable(name);
-
- const urlRegex = /^[a-z]+([a-z\-0-9]+|(__)?)[a-z0-9]+$/g;
- if (cleanedName !== name || !urlRegex.test(name)) {
- this.setState({nameError: formatMessage(holders.regex)});
- return;
- } else if (cleanedName.length < 4 || cleanedName.length > 15) {
- this.setState({nameError: formatMessage(holders.charLength)});
- return;
- }
-
- if (global.window.mm_config.RestrictTeamNames === 'true') {
- for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) {
- if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) {
- this.setState({nameError: formatMessage(holders.taken)});
- return;
- }
- }
- }
-
- Client.findTeamByName(name,
- (data) => {
- if (data) {
- this.setState({nameError: formatMessage(holders.unavailable)});
- } else {
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'username';
- }
- this.props.state.team.type = 'O';
-
- this.props.state.team.name = name;
- this.props.updateParent(this.props.state);
- }
- },
- (err) => {
- this.setState({nameError: err.message});
- }
- );
- }
- handleFocus(e) {
- e.preventDefault();
-
- e.currentTarget.select();
- }
- render() {
- $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
-
- Client.track('signup', 'signup_team_03_url');
-
- let nameError = null;
- let nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- const title = `${Utils.getWindowLocationOrigin()}/`;
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2>
- <FormattedMessage
- id='team_signup_url.teamUrl'
- defaultMessage='Team URL'
- />
- </h2>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-11'>
- <div className='input-group input-group--limit'>
- <span
- data-toggle='tooltip'
- title={title}
- className='input-group-addon'
- >
- {title}
- </span>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength='128'
- defaultValue={this.props.state.team.name}
- autoFocus={true}
- onFocus={this.handleFocus}
- spellCheck='false'
- />
- </div>
- </div>
- </div>
- {nameError}
- </div>
- <p>
- <FormattedMessage
- id='team_signup_url.webAddress'
- defaultMessage='Choose the web address of your new team:'
- />
- </p>
- <ul className='color--light'>
- <FormattedHTMLMessage
- id='team_signup_url.hint'
- defaultMessage="<li>Short and memorable is best</li>
- <li>Use lowercase 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}
- >
- <FormattedMessage
- id='team_signup_url.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_url.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupUrlPage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupUrlPage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_username_page.jsx b/web/react/components/signup_team_complete/components/team_signup_username_page.jsx
deleted file mode 100644
index e56aa4cd7..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_username_page.jsx
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import Constants from '../../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- reserved: {
- id: 'team_signup_username.reserved',
- defaultMessage: 'This username is reserved, please choose a new one.'
- },
- invalid: {
- id: 'team_signup_username.invalid',
- defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''
- }
-});
-
-class TeamSignupUsernamePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'team_url';
- }
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var name = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
-
- var usernameError = Utils.isValidUsername(name);
- if (usernameError === 'Cannot use a reserved word as a username.') { //this should be change to some kind of ID
- this.setState({nameError: formatMessage(holders.reserved)});
- return;
- } else if (usernameError) {
- this.setState({nameError: formatMessage(holders.invalid, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
- return;
- }
-
- this.props.state.wizard = 'password';
- this.props.state.user.username = name;
- this.props.updateParent(this.props.state);
- }
- render() {
- Client.track('signup', 'signup_team_06_username');
-
- var nameError = null;
- var nameHelpText = (
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_username.hint'
- defaultMessage="Usernames must begin with a letter and contain between {min} to {max} characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"
- values={{
- min: Constants.MIN_USERNAME_LENGTH,
- max: Constants.MAX_USERNAME_LENGTH
- }}
- />
- </span>
- );
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameHelpText = '';
- nameDivClass += ' has-error';
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_username.username'
- defaultMessage='Your username'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_username.memorable'
- defaultMessage='Select a memorable username that makes it easy for teammates to identify you:'
- />
- </h5>
- <div className='inner__content margin--extra'>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_username.chooseUsername'
- defaultMessage='Choose your username'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- defaultValue={this.props.state.user.username}
- maxLength={Constants.MAX_USERNAME_LENGTH}
- spellCheck='false'
- />
- {nameHelpText}
- </div>
- </div>
- {nameError}
- </div>
- </div>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_username.next'
- defaultMessage='Next'
- />
- <i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_username.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupUsernamePage.defaultProps = {
- state: null
-};
-TeamSignupUsernamePage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupUsernamePage);
diff --git a/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx b/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx
deleted file mode 100644
index 97782e54a..000000000
--- a/web/react/components/signup_team_complete/components/team_signup_welcome_page.jsx
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../../utils/utils.jsx';
-import * as Client from '../../../utils/client.jsx';
-import BrowserStore from '../../../stores/browser_store.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- storageError: {
- id: 'team_signup_welcome.storageError',
- defaultMessage: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'
- },
- validEmailError: {
- id: 'team_signup_welcome.validEmailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'team_signup_welcome.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class TeamSignupWelcomePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitNext = this.submitNext.bind(this);
- this.handleDiffEmail = this.handleDiffEmail.bind(this);
- this.handleDiffSubmit = this.handleDiffSubmit.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
-
- this.state = {useDiff: false};
-
- document.addEventListener('keyup', this.handleKeyPress, false);
- }
- submitNext(e) {
- if (!BrowserStore.isLocalStorageSupported()) {
- this.setState({storageError: this.props.intl.formatMessage(holders.storageError)});
- return;
- }
- e.preventDefault();
- this.props.state.wizard = 'team_display_name';
- this.props.updateParent(this.props.state);
- }
- handleDiffEmail(e) {
- e.preventDefault();
- this.setState({useDiff: true});
- }
- handleDiffSubmit(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var state = {useDiff: true, serverError: ''};
-
- var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!email || !Utils.isEmail(email)) {
- state.emailError = formatMessage(holders.validEmailError);
- this.setState(state);
- return;
- } else if (!BrowserStore.isLocalStorageSupported()) {
- state.emailError = formatMessage(holders.storageError);
- this.setState(state);
- return;
- }
- state.emailError = '';
-
- Client.signupTeam(email,
- function success(data) {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- } else {
- this.props.state.wizard = 'finished';
- this.props.updateParent(this.props.state);
- browserHistory.push('/signup_team_confirm/?email=' + encodeURIComponent(email));
- }
- }.bind(this),
- function error(err) {
- let errorMsg = err.message;
-
- if (err.detailed_error.indexOf('Invalid RCPT TO address provided') >= 0) {
- errorMsg = formatMessage(holders.validEmailError);
- }
-
- this.setState({emailError: '', serverError: errorMsg});
- }.bind(this)
- );
- }
- handleKeyPress(event) {
- if (event.keyCode === 13) {
- this.submitNext(event);
- }
- }
- componentWillUnmount() {
- document.removeEventListener('keyup', this.handleKeyPress, false);
- }
- render() {
- Client.track('signup', 'signup_team_01_welcome');
-
- var storageError = null;
- if (this.state.storageError) {
- storageError = <label className='control-label'>{this.state.storageError}</label>;
- }
-
- var emailError = null;
- var emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- var differentEmailLinkClass = '';
- var emailDivContainerClass = 'hidden';
- if (this.state.useDiff) {
- differentEmailLinkClass = 'hidden';
- emailDivContainerClass = '';
- }
-
- return (
- <div>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h3 className='sub-heading'>
- <FormattedMessage
- id='team_signup_welcome.welcome'
- defaultMessage='Welcome to:'
- />
- </h3>
- <h1 className='margin--top-none'>{global.window.mm_config.SiteName}</h1>
- <p className='margin--less'>
- <FormattedMessage
- id='team_signup_welcome.lets'
- defaultMessage="Let's set up your new team"
- />
- </p>
- <div>
- <FormattedMessage
- id='team_signup_welcome.confirm'
- defaultMessage='Please confirm your email address:'
- />
- <br/>
- <div className='inner__content'>
- <div className='block--gray'>{this.props.state.team.email}</div>
- </div>
- </div>
- <p className='margin--extra color--light'>
- <FormattedHTMLMessage
- id='team_signup_welcome.admin'
- defaultMessage='Your account will administer the new team site. <br />
- You can add other administrators later.'
- />
- </p>
- <div className='form-group'>
- <button
- className='btn-primary btn form-group'
- type='submit'
- onClick={this.submitNext}
- >
- <i className='glyphicon glyphicon-ok'></i>
- <FormattedMessage
- id='team_signup_welcome.yes'
- defaultMessage='Yes, this address is correct'
- />
- </button>
- {storageError}
- </div>
- <hr/>
- <div className={emailDivContainerClass}>
- <div className={emailDivClass}>
- <div className='row'>
- <div className='col-sm-9'>
- <input
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- </div>
- </div>
- {emailError}
- </div>
- {serverError}
- <button
- className='btn btn-md btn-primary'
- type='button'
- onClick={this.handleDiffSubmit}
- >
- <FormattedMessage
- id='team_signup_welcome.instead'
- defaultMessage='Use this instead'
- />
- </button>
- </div>
- <a
- href='#'
- onClick={this.handleDiffEmail}
- className={differentEmailLinkClass}
- >
- <FormattedMessage
- id='team_signup_welcome.different'
- defaultMessage='Use a different email'
- />
- </a>
- </div>
- );
- }
-}
-
-TeamSignupWelcomePage.defaultProps = {
- state: {}
-};
-TeamSignupWelcomePage.propTypes = {
- intl: intlShape.isRequired,
- updateParent: React.PropTypes.func.isRequired,
- state: React.PropTypes.object
-};
-
-export default injectIntl(TeamSignupWelcomePage);
diff --git a/web/react/components/signup_team_confirm.jsx b/web/react/components/signup_team_confirm.jsx
deleted file mode 100644
index 1afbb3d30..000000000
--- a/web/react/components/signup_team_confirm.jsx
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class SignupTeamConfirm extends React.Component {
- render() {
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- <div className='col-sm-12'>
- <div classNameName='signup-team__container'>
- <h3>
- <FormattedMessage
- id='signup_team_confirm.title'
- defaultMessage='Sign up Complete'
- />
- </h3>
- <p>
- <FormattedHTMLMessage
- id='signup_team_confirm.checkEmail'
- defaultMessage='Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team'
- values={{
- email: this.props.location.query.email
- }}
- />
- </p>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupTeamConfirm.defaultProps = {
-};
-SignupTeamConfirm.propTypes = {
- location: React.PropTypes.object
-};
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
deleted file mode 100644
index d2128a50f..000000000
--- a/web/react/components/signup_user_complete.jsx
+++ /dev/null
@@ -1,491 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-import UserStore from '../stores/user_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import Constants from '../utils/constants.jsx';
-import LoadingScreen from '../components/loading_screen.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-class SignupUserComplete extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.inviteInfoRecieved = this.inviteInfoRecieved.bind(this);
-
- this.state = {
- data: '',
- hash: '',
- usedBefore: false,
- email: '',
- teamDisplayName: '',
- teamName: '',
- teamId: ''
- };
- }
- componentWillMount() {
- let data = this.props.location.query.d;
- let hash = this.props.location.query.h;
- const inviteId = this.props.location.query.id;
- let usedBefore = false;
- let email = '';
- let teamDisplayName = '';
- let teamName = '';
- let teamId = '';
-
- // If we have a hash in the url then we are attempting to access a private team
- if (hash) {
- const parsedData = JSON.parse(data);
- usedBefore = BrowserStore.getGlobalItem(hash);
- email = parsedData.email;
- teamDisplayName = parsedData.display_name;
- teamName = parsedData.name;
- teamId = parsedData.id;
- } else {
- Client.getInviteInfo(this.inviteInfoRecieved, null, inviteId);
- data = '';
- hash = '';
- }
-
- this.setState({
- data,
- hash,
- usedBefore,
- email,
- teamDisplayName,
- teamName,
- teamId
- });
- }
- inviteInfoRecieved(data) {
- if (!data) {
- return;
- }
-
- this.setState({
- teamDisplayName: data.display_name,
- teamName: data.name,
- teamId: data.id
- });
- }
- handleSubmit(e) {
- e.preventDefault();
-
- const providedEmail = ReactDOM.findDOMNode(this.refs.email).value.trim();
- if (!providedEmail) {
- this.setState({
- nameError: '',
- emailError: (<FormattedMessage id='signup_user_completed.required'/>),
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- if (!Utils.isEmail(providedEmail)) {
- this.setState({
- nameError: '',
- emailError: (<FormattedMessage id='signup_user_completed.validEmail'/>),
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const providedUsername = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
- if (!providedUsername) {
- this.setState({
- nameError: (<FormattedMessage id='signup_user_completed.required'/>),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const usernameError = Utils.isValidUsername(providedUsername);
- if (usernameError === 'Cannot use a reserved word as a username.') {
- this.setState({
- nameError: (<FormattedMessage id='signup_user_completed.reserved'/>),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- } else if (usernameError) {
- this.setState({
- nameError: (
- <FormattedMessage
- id='signup_user_completed.usernameLength'
- min={Constants.MIN_USERNAME_LENGTH}
- max={Constants.MAX_USERNAME_LENGTH}
- />
- ),
- emailError: '',
- passwordError: '',
- serverError: ''
- });
- return;
- }
-
- const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!providedPassword || providedPassword.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({
- nameError: '',
- emailError: '',
- passwordError: (
- <FormattedMessage
- id='signup_user_completed.passwordLength'
- min={Constants.MIN_PASSWORD_LENGTH}
- />
- ),
- serverError: ''
- });
- return;
- }
-
- this.setState({
- nameError: '',
- emailError: '',
- passwordError: '',
- serverError: ''
- });
-
- const user = {
- team_id: this.state.teamId,
- email: providedEmail,
- username: providedUsername,
- password: providedPassword,
- allow_marketing: true
- };
-
- Client.createUser(user, this.state.data, this.state.hash,
- () => {
- Client.track('signup', 'signup_user_02_complete');
-
- Client.loginByEmail(this.state.teamName, user.email, user.password,
- () => {
- UserStore.setLastEmail(user.email);
- if (this.state.hash > 0) {
- BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true}));
- }
- browserHistory.push('/' + this.state.teamName + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/should_verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.state.teamName));
- } else {
- this.setState({serverError: err.message});
- }
- }
- );
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- render() {
- Client.track('signup', 'signup_user_01_welcome');
-
- // If we have been used then just display a message
- if (this.state.usedBefore) {
- return (
- <div>
- <FormattedMessage
- id='signup_user_completed.expired'
- defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
- />
- </div>
- );
- }
-
- // If we haven't got a team id yet we are waiting for
- // the client so just show the standard loading screen
- if (this.state.teamId === '') {
- return (<LoadingScreen/>);
- }
-
- // set up error labels
- var emailError = null;
- var emailHelpText = (
- <span className='help-block'>
- <FormattedMessage
- id='signup_user_completed.emailHelp'
- defaultMessage='Valid email required for sign-up'
- />
- </span>
- );
- var emailDivStyle = 'form-group';
- if (this.state.emailError) {
- emailError = (<label className='control-label'>{this.state.emailError}</label>);
- emailHelpText = '';
- emailDivStyle += ' has-error';
- }
-
- var nameError = null;
- var nameHelpText = (
- <span className='help-block'>
- <FormattedMessage
- id='signup_user_completed.userHelp'
- defaultMessage="Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"
- values={{
- min: Constants.MIN_USERNAME_LENGTH,
- max: Constants.MAX_USERNAME_LENGTH
- }}
- />
- </span>
- );
- var nameDivStyle = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameHelpText = '';
- nameDivStyle += ' has-error';
- }
-
- var passwordError = null;
- var passwordDivStyle = 'form-group';
- if (this.state.passwordError) {
- passwordError = <label className='control-label'>{this.state.passwordError}</label>;
- passwordDivStyle += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className={'form-group has-error'}>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- // set up the email entry and hide it if an email was provided
- var yourEmailIs = '';
- if (this.state.email) {
- yourEmailIs = (
- <FormattedHTMLMessage
- id='signup_user_completed.emailIs'
- defaultMessage="Your email address is <strong>{email}</strong>. You'll use this address to sign in to {siteName}."
- values={{
- email: this.state.email,
- siteName: global.window.mm_config.SiteName
- }}
- />
- );
- }
-
- var emailContainerStyle = 'margin--extra';
- if (this.state.email) {
- emailContainerStyle = 'hidden';
- }
-
- var email = (
- <div className={emailContainerStyle}>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.whatis'
- defaultMessage="What's your email address?"
- />
- </strong></h5>
- <div className={emailDivStyle}>
- <input
- type='email'
- ref='email'
- className='form-control'
- defaultValue={this.state.email}
- placeholder=''
- maxLength='128'
- autoFocus={true}
- spellCheck='false'
- />
- {emailError}
- {emailHelpText}
- </div>
- </div>
- );
-
- var signupMessage = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- signupMessage.push(
- <a
- className='btn btn-custom-login gitlab'
- key='gitlab'
- href={'/api/v1/oauth/gitlab/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='signup_user_completed.gitlab'
- defaultMessage='with GitLab'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- signupMessage.push(
- <a
- className='btn btn-custom-login google'
- key='google'
- href={'/api/v1/oauth/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='signup_user_completed.google'
- defaultMessage='with Google'
- />
- </span>
- </a>
- );
- }
-
- var emailSignup;
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- emailSignup = (
- <div>
- <div className='inner__content'>
- {email}
- {yourEmailIs}
- <div className='margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.chooseUser'
- defaultMessage='Choose your username'
- />
- </strong></h5>
- <div className={nameDivStyle}>
- <input
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- maxLength={Constants.MAX_USERNAME_LENGTH}
- spellCheck='false'
- />
- {nameError}
- {nameHelpText}
- </div>
- </div>
- <div className='margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='signup_user_completed.choosePwd'
- defaultMessage='Choose your password'
- />
- </strong></h5>
- <div className={passwordDivStyle}>
- <input
- type='password'
- ref='password'
- className='form-control'
- placeholder=''
- maxLength='128'
- spellCheck='false'
- />
- {passwordError}
- </div>
- </div>
- </div>
- <p className='margin--extra'>
- <button
- type='submit'
- onClick={this.handleSubmit}
- className='btn-primary btn'
- >
- <FormattedMessage
- id='signup_user_completed.create'
- defaultMessage='Create Account'
- />
- </button>
- </p>
- </div>
- );
- }
-
- if (signupMessage.length > 0 && emailSignup) {
- signupMessage = (
- <div>
- {signupMessage}
- <div className='or__container'>
- <FormattedMessage
- id='signup_user_completed.or'
- defaultMessage='or'
- />
- </div>
- </div>
- );
- }
-
- if (signupMessage.length === 0 && !emailSignup) {
- emailSignup = (
- <div>
- <FormattedMessage
- id='signup_user_completed.none'
- defaultMessage='No user creation method has been enabled. Please contact an administrator for access.'
- />
- </div>
- );
- }
-
- return (
- <div>
- <div className='signup-header'>
- <a href='/'>
- <span classNameNameName='fa fa-chevron-left'/>
- <FormattedMessage id='web.header.back'/>
- </a>
- </div>
- <div className='col-sm-12'>
- <div className='signup-team__container padding--less'>
- <form>
- <img
- className='signup-team-logo'
- src='/static/images/logo.png'
- />
- <h5 className='margin--less'>
- <FormattedMessage
- id='signup_user_completed.welcome'
- defaultMessage='Welcome to:'
- />
- </h5>
- <h2 className='signup-team__name'>{this.state.teamName}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='signup_user_completed.onSite'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
- <h4 className='color--light'>
- <FormattedMessage
- id='signup_user_completed.lets'
- defaultMessage="Let's create your account"
- />
- </h4>
- {signupMessage}
- {emailSignup}
- {serverError}
- </form>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupUserComplete.defaultProps = {
-};
-SignupUserComplete.propTypes = {
- location: React.PropTypes.object
-};
-
-export default SignupUserComplete;
diff --git a/web/react/components/suggestion/at_mention_provider.jsx b/web/react/components/suggestion/at_mention_provider.jsx
deleted file mode 100644
index b53b351d9..000000000
--- a/web/react/components/suggestion/at_mention_provider.jsx
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const MaxUserSuggestions = 40;
-
-class AtMentionSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let username;
- let description;
- let icon;
- if (item.username === 'all') {
- username = 'all';
- description = (
- <FormattedMessage
- id='suggestion.mention.all'
- defaultMessage='Notifies everyone in the team'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else if (item.username === 'channel') {
- username = 'channel';
- description = (
- <FormattedMessage
- id='suggestion.mention.channel'
- defaultMessage='Notifies everyone in the channel'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else {
- username = item.username;
- description = Utils.getFullName(item);
- icon = (
- <img
- className='mention__image'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- );
- }
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='pull-left'>
- {icon}
- </div>
- <div className='pull-left mention--align'>
- <span>
- {'@' + username}
- </span>
- <span className='mention__fullname'>
- {description}
- </span>
- </div>
- </div>
- );
- }
-}
-
-AtMentionSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class AtMentionProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getActiveOnlyProfiles(true);
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix) && user.delete_at <= 0) {
- filtered.push(user);
- }
-
- if (filtered.length >= MaxUserSuggestions) {
- break;
- }
- }
-
- // 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'});
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const mentions = filtered.map((user) => '@' + user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, captured[0]);
- SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/command_provider.jsx b/web/react/components/suggestion/command_provider.jsx
deleted file mode 100644
index fffb2df07..000000000
--- a/web/react/components/suggestion/command_provider.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-class CommandSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'command';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='command__title'>
- <string>{item.suggestion} {item.hint}</string>
- </div>
- <div className='command__desc'>
- {item.description}
- </div>
- </div>
- );
- }
-}
-
-CommandSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
- if (pretext.startsWith('/')) {
- AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/emoticon_provider.jsx b/web/react/components/suggestion/emoticon_provider.jsx
deleted file mode 100644
index fd470cf21..000000000
--- a/web/react/components/suggestion/emoticon_provider.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Emoticons from '../../utils/emoticons.jsx';
-
-const MAX_EMOTICON_SUGGESTIONS = 40;
-
-class EmoticonSuggestion extends React.Component {
- render() {
- const text = this.props.term;
- const name = this.props.item;
-
- let className = 'emoticon-suggestion';
- if (this.props.isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.props.onClick}
- >
- <div className='pull-left'>
- <img
- alt={text}
- className='emoticon-suggestion__image'
- src={Emoticons.getImagePathForEmoticon(name)}
- title={text}
- />
- </div>
- <div className='pull-left'>
- {text}
- </div>
- </div>
- );
- }
-}
-
-EmoticonSuggestion.propTypes = {
- item: React.PropTypes.string.isRequired,
- term: React.PropTypes.string.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class EmoticonProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/(?:^|\s)(:([a-zA-Z0-9_+\-]*))$/g).exec(pretext);
- if (captured) {
- const text = captured[1];
- const partialName = captured[2];
-
- const names = [];
-
- for (const emoticon of Emoticons.emoticonMap.keys()) {
- if (emoticon.indexOf(partialName) !== -1) {
- names.push(emoticon);
-
- if (names.length >= MAX_EMOTICON_SUGGESTIONS) {
- break;
- }
- }
- }
-
- // sort the emoticons so that emoticons starting with the entered text come first
- names.sort((a, b) => {
- const aPrefix = a.startsWith(partialName);
- const bPrefix = b.startsWith(partialName);
-
- if (aPrefix === bPrefix) {
- return a.localeCompare(b);
- } else if (aPrefix) {
- return -1;
- }
-
- return 1;
- });
-
- const terms = names.map((name) => ':' + name + ':');
-
- if (terms.length > 0) {
- SuggestionStore.setMatchedPretext(suggestionId, text);
- SuggestionStore.addSuggestions(suggestionId, terms, names, EmoticonSuggestion);
-
- // force the selection to be cleared since the order of elements may have changed
- SuggestionStore.clearSelection(suggestionId);
- }
- }
- }
-}
diff --git a/web/react/components/suggestion/search_channel_provider.jsx b/web/react/components/suggestion/search_channel_provider.jsx
deleted file mode 100644
index 66a534907..000000000
--- a/web/react/components/suggestion/search_channel_provider.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../../stores/channel_store.jsx';
-import Constants from '../../utils/constants.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-class SearchChannelSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- onClick={onClick}
- className={className}
- >
- <i className='fa fa fa-plus-square'></i>{item.name}
- </div>
- );
- }
-}
-
-SearchChannelSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchChannelProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const channelPrefix = captured[1];
-
- const channels = ChannelStore.getAll();
- const publicChannels = [];
- const privateChannels = [];
-
- for (const id of Object.keys(channels)) {
- const channel = channels[id];
-
- // don't show direct channels
- if (channel.type !== Constants.DM_CHANNEL && channel.name.startsWith(channelPrefix)) {
- if (channel.type === Constants.OPEN_CHANNEL) {
- publicChannels.push(channel);
- } else {
- privateChannels.push(channel);
- }
- }
- }
-
- publicChannels.sort((a, b) => a.name.localeCompare(b.name));
- const publicChannelNames = publicChannels.map((channel) => channel.name);
-
- privateChannels.sort((a, b) => a.name.localeCompare(b.name));
- const privateChannelNames = privateChannels.map((channel) => channel.name);
-
- SuggestionStore.setMatchedPretext(suggestionId, channelPrefix);
-
- SuggestionStore.addSuggestions(suggestionId, publicChannelNames, publicChannels, SearchChannelSuggestion);
- SuggestionStore.addSuggestions(suggestionId, privateChannelNames, privateChannels, SearchChannelSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/search_suggestion_list.jsx b/web/react/components/suggestion/search_suggestion_list.jsx
deleted file mode 100644
index 60a5562fa..000000000
--- a/web/react/components/suggestion/search_suggestion_list.jsx
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import SuggestionList from './suggestion_list.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SearchSuggestionList extends SuggestionList {
- componentDidUpdate(prevProps, prevState) {
- if (this.state.items.length > 0 && prevState.items.length === 0) {
- this.getContent().perfectScrollbar();
- }
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.popover)).find('.popover-content');
- }
-
- renderChannelDivider(type) {
- let text;
- if (type === Constants.OPEN_CHANNEL) {
- text = (
- <FormattedMessage
- id='suggestion.search.public'
- defaultMessage='Public Channels'
- />
- );
- } else {
- text = (
- <FormattedMessage
- id='suggestion.search.private'
- defaultMessage='Private Groups'
- />
- );
- }
-
- return (
- <div
- key={type + '-divider'}
- className='search-autocomplete__divider'
- >
- <span>{text}</span>
- </div>
- );
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- // temporary hack to add dividers between public and private channels in the search suggestion list
- if (i === 0 || item.type !== this.state.items[i - 1].type) {
- if (item.type === Constants.OPEN_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.OPEN_CHANNEL));
- } else if (item.type === Constants.PRIVATE_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.PRIVATE_CHANNEL));
- }
- }
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <ReactBootstrap.Popover
- ref='popover'
- id='search-autocomplete__popover'
- className='search-help-popover autocomplete visible'
- placement='bottom'
- >
- {items}
- </ReactBootstrap.Popover>
- );
- }
-}
-
-SearchSuggestionList.propTypes = {
- ...SuggestionList.propTypes
-};
diff --git a/web/react/components/suggestion/search_user_provider.jsx b/web/react/components/suggestion/search_user_provider.jsx
deleted file mode 100644
index 0d553bfc4..000000000
--- a/web/react/components/suggestion/search_user_provider.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-
-class SearchUserSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <img
- className='profile-img rounded'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- <i className='fa fa fa-plus-square'></i>{item.username}
- </div>
- );
- }
-}
-
-SearchUserSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchUserProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getProfiles();
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix)) {
- filtered.push(user);
- }
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const usernames = filtered.map((user) => user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, usernamePrefix);
- SuggestionStore.addSuggestions(suggestionId, usernames, filtered, SearchUserSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/suggestion_box.jsx b/web/react/components/suggestion/suggestion_box.jsx
deleted file mode 100644
index 12b098cbd..000000000
--- a/web/react/components/suggestion/suggestion_box.jsx
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-const KeyCodes = Constants.KeyCodes;
-
-export default class SuggestionBox extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDocumentClick = this.handleDocumentClick.bind(this);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleCompleteWord = this.handleCompleteWord.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handlePretextChanged = this.handlePretextChanged.bind(this);
-
- this.suggestionId = Utils.generateId();
- }
-
- componentDidMount() {
- SuggestionStore.registerSuggestionBox(this.suggestionId);
- $(document).on('click', this.handleDocumentClick);
-
- SuggestionStore.addCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
-
- SuggestionStore.unregisterSuggestionBox(this.suggestionId);
- $(document).off('click', this.handleDocumentClick);
- }
-
- getTextbox() {
- // this is to support old code that looks at the input/textarea DOM nodes
- return ReactDOM.findDOMNode(this.refs.textbox);
- }
-
- handleDocumentClick(e) {
- const container = $(ReactDOM.findDOMNode(this));
- if (!(container.is(e.target) || container.has(e.target).length > 0)) {
- // we can't just use blur for this because it fires and hides the children before
- // their click handlers can be called
- GlobalActions.emitClearSuggestions(this.suggestionId);
- }
- }
-
- handleChange(e) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
- const pretext = textbox.value.substring(0, caret);
-
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(textbox.value);
- }
-
- if (this.props.onChange) {
- this.props.onChange(e);
- }
- }
-
- handleCompleteWord(term) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
-
- const text = this.props.value;
- const prefix = text.substring(0, caret - SuggestionStore.getMatchedPretext(this.suggestionId).length);
- const suffix = text.substring(caret);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(prefix + term + ' ' + suffix);
- }
-
- // set the caret position after the next rendering
- window.requestAnimationFrame(() => {
- Utils.setCaretPosition(textbox, prefix.length + term.length + 1);
- });
- }
-
- handleKeyDown(e) {
- if (SuggestionStore.hasSuggestions(this.suggestionId)) {
- if (e.which === KeyCodes.UP) {
- GlobalActions.emitSelectPreviousSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.DOWN) {
- GlobalActions.emitSelectNextSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) {
- GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- handlePretextChanged(pretext) {
- for (const provider of this.props.providers) {
- provider.handlePretextChanged(this.suggestionId, pretext);
- }
- }
-
- render() {
- const newProps = Object.assign({}, this.props, {
- onChange: this.handleChange,
- onKeyDown: this.handleKeyDown
- });
-
- let textbox = null;
- if (this.props.type === 'input') {
- textbox = (
- <input
- ref='textbox'
- type='text'
- {...newProps}
- />
- );
- } else if (this.props.type === 'textarea') {
- textbox = (
- <textarea
- ref='textbox'
- {...newProps}
- />
- );
- }
-
- const SuggestionListComponent = this.props.listComponent;
-
- return (
- <div>
- {textbox}
- <SuggestionListComponent suggestionId={this.suggestionId}/>
- </div>
- );
- }
-}
-
-SuggestionBox.defaultProps = {
- type: 'input'
-};
-
-SuggestionBox.propTypes = {
- listComponent: React.PropTypes.func.isRequired,
- type: React.PropTypes.oneOf(['input', 'textarea']).isRequired,
- value: React.PropTypes.string.isRequired,
- onUserInput: React.PropTypes.func,
- providers: React.PropTypes.arrayOf(React.PropTypes.object),
-
- // explicitly name any input event handlers we override and need to manually call
- onChange: React.PropTypes.func,
- onKeyDown: React.PropTypes.func
-};
diff --git a/web/react/components/suggestion/suggestion_list.jsx b/web/react/components/suggestion/suggestion_list.jsx
deleted file mode 100644
index 0f5907179..000000000
--- a/web/react/components/suggestion/suggestion_list.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-export default class SuggestionList extends React.Component {
- constructor(props) {
- super(props);
-
- this.getContent = this.getContent.bind(this);
-
- this.handleItemClick = this.handleItemClick.bind(this);
- this.handleSuggestionsChanged = this.handleSuggestionsChanged.bind(this);
-
- this.scrollToItem = this.scrollToItem.bind(this);
-
- this.state = {
- items: [],
- terms: [],
- components: [],
- selection: ''
- };
- }
-
- componentDidMount() {
- SuggestionStore.addSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.content));
- }
-
- handleItemClick(term, e) {
- GlobalActions.emitCompleteWordSuggestion(this.props.suggestionId, term);
-
- e.preventDefault();
- }
-
- handleSuggestionsChanged() {
- const selection = SuggestionStore.getSelection(this.props.suggestionId);
-
- this.setState({
- items: SuggestionStore.getItems(this.props.suggestionId),
- terms: SuggestionStore.getTerms(this.props.suggestionId),
- components: SuggestionStore.getComponents(this.props.suggestionId),
- selection
- });
-
- if (selection) {
- window.requestAnimationFrame(() => this.scrollToItem(this.state.selection));
- }
- }
-
- scrollToItem(term) {
- const content = this.getContent();
- const visibleContentHeight = content[0].clientHeight;
- const actualContentHeight = content[0].scrollHeight;
-
- if (visibleContentHeight < actualContentHeight) {
- const contentTop = content.scrollTop();
- const contentTopPadding = parseInt(content.css('padding-top'), 10);
- const contentBottomPadding = parseInt(content.css('padding-top'), 10);
-
- const item = $(ReactDOM.findDOMNode(this.refs[term]));
- const itemTop = item[0].offsetTop - parseInt(item.css('margin-top'), 10);
- const itemBottomMargin = parseInt(item.css('margin-bottom'), 10) + parseInt(item.css('padding-bottom'), 10);
- const itemBottom = item[0].offsetTop + item.height() + itemBottomMargin;
-
- if (itemTop - contentTopPadding < contentTop) {
- // the item is off the top of the visible space
- content.scrollTop(itemTop - contentTopPadding);
- } else if (itemBottom + contentTopPadding + contentBottomPadding > contentTop + visibleContentHeight) {
- // the item has gone off the bottom of the visible space
- content.scrollTop(itemBottom - visibleContentHeight + contentTopPadding + contentBottomPadding);
- }
- }
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- term={term}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <div className='suggestion-list suggestion-list--top'>
- <div
- ref='content'
- className='suggestion-list__content suggestion-list__content--top'
- >
- {items}
- </div>
- </div>
- );
- }
-}
-
-SuggestionList.propTypes = {
- suggestionId: React.PropTypes.string.isRequired
-};
diff --git a/web/react/components/team_export_tab.jsx b/web/react/components/team_export_tab.jsx
deleted file mode 100644
index 12743d9e3..000000000
--- a/web/react/components/team_export_tab.jsx
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../utils/client.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamExportTab extends React.Component {
- constructor(props) {
- super(props);
- this.state = {status: 'request', link: '', err: ''};
-
- this.onExportSuccess = this.onExportSuccess.bind(this);
- this.onExportFailure = this.onExportFailure.bind(this);
- this.doExport = this.doExport.bind(this);
- }
- onExportSuccess(data) {
- this.setState({status: 'ready', link: data.link, err: ''});
- }
- onExportFailure(e) {
- this.setState({status: 'failure', link: '', err: e.message});
- }
- doExport() {
- if (this.state.status === 'in-progress') {
- return;
- }
- this.setState({status: 'in-progress'});
- Client.exportTeam(this.onExportSuccess, this.onExportFailure);
- }
- render() {
- var messageSection = '';
- switch (this.state.status) {
- case 'request':
- messageSection = '';
- break;
- case 'in-progress':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-spinner fa-pulse'/>
- <FormattedMessage
- id='team_export_tab.exporting'
- defaultMessage=' Exporting...'
- />
- </p>
- );
- break;
- case 'ready':
- messageSection = (
- <p className='confirm-import alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='team_export_tab.ready'
- defaultMessage=' Ready for '
- />
- <a
- href={this.state.link}
- download={true}
- >
- <FormattedMessage
- id='team_export_tab.download'
- defaultMessage='download'
- />
- </a>
- </p>
- );
- break;
- case 'failure':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-warning'/>
- <FormattedMessage
- id='team_export_tab.unable'
- defaultMessage=' Unable to export: {error}'
- values={{
- error: this.state.err
- }}
- />
- </p>
- );
- break;
- }
-
- return (
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='team_export_tab.export'
- defaultMessage='Export'
- />
- </h3>
- <div className='divider-dark first'/>
- <ul className='section-max'>
- <li className='col-xs-12 section-title'>
- <FormattedMessage
- id='team_export_tab.exportTeam'
- defaultMessage='Export your team'
- />
- </li>
- <li className='col-xs-offset-3 col-xs-8'>
- <ul className='setting-list'>
- <li className='setting-list-item'>
- <a
- className='btn btn-sm btn-primary btn-file sel-btn'
- href='#'
- onClick={this.doExport}
- >
- <FormattedMessage
- id='team_export_tab.export'
- defaultMessage='Export'
- />
- </a>
- </li>
- </ul>
- </li>
- </ul>
- <div className='divider-dark'/>
- {messageSection}
- </div>
- );
- }
-}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
deleted file mode 100644
index ef7410b2f..000000000
--- a/web/react/components/team_general_tab.jsx
+++ /dev/null
@@ -1,655 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from './setting_item_min.jsx';
-import SettingItemMax from './setting_item_max.jsx';
-
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- dirDisabled: {
- id: 'general_tab.dirDisabled',
- defaultMessage: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'
- },
- required: {
- id: 'general_tab.required',
- defaultMessage: 'This field is required'
- },
- chooseName: {
- id: 'general_tab.chooseName',
- defaultMessage: 'Please choose a new name for your team'
- },
- includeDirTitle: {
- id: 'general_tab.includeDirTitle',
- defaultMessage: 'Include this team in the Team Directory'
- },
- yes: {
- id: 'general_tab.yes',
- defaultMessage: 'Yes'
- },
- no: {
- id: 'general_tab.no',
- defaultMessage: 'No'
- },
- dirOff: {
- id: 'general_tab.dirOff',
- defaultMessage: 'Team directory is turned off for this system.'
- },
- openInviteTitle: {
- id: 'general_tab.openInviteTitle',
- defaultMessage: 'Allow anyone to sign-up from login page'
- },
- codeTitle: {
- id: 'general_tab.codeTitle',
- defaultMessage: 'Invite Code'
- },
- codeDesc: {
- id: 'general_tab.codeDesc',
- defaultMessage: "Click 'Edit' to regenerate Invite Code."
- },
- teamNameInfo: {
- id: 'general_tab.teamNameInfo',
- defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
- }
-});
-
-class GeneralTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
- this.handleNameSubmit = this.handleNameSubmit.bind(this);
- this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
- this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
- this.handleTeamListingSubmit = this.handleTeamListingSubmit.bind(this);
- this.handleClose = this.handleClose.bind(this);
- this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
- this.updateName = this.updateName.bind(this);
- this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this);
- this.updateInviteId = this.updateInviteId.bind(this);
- this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
- this.handleOpenInviteRadio = this.handleOpenInviteRadio.bind(this);
- this.onUpdateTeamListingSection = this.onUpdateTeamListingSection.bind(this);
- this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
- this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
-
- this.state = this.setupInitialState(props);
- }
-
- updateSection(section) {
- this.setState(this.setupInitialState(this.props));
- this.props.updateSection(section);
- }
-
- setupInitialState(props) {
- const team = props.team;
-
- return {
- name: team.display_name,
- invite_id: team.invite_id,
- allow_open_invite: team.allow_open_invite,
- allow_team_listing: team.allow_team_listing,
- serverError: '',
- clientError: ''
- };
- }
-
- handleGenerateInviteId(e) {
- e.preventDefault();
-
- var newId = '';
- for (var i = 0; i < 32; i++) {
- newId += Math.floor(Math.random() * 16).toString(16);
- }
-
- this.setState({invite_id: newId});
- }
-
- handleOpenInviteRadio(openInvite) {
- this.setState({allow_open_invite: openInvite});
- }
-
- handleTeamListingRadio(listing) {
- if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
- } else {
- this.setState({allow_team_listing: listing});
- }
- }
-
- handleOpenInviteSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
-
- var data = this.props.team;
- data.allow_open_invite = this.state.allow_open_invite;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleTeamListingSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
-
- var data = this.props.team;
- data.allow_team_listing = this.state.allow_team_listing;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleNameSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
- let valid = true;
-
- const {formatMessage} = this.props.intl;
- const name = this.state.name.trim();
- if (!name) {
- state.clientError = formatMessage(holders.required);
- valid = false;
- } else if (name === this.props.team.display_name) {
- state.clientError = formatMessage(holders.chooseName);
- valid = false;
- } else {
- state.clientError = '';
- }
-
- this.setState(state);
-
- if (!valid) {
- return;
- }
-
- var data = this.props.team;
- data.display_name = this.state.name;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- handleInviteIdSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
- let valid = true;
-
- const inviteId = this.state.invite_id.trim();
- if (inviteId) {
- state.clientError = '';
- } else {
- state.clientError = this.props.intl.fromatMessage(holders.required);
- valid = false;
- }
-
- this.setState(state);
-
- if (!valid) {
- return;
- }
-
- var data = this.props.team;
- data.invite_id = this.state.invite_id;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
- componentWillReceiveProps(newProps) {
- if (newProps.team && newProps.teamDisplayName) {
- this.setState({name: newProps.teamDisplayName});
- }
- }
-
- handleClose() {
- this.updateSection('');
- }
-
- componentDidMount() {
- $('#team_settings').on('hidden.bs.modal', this.handleClose);
- }
-
- componentWillUnmount() {
- $('#team_settings').off('hidden.bs.modal', this.handleClose);
- }
-
- onUpdateNameSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'name') {
- this.updateSection('');
- } else {
- this.updateSection('name');
- }
- }
-
- onUpdateInviteIdSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'invite_id') {
- this.updateSection('');
- } else {
- this.updateSection('invite_id');
- }
- }
-
- onUpdateOpenInviteSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'open_invite') {
- this.updateSection('');
- } else {
- this.updateSection('open_invite');
- }
- }
-
- onUpdateTeamListingSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'team_listing') {
- this.updateSection('');
- } else {
- this.updateSection('team_listing');
- }
- }
-
- updateName(e) {
- e.preventDefault();
- this.setState({name: e.target.value});
- }
-
- updateInviteId(e) {
- e.preventDefault();
- this.setState({invite_id: e.target.value});
- }
-
- render() {
- let clientError = null;
- let serverError = null;
- if (this.state.clientError) {
- clientError = this.state.clientError;
- }
- if (this.state.serverError) {
- serverError = this.state.serverError;
- }
-
- const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
- const {formatMessage} = this.props.intl;
-
- let teamListingSection;
- if (this.props.activeSection === 'team_listing') {
- const inputs = [];
- let submitHandle = null;
-
- if (enableTeamListing) {
- submitHandle = this.handleTeamListingSubmit;
-
- inputs.push(
- <div key='userTeamListingOptions'>
- <div className='radio'>
- <label>
- <input
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, true)}
- />
- <FormattedMessage
- id='general_tab.yes'
- defaultMessage='Yes'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- ref='teamListingRadioNo'
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={!this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, false)}
- />
- <FormattedMessage
- id='general_tab.no'
- defaultMessage='No'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.includeDirDesc'
- defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
- />
- </div>
- </div>
- );
- } else {
- inputs.push(
- <div key='userTeamListingOptions'>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.dirContact'
- defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
- />
- </div>
- </div>
- );
- }
-
- teamListingSection = (
- <SettingItemMax
- title={formatMessage(holders.includeDirTitle)}
- inputs={inputs}
- submit={submitHandle}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- } else {
- let describe = '';
-
- if (enableTeamListing) {
- if (this.state.allow_team_listing === true) {
- describe = formatMessage(holders.yes);
- } else {
- describe = formatMessage(holders.no);
- }
- } else {
- describe = formatMessage(holders.dirOff);
- }
-
- teamListingSection = (
- <SettingItemMin
- title={formatMessage(holders.includeDirTitle)}
- describe={describe}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- }
-
- let openInviteSection;
- if (this.props.activeSection === 'open_invite') {
- const inputs = [
- <div key='userOpenInviteOptions'>
- <div className='radio'>
- <label>
- <input
- name='userOpenInviteOptions'
- type='radio'
- defaultChecked={this.state.allow_open_invite}
- onChange={this.handleOpenInviteRadio.bind(this, true)}
- />
- <FormattedMessage
- id='general_tab.yes'
- defaultMessage='Yes'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- name='userOpenInviteOptions'
- type='radio'
- defaultChecked={!this.state.allow_open_invite}
- onChange={this.handleOpenInviteRadio.bind(this, false)}
- />
- <FormattedMessage
- id='general_tab.no'
- defaultMessage='No'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.openInviteDesc'
- defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
- />
- </div>
- </div>
- ];
-
- openInviteSection = (
- <SettingItemMax
- title={formatMessage(holders.openInviteTitle)}
- inputs={inputs}
- submit={this.handleOpenInviteSubmit}
- server_error={serverError}
- updateSection={this.onUpdateOpenInviteSection}
- />
- );
- } else {
- let describe = '';
- if (this.state.allow_open_invite === true) {
- describe = formatMessage(holders.yes);
- } else {
- describe = formatMessage(holders.no);
- }
-
- openInviteSection = (
- <SettingItemMin
- title={formatMessage(holders.openInviteTitle)}
- describe={describe}
- updateSection={this.onUpdateOpenInviteSection}
- />
- );
- }
-
- let inviteSection;
-
- if (this.props.activeSection === 'invite_id') {
- const inputs = [];
-
- inputs.push(
- <div key='teamInviteSetting'>
- <div className='row'>
- <label className='col-sm-5 control-label visible-xs-block'>
- </label>
- <div className='col-sm-12'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateInviteId}
- value={this.state.invite_id}
- maxLength='32'
- />
- <div className='padding-top x2'>
- <a
- href='#'
- onClick={this.handleGenerateInviteId}
- >
- <FormattedMessage
- id='general_tab.regenerate'
- defaultMessage='Re-Generate'
- />
- </a>
- </div>
- </div>
- </div>
- <div className='setting-list__hint'>
- <FormattedMessage
- id='general_tab.codeLongDesc'
- defaultMessage='The Invite Code is used as part of the URL in the team invitation link created by **Get Team Invite Link** in the main menu. Regenerating creates a new team invitation link and invalidates the previous link.'
- />
- </div>
- </div>
- );
-
- inviteSection = (
- <SettingItemMax
- title={formatMessage(holders.codeTitle)}
- inputs={inputs}
- submit={this.handleInviteIdSubmit}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateInviteIdSection}
- />
- );
- } else {
- inviteSection = (
- <SettingItemMin
- title={formatMessage(holders.codeTitle)}
- describe={formatMessage(holders.codeDesc)}
- updateSection={this.onUpdateInviteIdSection}
- />
- );
- }
-
- let nameSection;
-
- if (this.props.activeSection === 'name') {
- const inputs = [];
-
- let teamNameLabel = (
- <FormattedMessage
- id='general_tab.teamName'
- defaultMessage='Team Name'
- />
- );
- if (Utils.isMobile()) {
- teamNameLabel = '';
- }
-
- inputs.push(
- <div
- key='teamNameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{teamNameLabel}</label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- maxLength='22'
- onChange={this.updateName}
- value={this.state.name}
- />
- </div>
- </div>
- );
-
- const nameExtraInfo = <span>{formatMessage(holders.teamNameInfo)}</span>;
-
- nameSection = (
- <SettingItemMax
- title={formatMessage({id: 'general_tab.teamName'})}
- inputs={inputs}
- submit={this.handleNameSubmit}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateNameSection}
- extraInfo={nameExtraInfo}
- />
- );
- } else {
- var describe = this.state.name;
-
- nameSection = (
- <SettingItemMin
- title={formatMessage({id: 'general_tab.teamName'})}
- describe={describe}
- updateSection={this.onUpdateNameSection}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i className='fa fa-angle-left'></i>
- </div>
- <FormattedMessage
- id='general_tab.title'
- defaultMessage='General Settings'
- />
- </h4>
- </div>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='general_tab.title'
- defaultMessage='General Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {nameSection}
- <div className='divider-light'/>
- {openInviteSection}
- <div className='divider-light'/>
- {teamListingSection}
- <div className='divider-light'/>
- {inviteSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-GeneralTab.propTypes = {
- intl: intlShape.isRequired,
- updateSection: React.PropTypes.func.isRequired,
- team: React.PropTypes.object.isRequired,
- activeSection: React.PropTypes.string.isRequired
-};
-
-export default injectIntl(GeneralTab); \ No newline at end of file
diff --git a/web/react/components/team_import_tab.jsx b/web/react/components/team_import_tab.jsx
deleted file mode 100644
index c9a8afe6c..000000000
--- a/web/react/components/team_import_tab.jsx
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import SettingUpload from './setting_upload.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- importSlack: {
- id: 'team_import_tab.importSlack',
- defaultMessage: 'Import from Slack (Beta)'
- }
-});
-
-class TeamImportTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.onImportFailure = this.onImportFailure.bind(this);
- this.onImportSuccess = this.onImportSuccess.bind(this);
- this.doImportSlack = this.doImportSlack.bind(this);
-
- this.state = {
- status: 'ready',
- link: ''
- };
- }
-
- onImportFailure() {
- this.setState({status: 'fail', link: ''});
- }
-
- onImportSuccess(data) {
- this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(data)});
- }
-
- doImportSlack(file) {
- this.setState({status: 'in-progress', link: ''});
- utils.importSlack(file, this.onImportSuccess, this.onImportFailure);
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var uploadHelpText = (
- <div>
- <FormattedHTMLMessage
- id='team_import_tab.importHelp'
- defaultMessage="<p>To import a team from Slack go to Slack > Team Settings > Import/Export Data > Export > Start Export. Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.</p><p>The Slack import to Mattermost is in 'Beta'. Slack bot posts do not yet import and Slack @mentions are not currently supported.</p>"
- />
- </div>
- );
-
- var uploadSection = (
- <SettingUpload
- title={formatMessage(holders.importSlack)}
- submit={this.doImportSlack}
- helpText={uploadHelpText}
- fileTypesAccepted='.zip'
- />
- );
-
- var messageSection;
- switch (this.state.status) {
-
- case 'ready':
- messageSection = '';
- break;
- case 'in-progress':
- messageSection = (
- <p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>
- <FormattedMessage
- id='team_import_tab.importing'
- defaultMessage=' Importing...'
- />
- </p>
- );
- break;
- case 'done':
- messageSection = (
- <p className='confirm-import alert alert-success'>
- <i className='fa fa-check'/>
- <FormattedMessage
- id='team_import_tab.successful'
- defaultMessage=' Import successful: '
- />
- <a
- href={this.state.link}
- download='MattermostImportSummary.txt'
- >
- <FormattedMessage
- id='team_import_tab.summary'
- defaultMessage='View Summary'
- />
- </a>
- </p>
- );
- break;
- case 'fail':
- messageSection = (
- <p className='confirm-import alert alert-warning'>
- <i className='fa fa-warning'/>
- <FormattedMessage
- id='team_import_tab.failure'
- defaultMessage=' Import failure: '
- />
- <a
- href={this.state.link}
- download='MattermostImportSummary.txt'
- >
- <FormattedMessage
- id='team_import_tab.summary'
- defaultMessage='View Summary'
- />
- </a>
- </p>
- );
- break;
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i className='fa fa-angle-left'></i>
- </div>
- <FormattedMessage
- id='team_import_tab.import'
- defaultMessage='Import'
- />
- </h4>
- </div>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='team_import_tab.import'
- defaultMessage='Import'
- />
- </h3>
- <div className='divider-dark first'/>
- {uploadSection}
- <div className='divider-dark'/>
- {messageSection}
- </div>
- </div>
- );
- }
-}
-
-TeamImportTab.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(TeamImportTab); \ No newline at end of file
diff --git a/web/react/components/team_members_dropdown.jsx b/web/react/components/team_members_dropdown.jsx
deleted file mode 100644
index cd7585d94..000000000
--- a/web/react/components/team_members_dropdown.jsx
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import ConfirmModal from './confirm_modal.jsx';
-import TeamStore from '../stores/team_store.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class TeamMembersDropdown extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleMakeMember = this.handleMakeMember.bind(this);
- this.handleMakeActive = this.handleMakeActive.bind(this);
- this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
- this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
- this.handleDemote = this.handleDemote.bind(this);
- this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
- this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
-
- this.state = {
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- };
- }
- handleMakeMember() {
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, '');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
- Client.updateRoles(data,
- () => {
- AsyncClient.getProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
- handleMakeActive() {
- Client.updateActive(this.props.user.id, true,
- () => {
- AsyncClient.getProfiles();
- AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleMakeNotActive() {
- Client.updateActive(this.props.user.id, false,
- () => {
- AsyncClient.getProfiles();
- AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleMakeAdmin() {
- const me = UserStore.getCurrentUser();
- if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
- } else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
- () => {
- AsyncClient.getProfiles();
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- }
- handleDemote(user, role) {
- this.setState({
- serverError: this.state.serverError,
- showDemoteModal: true,
- user,
- role
- });
- }
- handleDemoteCancel() {
- this.setState({
- serverError: null,
- showDemoteModal: false,
- user: null,
- role: null
- });
- }
- handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
- () => {
- const teamUrl = TeamStore.getCurrentTeamUrl();
- if (teamUrl) {
- window.location.href = teamUrl;
- } else {
- window.location.href = '/';
- }
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='has-error'>
- <label className='has-error control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- const user = this.props.user;
- let currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.member'
- defaultMessage='Member'
- />
- );
-
- if (user.roles.length > 0) {
- if (Utils.isSystemAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.systemAdmin'
- defaultMessage='System Admin'
- />
- );
- } else if (Utils.isAdmin(user.roles)) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.teamAdmin'
- defaultMessage='Team Admin'
- />
- );
- } else {
- currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
- }
- }
-
- let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
- let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
-
- if (user.delete_at > 0) {
- currentRoles = (
- <FormattedMessage
- id='team_members_dropdown.inactive'
- defaultMessage='Inactive'
- />
- );
- showMakeMember = false;
- showMakeAdmin = false;
- showMakeActive = true;
- showMakeNotActive = false;
- }
-
- let makeAdmin = null;
- if (showMakeAdmin) {
- makeAdmin = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeAdmin}
- >
- <FormattedMessage
- id='team_members_dropdown.makeAdmin'
- defaultMessage='Make Team Admin'
- />
- </a>
- </li>
- );
- }
-
- let makeMember = null;
- if (showMakeMember) {
- makeMember = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeMember}
- >
- <FormattedMessage
- id='team_members_dropdown.makeMember'
- defaultMessage='Make Member'
- />
- </a>
- </li>
- );
- }
-
- let makeActive = null;
- if (showMakeActive) {
- makeActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeActive}
- >
- <FormattedMessage
- id='team_members_dropdown.makeActive'
- defaultMessage='Make Active'
- />
- </a>
- </li>
- );
- }
-
- let makeNotActive = null;
- if (showMakeNotActive) {
- makeNotActive = (
- <li role='presentation'>
- <a
- role='menuitem'
- href='#'
- onClick={this.handleMakeNotActive}
- >
- <FormattedMessage
- id='team_members_dropdown.makeInactive'
- defaultMessage='Make Inactive'
- />
- </a>
- </li>
- );
- }
- const me = UserStore.getCurrentUser();
- let makeDemoteModal = null;
- if (this.props.user.id === me.id) {
- const title = (
- <FormattedMessage
- id='team_members_dropdown.confirmDemoteRoleTitle'
- defaultMessage='Confirm demotion from System Admin role'
- />
- );
-
- const message = (
- <div>
- <FormattedMessage
- id='team_members_dropdown.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."
- />
- <br/>
- <br/>
- <FormattedMessage
- id='team_members_dropdown.confirmDemotionCmd'
- defaultMessage='platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"'
- />
- {serverError}
- </div>
- );
-
- const confirmButton = (
- <FormattedMessage
- id='team_members_dropdown.confirmDemotion'
- defaultMessage='Confirm Demotion'
- />
- );
-
- makeDemoteModal = (
- <ConfirmModal
- show={this.state.showDemoteModal}
- title={title}
- message={message}
- confirmButton={confirmButton}
- onConfirm={this.handleDemoteSubmit}
- onCancel={this.handleDemoteCancel}
- />
- );
- }
-
- return (
- <div className='dropdown member-drop'>
- <a
- href='#'
- className='dropdown-toggle theme'
- type='button'
- data-toggle='dropdown'
- aria-expanded='true'
- >
- <span>{currentRoles} </span>
- <span className='fa fa-chevron-down'></span>
- </a>
- <ul
- className='dropdown-menu member-menu'
- role='menu'
- >
- {makeAdmin}
- {makeMember}
- {makeActive}
- {makeNotActive}
- </ul>
- {makeDemoteModal}
- {serverError}
- </div>
- );
- }
-}
-
-TeamMembersDropdown.propTypes = {
- user: React.PropTypes.object.isRequired
-};
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
deleted file mode 100644
index 786e8f947..000000000
--- a/web/react/components/team_members_modal.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import MemberListTeam from './member_list_team.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-
-export default class TeamMembersModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.teamChanged = this.teamChanged.bind(this);
-
- this.state = {
- team: TeamStore.getCurrent()
- };
- }
- componentDidMount() {
- if (this.props.show) {
- this.onShow();
- }
-
- TeamStore.addChangeListener(this.teamChanged);
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.teamChanged);
- }
-
- teamChanged() {
- this.setState({team: TeamStore.getCurrent()});
- }
-
- render() {
- let teamDisplayName = '';
- if (this.state.team) {
- teamDisplayName = this.state.team.display_name;
- }
-
- let maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
- return (
- <Modal
- dialogClassName='more-modal'
- show={this.props.show}
- onHide={this.props.onHide}
- >
- <Modal.Header closeButton={true}>
- <FormattedMessage
- id='team_member_modal.members'
- defaultMessage='{team} Members'
- values={{
- team: teamDisplayName
- }}
- />
- </Modal.Header>
- <Modal.Body>
- <MemberListTeam style={{maxHeight}}/>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={this.props.onHide}
- >
- <FormattedMessage
- id='team_member_modal.close'
- defaultMessage='Close'
- />
- </button>
- </Modal.Footer>
- </Modal>
- );
- }
-}
-
-TeamMembersModal.propTypes = {
- show: React.PropTypes.bool.isRequired,
- onHide: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx
deleted file mode 100644
index 0eb9d1211..000000000
--- a/web/react/components/team_settings.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import TeamStore from '../stores/team_store.jsx';
-import ImportTab from './team_import_tab.jsx';
-import ExportTab from './team_export_tab.jsx';
-import GeneralTab from './team_general_tab.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default class TeamSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {team: TeamStore.getCurrent()};
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onChange);
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onChange);
- }
- onChange() {
- var team = TeamStore.getCurrent();
- if (!Utils.areObjectsEqual(this.state.team, team)) {
- this.setState({team});
- }
- }
- render() {
- if (!this.state.team) {
- return null;
- }
- var result;
- switch (this.props.activeTab) {
- case 'general':
- result = (
- <div>
- <GeneralTab
- team={this.state.team}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- />
- </div>
- );
- break;
- case 'import':
- result = (
- <div>
- <ImportTab
- team={this.state.team}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- />
- </div>
- );
- break;
- case 'export':
- result = (
- <div>
- <ExportTab/>
- </div>
- );
- break;
- default:
- result = (
- <div/>
- );
- break;
- }
- return result;
- }
-}
-
-TeamSettings.defaultProps = {
- activeTab: '',
- activeSection: ''
-};
-
-TeamSettings.propTypes = {
- activeTab: React.PropTypes.string.isRequired,
- activeSection: React.PropTypes.string.isRequired,
- updateSection: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
deleted file mode 100644
index bef3ebf24..000000000
--- a/web/react/components/team_settings_modal.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingsSidebar from './settings_sidebar.jsx';
-import TeamSettings from './team_settings.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- generalTab: {
- id: 'team_settings_modal.generalTab',
- defaultMessage: 'General'
- },
- importTab: {
- id: 'team_settings_modal.importTab',
- defaultMessage: 'Import'
- },
- exportTab: {
- id: 'team_settings_modal.exportTab',
- defaultMessage: 'Export'
- }
-});
-
-class TeamSettingsModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateTab = this.updateTab.bind(this);
- this.updateSection = this.updateSection.bind(this);
-
- this.state = {
- activeTab: 'general',
- activeSection: ''
- };
- }
- componentDidMount() {
- const modal = $(ReactDOM.findDOMNode(this.refs.modal));
-
- modal.on('click', '.modal-back', function handleBackClick() {
- $(this).closest('.modal-dialog').removeClass('display--content');
- $(this).closest('.modal-dialog').find('.settings-table .nav li.active').removeClass('active');
- });
- modal.on('click', '.modal-header .close', () => {
- setTimeout(() => {
- $('.modal-dialog.display--content').removeClass('display--content');
- }, 500);
- });
- }
- updateTab(tab) {
- this.setState({activeTab: tab, activeSection: ''});
- }
- updateSection(section) {
- this.setState({activeSection: section});
- }
- render() {
- const {formatMessage} = this.props.intl;
- const tabs = [];
- tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'glyphicon glyphicon-cog'});
- tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'glyphicon glyphicon-upload'});
-
- // To enable export uncomment this line
- //tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'glyphicon glyphicon-download'});
-
- return (
- <div
- className='modal fade'
- ref='modal'
- id='team_settings'
- role='dialog'
- tabIndex='-1'
- aria-hidden='true'
- >
- <div className='modal-dialog settings-modal'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>&times;</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <FormattedMessage
- id='team_settings_modal.title'
- defaultMessage='Team Settings'
- />
- </h4>
- </div>
- <div className='modal-body settings-modal__body'>
- <div className='settings-table'>
- <div className='settings-links'>
- <SettingsSidebar
- tabs={tabs}
- activeTab={this.state.activeTab}
- updateTab={this.updateTab}
- />
- </div>
- <div className='settings-content minimize-settings'>
- <TeamSettings
- activeTab={this.state.activeTab}
- activeSection={this.state.activeSection}
- updateSection={this.updateSection}
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamSettingsModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(TeamSettingsModal); \ No newline at end of file
diff --git a/web/react/components/team_signup_choose_auth.jsx b/web/react/components/team_signup_choose_auth.jsx
deleted file mode 100644
index 5db83fe7d..000000000
--- a/web/react/components/team_signup_choose_auth.jsx
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ChooseAuthPage extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- var buttons = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={
- function clickGit(e) {
- e.preventDefault();
- this.props.updatePage('gitlab');
- }.bind(this)
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.gitlabCreate'
- defaultMessage='Create new team with GitLab Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('google');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.googleCreate'
- defaultMessage='Create new team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('ldap');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.ldapCreate'
- defaultMessage='Create new team with LDAP Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login email btn-full'
- key='email'
- href='#'
- onClick={
- function clickEmail(e) {
- e.preventDefault();
- this.props.updatePage('email');
- }.bind(this)
- }
- >
- <span className='fa fa-envelope'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.emailCreate'
- defaultMessage='Create new team with email address'
- />
- </span>
- </a>
- );
- }
-
- if (buttons.length === 0) {
- buttons = (
- <span>
- <FormattedMessage
- id='choose_auth_page.noSignup'
- defaultMessage='No sign-up methods configured, please contact your system administrator.'
- />
- </span>
- );
- }
-
- return (
- <div>
- {buttons}
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='choose_auth_page.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </div>
- );
- }
-}
-
-ChooseAuthPage.propTypes = {
- updatePage: React.PropTypes.func
-};
diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx
deleted file mode 100644
index a81b22d90..000000000
--- a/web/react/components/team_signup_with_email.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- emailError: {
- id: 'email_signup.emailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'email_signup.address',
- defaultMessage: 'Email Address'
- }
-});
-
-class EmailSignUpPage extends React.Component {
- constructor() {
- super();
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {};
- }
- handleSubmit(e) {
- e.preventDefault();
- const team = {};
- const state = {serverError: null};
- let isValid = true;
-
- team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!team.email || !Utils.isEmail(team.email)) {
- state.emailError = this.props.intl.formatMessage(holders.emailError);
- isValid = false;
- } else {
- state.emailError = null;
- }
-
- if (!isValid) {
- this.setState(state);
- return;
- }
-
- Client.signupTeam(team.email,
- (data) => {
- if (data.follow_link) {
- browserHistory.push(data.follow_link);
- } else {
- browserHistory.push(`/signup_team_confirm/?email=${encodeURIComponent(team.email)}`);
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- let emailError = null;
- if (this.state.emailError) {
- emailError = <div className='form-group has-error'><label className='control-label'>{this.state.emailError}</label></div>;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className='form-group'>
- <input
- autoFocus={true}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- <div className='form-group'>
- <button
- className='btn btn-md btn-primary'
- type='submit'
- >
- <FormattedMessage
- id='email_signup.createTeam'
- defaultMessage='Create Team'
- />
- </button>
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='email_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-EmailSignUpPage.defaultProps = {
-};
-EmailSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(EmailSignUpPage);
diff --git a/web/react/components/team_signup_with_ldap.jsx b/web/react/components/team_signup_with_ldap.jsx
deleted file mode 100644
index 8c808fa88..000000000
--- a/web/react/components/team_signup_with_ldap.jsx
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as Client from '../utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- team_error: {
- id: 'ldap_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'ldap_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'ldap_signup.teamName',
- defaultMessage: 'Enter name of new team'
- },
- badTeam: {
- id: 'login_ldap.badTeam',
- defaultMessage: 'Bad team name'
- },
- idReq: {
- id: 'login_ldap.idlReq',
- defaultMessage: 'An LDAP ID is required'
- },
- pwdReq: {
- id: 'login_ldap.pwdReq',
- defaultMessage: 'An LDAP password is required'
- },
- username: {
- id: 'login_ldap.username',
- defaultMessage: 'LDAP Username'
- },
- pwd: {
- id: 'login_ldap.pwd',
- defaultMessage: 'LDAP Password'
- }
-});
-
-class LdapSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.valueChange = this.valueChange.bind(this);
-
- this.state = {name: '', id: '', password: ''};
- }
-
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var teamSignup = {};
- teamSignup.user = {};
- teamSignup.team = {};
- var state = this.state;
- state.serverError = null;
-
- teamSignup.team.display_name = this.state.name;
-
- if (!teamSignup.team.display_name) {
- state.serverError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (teamSignup.team.display_name.length <= 2) {
- state.serverError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- const id = this.refs.id.value.trim();
- if (!id) {
- state.serverError = formatMessage(holders.idReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- teamSignup.team.name = utils.cleanUpUrlable(teamSignup.team.display_name);
- teamSignup.team.type = 'O';
-
- teamSignup.user.username = ReactDOM.findDOMNode(this.refs.id).value.trim();
- teamSignup.user.password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- teamSignup.user.allow_marketing = true;
- teamSignup.user.ldap = true;
- teamSignup.user.auth_service = 'ldap';
-
- $('#ldap-button').button('loading');
-
- Client.createTeamWithLdap(teamSignup,
- () => {
- Client.track('signup', 'signup_team_ldap_complete');
-
- Client.loginByLdap(teamSignup.team.name, id, password,
- () => {
- window.location.href = '/' + teamSignup.team.name + '/channels/town-square';
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- }
-
- valueChange() {
- this.setState({
- name: ReactDOM.findDOMNode(this.refs.teamname).value.trim(),
- id: ReactDOM.findDOMNode(this.refs.id).value.trim(),
- password: ReactDOM.findDOMNode(this.refs.password).value.trim()
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- if (this.state.id.length <= 1) {
- disabled = true;
- }
-
- if (this.state.password.length <= 1) {
- disabled = true;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.valueChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className={nameDivClass}>
- <input
- className='form-control'
- ref='id'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className={nameDivClass}>
- <input
- type='password'
- className='form-control'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className='form-group'>
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- id='ldap-button'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='ldap_signup.ldap'
- defaultMessage='Create team with LDAP Account'
- />
- </span>
- </a>
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='ldap_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-LdapSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(LdapSignUpPage); \ No newline at end of file
diff --git a/web/react/components/team_signup_with_sso.jsx b/web/react/components/team_signup_with_sso.jsx
deleted file mode 100644
index 465f73fd2..000000000
--- a/web/react/components/team_signup_with_sso.jsx
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as utils from '../utils/utils.jsx';
-import * as client from '../utils/client.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- team_error: {
- id: 'sso_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'sso_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'sso_signup.teamName',
- defaultMessage: 'Enter name of new team'
- }
-});
-
-class SSOSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.nameChange = this.nameChange.bind(this);
-
- this.state = {name: ''};
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var team = {};
- var state = this.state;
- state.nameError = null;
- state.serverError = null;
-
- team.display_name = this.state.name;
-
- if (!team.display_name) {
- state.nameError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (team.display_name.length <= 2) {
- state.nameError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- team.name = utils.cleanUpUrlable(team.display_name);
- team.type = 'O';
-
- client.createTeamWithSSO(team,
- this.props.service,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- } else {
- window.location.href = '/' + team.name + '/channels/town-square';
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- nameChange() {
- this.setState({name: ReactDOM.findDOMNode(this.refs.teamname).value.trim()});
- }
- render() {
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- var button = null;
-
- if (this.props.service === Constants.GITLAB_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.gitlab'
- defaultMessage='Create team with GitLab Account'
- />
- </span>
- </a>
- );
- } else if (this.props.service === Constants.GOOGLE_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.google'
- defaultMessage='Create team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.nameChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className='form-group'>
- {button}
- {serverError}
- </div>
- <div className='form-group margin--extra-2x'>
- <span><a href='/find_team'>
- <FormattedMessage
- id='sso_signup.find'
- defaultMessage='Find my teams'
- />
- </a></span>
- </div>
- </form>
- );
- }
-}
-
-SSOSignUpPage.defaultProps = {
- service: ''
-};
-SSOSignUpPage.propTypes = {
- intl: intlShape.isRequired,
- service: React.PropTypes.string
-};
-
-export default injectIntl(SSOSignUpPage); \ No newline at end of file
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
deleted file mode 100644
index d4eb60676..000000000
--- a/web/react/components/textbox.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AtMentionProvider from './suggestion/at_mention_provider.jsx';
-import CommandProvider from './suggestion/command_provider.jsx';
-import EmoticonProvider from './suggestion/emoticon_provider.jsx';
-import SuggestionList from './suggestion/suggestion_list.jsx';
-import SuggestionBox from './suggestion/suggestion_box.jsx';
-import ErrorStore from '../stores/error_store.jsx';
-
-import * as TextFormatting from '../utils/text_formatting.jsx';
-import * as Utils from '../utils/utils.jsx';
-import Constants from '../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
-
-export default class Textbox extends React.Component {
- constructor(props) {
- super(props);
-
- this.focus = this.focus.bind(this);
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onRecievedError = this.onRecievedError.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.resize = this.resize.bind(this);
- this.showPreview = this.showPreview.bind(this);
-
- this.state = {
- connection: ''
- };
-
- this.suggestionProviders = [new AtMentionProvider(), new EmoticonProvider()];
- if (props.supportsCommands) {
- this.suggestionProviders.push(new CommandProvider());
- }
- }
-
- getStateFromStores() {
- const error = ErrorStore.getLastError();
-
- if (error) {
- return {message: error.message};
- }
-
- return {message: null};
- }
-
- componentDidMount() {
- ErrorStore.addChangeListener(this.onRecievedError);
-
- this.resize();
- }
-
- componentWillUnmount() {
- ErrorStore.removeChangeListener(this.onRecievedError);
- }
-
- onRecievedError() {
- const errorCount = ErrorStore.getConnectionErrorCount();
-
- if (errorCount > 1) {
- this.setState({connection: 'bad-connection'});
- } else {
- this.setState({connection: ''});
- }
- }
-
- componentDidUpdate() {
- this.resize();
- }
-
- handleKeyPress(e) {
- this.props.onKeyPress(e);
- }
-
- handleKeyDown(e) {
- if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- focus() {
- this.refs.message.getTextbox().focus();
- }
-
- resize() {
- const textbox = this.refs.message.getTextbox();
- const $textbox = $(textbox);
- const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
-
- const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10);
- const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10);
- const maxHeight = parseInt($textbox.css('max-height'), 10) - borders;
-
- // set the height to auto and remove the scrollbar so we can get the actual size of the contents
- $textbox.css('height', 'auto').css('overflow-y', 'hidden');
-
- let height = textbox.scrollHeight - padding;
-
- if (height + padding > maxHeight) {
- height = maxHeight - padding;
-
- // turn scrollbar on and move over attachment icon to compensate for that
- $textbox.css('overflow-y', 'scroll');
- $wrapper.closest('.post-body__cell').addClass('scroll');
- } else {
- $wrapper.closest('.post-body__cell').removeClass('scroll');
- }
-
- // set the textarea to be the proper height
- $textbox.height(height);
-
- // set the wrapper height to match the height of the textbox including padding and borders
- $wrapper.height(height + padding + borders);
-
- if (this.state.preview) {
- $(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders);
- }
- }
-
- showPreview(e) {
- e.preventDefault();
- e.target.blur();
- this.setState({preview: !this.state.preview});
- this.resize();
- }
-
- render() {
- const hasText = this.props.messageText.length > 0;
-
- let previewLink = null;
- if (Utils.isFeatureEnabled(PreReleaseFeatures.MARKDOWN_PREVIEW)) {
- previewLink = (
- <a
- onClick={this.showPreview}
- className='textbox-preview-link'
- >
- {this.state.preview ? (
- <FormattedMessage
- id='textbox.edit'
- defaultMessage='Edit message'
- />
- ) : (
- <FormattedMessage
- id='textbox.preview'
- defaultMessage='Preview'
- />
- )}
- </a>
- );
- }
-
- let helpText = (
- <div
- style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}}
- className='help_format_text'
- >
- <b>
- <FormattedMessage
- id='textbox.bold'
- defaultMessage='**bold**'
- />
- </b>
- <i>
- <FormattedMessage
- id='textbox.italic'
- defaultMessage='_italic_'
- />
- </i>
- <span>~~<strike>
- <FormattedMessage
- id='textbox.strike'
- defaultMessage='strike'
- />
- </strike>~~ </span>
- <code>
- <FormattedMessage
- id='textbox.inlinecode'
- defaultMessage='`inline code`'
- />
- </code>
- <code>
- <FormattedMessage
- id='textbox.preformatted'
- defaultMessage='```preformatted```'
- />
- </code>
- <span>
- <FormattedMessage
- id='textbox.quote'
- defaultMessage='>quote'
- />
- </span>
- </div>
- );
-
- return (
- <div
- ref='wrapper'
- className='textarea-wrapper'
- >
- <SuggestionBox
- id={this.props.id}
- ref='message'
- className={`form-control custom-textarea ${this.state.connection}`}
- type='textarea'
- spellCheck='true'
- autoComplete='off'
- autoCorrect='off'
- rows='1'
- maxLength={Constants.MAX_POST_LEN}
- placeholder={this.props.createMessage}
- value={this.props.messageText}
- onUserInput={this.props.onUserInput}
- onKeyPress={this.handleKeyPress}
- onKeyDown={this.handleKeyDown}
- style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
- listComponent={SuggestionList}
- providers={this.suggestionProviders}
- />
- <div
- ref='preview'
- className='form-control custom-textarea textbox-preview-area'
- style={{display: this.state.preview ? 'block' : 'none'}}
- dangerouslySetInnerHTML={{__html: this.state.preview ? TextFormatting.formatText(this.props.messageText) : ''}}
- >
- </div>
- {helpText}
- <div className='help__text'>
- {previewLink}
- <a
- target='_blank'
- href='http://docs.mattermost.com/help/getting-started/messaging-basics.html'
- className='textbox-help-link'
- >
- <FormattedMessage
- id='textbox.help'
- defaultMessage='Help'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-
-Textbox.defaultProps = {
- supportsCommands: true
-};
-
-Textbox.propTypes = {
- id: React.PropTypes.string.isRequired,
- channelId: React.PropTypes.string,
- messageText: React.PropTypes.string.isRequired,
- onUserInput: React.PropTypes.func.isRequired,
- onKeyPress: React.PropTypes.func.isRequired,
- createMessage: React.PropTypes.string.isRequired,
- onKeyDown: React.PropTypes.func,
- supportsCommands: React.PropTypes.bool.isRequired
-};
diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx
deleted file mode 100644
index d5891cc57..000000000
--- a/web/react/components/time_since.jsx
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedRelative, FormattedDate} from 'mm-intl';
-
-var Tooltip = ReactBootstrap.Tooltip;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-export default class TimeSince extends React.Component {
- componentDidMount() {
- this.intervalId = setInterval(() => {
- this.forceUpdate();
- }, Constants.TIME_SINCE_UPDATE_INTERVAL);
- }
- componentWillUnmount() {
- clearInterval(this.intervalId);
- }
- render() {
- if (this.props.sameUser) {
- return (
- <time className='post__time'>
- {Utils.displayTimeFormatted(this.props.eventTime)}
- </time>
- );
- }
-
- const tooltip = (
- <Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
- <FormattedDate
- value={this.props.eventTime}
- month='long'
- day='numeric'
- year='numeric'
- hour12={true}
- hour='numeric'
- minute='2-digit'
- />
- </Tooltip>
- );
-
- return (
- <OverlayTrigger
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={tooltip}
- >
- <time className='post__time'>
- <FormattedRelative value={this.props.eventTime}/>
- </time>
- </OverlayTrigger>
- );
- }
-}
-
-TimeSince.defaultProps = {
- eventTime: 0,
- sameUser: false
-};
-
-TimeSince.propTypes = {
- eventTime: React.PropTypes.number.isRequired,
- sameUser: React.PropTypes.bool
-};
diff --git a/web/react/components/toggle_modal_button.jsx b/web/react/components/toggle_modal_button.jsx
deleted file mode 100644
index 10f9a4736..000000000
--- a/web/react/components/toggle_modal_button.jsx
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class ModalToggleButton extends React.Component {
- constructor(props) {
- super(props);
-
- this.show = this.show.bind(this);
- this.hide = this.hide.bind(this);
-
- this.state = {
- show: false
- };
- }
-
- show() {
- this.setState({show: true});
- }
-
- hide() {
- this.setState({show: false});
- }
-
- render() {
- const {children, dialogType, dialogProps, onClick, ...props} = this.props; // eslint-disable-line no-use-before-define
-
- // allow callers to provide an onClick which will be called before the modal is shown
- let clickHandler = this.show;
- if (onClick) {
- clickHandler = () => {
- onClick();
-
- this.show();
- };
- }
-
- // this assumes that all modals will have a show property and an onHide event
- const dialog = React.createElement(this.props.dialogType, Object.assign({}, dialogProps, {
- show: this.state.show,
- onHide: () => {
- this.hide();
-
- if (dialogProps.onHide) {
- dialogProps.onHide();
- }
- }
- }));
-
- // nesting the dialog in the anchor tag looks like it shouldn't work, but it does due to how react-bootstrap
- // renders modals at the top level of the DOM instead of where you specify in the virtual DOM
- return (
- <a
- {...props}
- href='#'
- onClick={clickHandler}
- >
- {children}
- {dialog}
- </a>
- );
- }
-}
-
-ModalToggleButton.propTypes = {
- children: React.PropTypes.node.isRequired,
- dialogType: React.PropTypes.func.isRequired,
- dialogProps: React.PropTypes.object,
- onClick: React.PropTypes.func
-};
-
-ModalToggleButton.defaultProps = {
- dialogProps: {}
-};
diff --git a/web/react/components/tutorial/tutorial_intro_screens.jsx b/web/react/components/tutorial/tutorial_intro_screens.jsx
deleted file mode 100644
index 45ebceb28..000000000
--- a/web/react/components/tutorial/tutorial_intro_screens.jsx
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../../stores/user_store.jsx';
-import ChannelStore from '../../stores/channel_store.jsx';
-import TeamStore from '../../stores/team_store.jsx';
-import PreferenceStore from '../../stores/preference_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-const NUM_SCREENS = 3;
-
-export default class TutorialIntroScreens extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleNext = this.handleNext.bind(this);
- this.createScreen = this.createScreen.bind(this);
- this.createCircles = this.createCircles.bind(this);
-
- this.state = {currentScreen: 0};
- }
- handleNext() {
- if (this.state.currentScreen < 2) {
- this.setState({currentScreen: this.state.currentScreen + 1});
- return;
- }
-
- Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
-
- let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
-
- const newValue = (parseInt(preference.value, 10) + 1).toString();
-
- preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
- AsyncClient.savePreferences([preference]);
- }
- componentDidMount() {
- $('.tutorials__scroll').perfectScrollbar();
- }
- skipTutorial(e) {
- e.preventDefault();
- const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
- AsyncClient.savePreferences([preference]);
- }
- createScreen() {
- switch (this.state.currentScreen) {
- case 0:
- return this.createScreenOne();
- case 1:
- return this.createScreenTwo();
- case 2:
- return this.createScreenThree();
- }
- return null;
- }
- createScreenOne() {
- const circles = this.createCircles();
-
- return (
- <div>
- <FormattedHTMLMessage
- id='tutorial_intro.screenOne'
- defaultMessage='<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>'
- />
- {circles}
- </div>
- );
- }
- createScreenTwo() {
- const circles = this.createCircles();
-
- return (
- <div>
- <FormattedHTMLMessage
- id='tutorial_intro.screenTwo'
- defaultMessage='<h3>How Mattermost works:</h3>
- <p>Communication happens in public discussion channels, private groups and direct messages.</p>
- <p>Everything is archived and searchable from any web-enabled desktop, laptop or phone.</p>'
- />
- {circles}
- </div>
- );
- }
- createScreenThree() {
- const team = TeamStore.getCurrent();
- let inviteModalLink;
- if (team.type === Constants.INVITE_TEAM) {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#invite_member'
- >
- <FormattedMessage
- id='tutorial_intro.invite'
- defaultMessage='Invite teammates'
- />
- </a>
- );
- } else {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
- >
- <FormattedMessage
- id='tutorial_intro.teamInvite'
- defaultMessage='Team Invite'
- />
- </a>
- );
- }
-
- const circles = this.createCircles();
-
- let supportInfo = null;
- if (global.window.mm_config.SupportEmail) {
- supportInfo = (
- <p>
- <FormattedMessage
- id='tutorial_intro.support'
- defaultMessage='Need anything, just email us at '
- />
- <a
- href={'mailto:' + global.window.mm_config.SupportEmail}
- target='_blank'
- >
- {global.window.mm_config.SupportEmail}
- </a>
- {'.'}
- </p>
- );
- }
-
- return (
- <div>
- <h3>
- <FormattedMessage
- id='tutorial_intro.allSet'
- defaultMessage='Youā€™re all set'
- />
- </h3>
- <p>
- {inviteModalLink}
- <FormattedMessage
- id='tutorial_intro.whenReady'
- defaultMessage=' when youā€™re ready.'
- />
- </p>
- {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.'
- />
- {circles}
- </div>
- );
- }
- createCircles() {
- const circles = [];
- for (let i = 0; i < NUM_SCREENS; i++) {
- let className = 'circle';
- if (i === this.state.currentScreen) {
- className += ' active';
- }
-
- circles.push(
- <a
- href='#'
- key={'circle' + i}
- className={className}
- onClick={(e) => { //eslint-disable-line no-loop-func
- e.preventDefault();
- this.setState({currentScreen: i});
- }}
- />
- );
- }
-
- return (
- <div className='tutorial__circles'>
- {circles}
- </div>
- );
- }
- render() {
- const height = Utils.windowHeight() - 100;
- const screen = this.createScreen();
-
- return (
- <div
- className='tutorials__scroll'
- style={{height}}
- >
- <div className='tutorial-steps__container'>
- <div className='tutorial__content'>
- <div className='tutorial__steps'>
- {screen}
- <div className='tutorial__footer'>
- <button
- className='btn btn-primary'
- tabIndex='1'
- onClick={this.handleNext}
- >
- <FormattedMessage
- id='tutorial_intro.next'
- defaultMessage='Next'
- />
- </button>
- <a
- className='tutorial-skip'
- href='#'
- onClick={this.skipTutorial}
- >
- <FormattedMessage
- id='tutorial_intro.skip'
- defaultMessage='Skip tutorial'
- />
- </a>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
diff --git a/web/react/components/tutorial/tutorial_tip.jsx b/web/react/components/tutorial/tutorial_tip.jsx
deleted file mode 100644
index 6bd7d89a4..000000000
--- a/web/react/components/tutorial/tutorial_tip.jsx
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../../stores/user_store.jsx';
-import PreferenceStore from '../../stores/preference_store.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const Preferences = Constants.Preferences;
-
-const Overlay = ReactBootstrap.Overlay;
-
-export default class TutorialTip extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleNext = this.handleNext.bind(this);
- this.toggle = this.toggle.bind(this);
-
- this.state = {currentScreen: 0, show: false};
- }
- toggle() {
- const show = !this.state.show;
- this.setState({show});
-
- if (!show && this.state.currentScreen >= this.props.screens.length - 1) {
- let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
-
- const newValue = (parseInt(preference.value, 10) + 1).toString();
-
- preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
- AsyncClient.savePreferences([preference]);
- }
- }
- handleNext() {
- if (this.state.currentScreen < this.props.screens.length - 1) {
- this.setState({currentScreen: this.state.currentScreen + 1});
- return;
- }
-
- this.toggle();
- }
- skipTutorial(e) {
- e.preventDefault();
- const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
- AsyncClient.savePreferences([preference]);
- }
- render() {
- const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? (
- <FormattedMessage
- id='tutorial_tip.ok'
- defaultMessage='Okay'
- />
- ) : (
- <FormattedMessage
- id='tutorial_tip.next'
- defaultMessage='Next'
- />
- );
-
- const dots = [];
- if (this.props.screens.length > 1) {
- for (let i = 0; i < this.props.screens.length; i++) {
- let className = 'circle';
- if (i === this.state.currentScreen) {
- className += ' active';
- }
-
- dots.push(
- <a
- href='#'
- key={'dotactive' + i}
- className={className}
- onClick={(e) => { //eslint-disable-line no-loop-func
- e.preventDefault();
- this.setState({currentScreen: i});
- }}
- />
- );
- }
- }
-
- var tipColor = '';
- if (this.props.overlayClass === 'tip-overlay--header' || this.props.overlayClass === 'tip-overlay--sidebar') {
- tipColor = 'White';
- }
-
- return (
- <div className={'tip-div ' + this.props.overlayClass}>
- <img
- className='tip-button'
- src={'/static/images/tutorialTip' + tipColor + '.gif'}
- width='35'
- onClick={this.toggle}
- ref='target'
- />
-
- <Overlay
- show={this.state.show}
- >
- <div className='tip-backdrop'/>
- </Overlay>
-
- <Overlay
- placement={this.props.placement}
- show={this.state.show}
- rootClose={true}
- onHide={this.toggle}
- target={() => this.refs.target}
- >
- <div className={'tip-overlay ' + this.props.overlayClass}>
- <div className='arrow'></div>
- {this.props.screens[this.state.currentScreen]}
- <div className='tutorial__footer'>
- <div className='tutorial__circles'>{dots}</div>
- <div className='text-right'>
- <button
- className='btn btn-primary'
- onClick={this.handleNext}
- >
- {buttonText}
- </button>
- <div className='tip-opt'>
- <FormattedMessage
- id='tutorial_tip.seen'
- defaultMessage='Seen this before? '
- />
- <a
- href='#'
- onClick={this.skipTutorial}
- >
- <FormattedMessage
- id='tutorial_tip.out'
- defaultMessage='Opt out of these tips.'
- />
- </a>
- </div>
- </div>
- </div>
- </div>
- </Overlay>
- </div>
- );
- }
-}
-
-TutorialTip.defaultProps = {
- overlayClass: ''
-};
-
-TutorialTip.propTypes = {
- screens: React.PropTypes.array.isRequired,
- placement: React.PropTypes.string.isRequired,
- overlayClass: React.PropTypes.string
-};
diff --git a/web/react/components/unread_channel_indicator.jsx b/web/react/components/unread_channel_indicator.jsx
deleted file mode 100644
index f897cdea3..000000000
--- a/web/react/components/unread_channel_indicator.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-// Indicator for the left sidebar which indicate if there's unread posts in a channel that is not shown
-// because it is either above or below the screen
-export default class UnreadChannelIndicator extends React.Component {
- render() {
- let displayValue = 'none';
- if (this.props.show) {
- displayValue = 'block';
- }
- return (
- <div
- className={'nav-pills__unread-indicator ' + this.props.extraClass}
- style={{display: displayValue}}
- >
- {this.props.text}
- </div>
- );
- }
-}
-
-UnreadChannelIndicator.defaultProps = {
- show: false,
- extraClass: '',
- text: ''
-};
-UnreadChannelIndicator.propTypes = {
- show: React.PropTypes.bool,
- extraClass: React.PropTypes.string,
- text: React.PropTypes.object
-};
diff --git a/web/react/components/user_list.jsx b/web/react/components/user_list.jsx
deleted file mode 100644
index 783d552dc..000000000
--- a/web/react/components/user_list.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-import UserListRow from './user_list_row.jsx';
-
-export default class UserList extends React.Component {
- render() {
- const users = this.props.users;
-
- let content;
- if (users.length > 0) {
- content = users.map((user) => {
- return (
- <UserListRow
- key={user.id}
- user={user}
- actions={this.props.actions}
- />
- );
- });
- } else {
- content = (
- <div key='no-users-found'>
- <FormattedMessage
- id='user_list.notFound'
- defaultMessage='No users found :('
- />
- </div>
- );
- }
-
- return (
- <div>
- {content}
- </div>
- );
- }
-}
-
-UserList.defaultProps = {
- users: [],
- actions: []
-};
-
-UserList.propTypes = {
- users: React.PropTypes.arrayOf(React.PropTypes.object),
- actions: React.PropTypes.arrayOf(React.PropTypes.func)
-};
diff --git a/web/react/components/user_list_row.jsx b/web/react/components/user_list_row.jsx
deleted file mode 100644
index 8c664ba82..000000000
--- a/web/react/components/user_list_row.jsx
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import * as Utils from '../utils/utils.jsx';
-
-export default function UserListRow({user, actions}) {
- const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', '');
-
- let name = user.username;
- if (user.nickname && nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME) {
- name = `${user.nickname} (@${user.username})`;
- } else if ((user.first_name || user.last_name) && (nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME || nameFormat === Constants.Preferences.DISPLAY_PREFER_FULL_NAME)) {
- name = `${Utils.getFullName(user)} (@${user.username})`;
- }
-
- const buttons = actions.map((Action, index) => {
- return (
- <Action
- key={index.toString()}
- user={user}
- />
- );
- });
-
- return (
- <div
- key={user.id}
- className='more-modal__row'
- >
- <img
- className='more-modal__image'
- width='38'
- height='38'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
- />
- <div
- className='more-modal__details'
- >
- <div className='more-modal__name'>
- {name}
- </div>
- <div className='more-modal__description'>
- {user.email}
- </div>
- </div>
- <div
- className='more-modal__actions'
- >
- {buttons}
- </div>
- </div>
- );
-}
-
-UserListRow.defaultProps = {
- actions: []
-};
-
-UserListRow.propTypes = {
- user: React.PropTypes.object.isRequired,
- actions: React.PropTypes.arrayOf(React.PropTypes.func)
-};
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
deleted file mode 100644
index e7a286b77..000000000
--- a/web/react/components/user_profile.jsx
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-var Popover = ReactBootstrap.Popover;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
-
-var id = 0;
-
-function nextId() {
- id = id + 1;
- return id;
-}
-
-export default class UserProfile extends React.Component {
- constructor(props) {
- super(props);
- this.uniqueId = nextId();
- }
- componentDidMount() {
- if (!this.props.disablePopover) {
- $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
- }
- }
- render() {
- let name = '...';
- let email = '';
- let profileImg = '';
- if (this.props.user) {
- name = Utils.displayUsername(this.props.user.id);
- email = this.props.user.email;
- profileImg = '/api/v1/users/' + this.props.user.id + '/image?time=' + this.props.user.update_at;
- }
-
- if (this.props.overwriteName) {
- name = this.props.overwriteName;
- }
-
- if (this.props.overwriteImage) {
- profileImg = this.props.overwriteImage;
- }
-
- if (this.props.disablePopover) {
- return <div>{name}</div>;
- }
-
- var dataContent = [];
- dataContent.push(
- <img
- className='user-popover__image'
- src={profileImg}
- height='128'
- width='128'
- key='user-popover-image'
- />
- );
-
- if (!global.window.mm_config.ShowEmailAddress === 'true') {
- dataContent.push(
- <div
- className='text-nowrap'
- key='user-popover-no-email'
- >
- <FormattedMessage
- id='user_profile.notShared'
- defaultMessage='Email not shared'
- />
- </div>
- );
- } else {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={email}
- key='user-popover-email'
- >
- <a
- href={'mailto:' + email}
- className='text-nowrap text-lowercase user-popover__email'
- >
- {email}
- </a>
- </div>
- );
- }
-
- return (
- <OverlayTrigger
- trigger='click'
- placement='right'
- rootClose={true}
- overlay={
- <Popover
- title={name}
- id='user-profile-popover'
- >
- {dataContent}
- </Popover>
- }
- >
- <div
- className='user-popover'
- id={'profile_' + this.uniqueId}
- >
- {name}
- </div>
- </OverlayTrigger>
- );
- }
-}
-
-UserProfile.defaultProps = {
- user: {},
- overwriteName: '',
- overwriteImage: '',
- disablePopover: false
-};
-UserProfile.propTypes = {
- user: React.PropTypes.object,
- overwriteName: React.PropTypes.string,
- overwriteImage: React.PropTypes.string,
- disablePopover: React.PropTypes.bool
-};
diff --git a/web/react/components/user_settings/custom_theme_chooser.jsx b/web/react/components/user_settings/custom_theme_chooser.jsx
deleted file mode 100644
index 4ee9fd0e2..000000000
--- a/web/react/components/user_settings/custom_theme_chooser.jsx
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-
-const OverlayTrigger = ReactBootstrap.OverlayTrigger;
-const Popover = ReactBootstrap.Popover;
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const messages = defineMessages({
- sidebarBg: {
- id: 'user.settings.custom_theme.sidebarBg',
- defaultMessage: 'Sidebar BG'
- },
- sidebarText: {
- id: 'user.settings.custom_theme.sidebarText',
- defaultMessage: 'Sidebar Text'
- },
- sidebarHeaderBg: {
- id: 'user.settings.custom_theme.sidebarHeaderBg',
- defaultMessage: 'Sidebar Header BG'
- },
- sidebarHeaderTextColor: {
- id: 'user.settings.custom_theme.sidebarHeaderTextColor',
- defaultMessage: 'Sidebar Header Text'
- },
- sidebarUnreadText: {
- id: 'user.settings.custom_theme.sidebarUnreadText',
- defaultMessage: 'Sidebar Unread Text'
- },
- sidebarTextHoverBg: {
- id: 'user.settings.custom_theme.sidebarTextHoverBg',
- defaultMessage: 'Sidebar Text Hover BG'
- },
- sidebarTextActiveBorder: {
- id: 'user.settings.custom_theme.sidebarTextActiveBorder',
- defaultMessage: 'Sidebar Text Active Border'
- },
- sidebarTextActiveColor: {
- id: 'user.settings.custom_theme.sidebarTextActiveColor',
- defaultMessage: 'Sidebar Text Active Color'
- },
- onlineIndicator: {
- id: 'user.settings.custom_theme.onlineIndicator',
- defaultMessage: 'Online Indicator'
- },
- awayIndicator: {
- id: 'user.settings.custom_theme.awayIndicator',
- defaultMessage: 'Away Indicator'
- },
- mentionBj: {
- id: 'user.settings.custom_theme.mentionBj',
- defaultMessage: 'Mention Jewel BG'
- },
- mentionColor: {
- id: 'user.settings.custom_theme.mentionColor',
- defaultMessage: 'Mention Jewel Text'
- },
- centerChannelBg: {
- id: 'user.settings.custom_theme.centerChannelBg',
- defaultMessage: 'Center Channel BG'
- },
- centerChannelColor: {
- id: 'user.settings.custom_theme.centerChannelColor',
- defaultMessage: 'Center Channel Text'
- },
- newMessageSeparator: {
- id: 'user.settings.custom_theme.newMessageSeparator',
- defaultMessage: 'New Message Separator'
- },
- linkColor: {
- id: 'user.settings.custom_theme.linkColor',
- defaultMessage: 'Link Color'
- },
- buttonBg: {
- id: 'user.settings.custom_theme.buttonBg',
- defaultMessage: 'Button BG'
- },
- buttonColor: {
- id: 'user.settings.custom_theme.buttonColor',
- defaultMessage: 'Button Text'
- },
- mentionHighlightBg: {
- id: 'user.settings.custom_theme.mentionHighlightBg',
- defaultMessage: 'Mention Highlight BG'
- },
- mentionHighlightLink: {
- id: 'user.settings.custom_theme.mentionHighlightLink',
- defaultMessage: 'Mention Highlight Link'
- },
- codeTheme: {
- id: 'user.settings.custom_theme.codeTheme',
- defaultMessage: 'Code Theme'
- }
-});
-
-class CustomThemeChooser extends React.Component {
- constructor(props) {
- super(props);
-
- this.onPickerChange = this.onPickerChange.bind(this);
- this.onInputChange = this.onInputChange.bind(this);
- this.pasteBoxChange = this.pasteBoxChange.bind(this);
- this.toggleContent = this.toggleContent.bind(this);
-
- this.state = {};
- }
- componentDidMount() {
- $('.color-picker').colorpicker({
- format: 'hex'
- });
- $('.color-picker').on('changeColor', this.onPickerChange);
- }
- componentDidUpdate() {
- const theme = this.props.theme;
- Constants.THEME_ELEMENTS.forEach((element) => {
- if (theme.hasOwnProperty(element.id) && element.id !== 'codeTheme') {
- $('#' + element.id).data('colorpicker').color.setColor(theme[element.id]);
- $('#' + element.id).colorpicker('update');
- }
- });
- }
- onPickerChange(e) {
- const theme = this.props.theme;
- theme[e.target.id] = e.color.toHex();
- theme.type = 'custom';
- this.props.updateTheme(theme);
- }
- onInputChange(e) {
- const theme = this.props.theme;
- theme[e.target.parentNode.id] = e.target.value;
- theme.type = 'custom';
- this.props.updateTheme(theme);
- }
- pasteBoxChange(e) {
- const text = e.target.value;
-
- if (text.length === 0) {
- return;
- }
-
- const colors = text.split(',');
-
- const theme = {type: 'custom'};
- let index = 0;
- Constants.THEME_ELEMENTS.forEach((element) => {
- if (index < colors.length - 1) {
- theme[element.id] = colors[index];
- }
- index++;
- });
- theme.codeTheme = colors[colors.length - 1];
-
- this.props.updateTheme(theme);
- }
- toggleContent(e) {
- e.stopPropagation();
- if ($(e.target).hasClass('theme-elements__header')) {
- $(e.target).next().slideToggle();
- $(e.target).toggleClass('open');
- } else {
- $(e.target).closest('.theme-elements__header').next().slideToggle();
- $(e.target).closest('.theme-elements__header').toggleClass('open');
- }
- }
- render() {
- const {formatMessage} = this.props.intl;
- const theme = this.props.theme;
-
- const sidebarElements = [];
- const centerChannelElements = [];
- const linkAndButtonElements = [];
- let colors = '';
- Constants.THEME_ELEMENTS.forEach((element, index) => {
- if (element.id === 'codeTheme') {
- const codeThemeOptions = [];
-
- element.themes.forEach((codeTheme, codeThemeIndex) => {
- codeThemeOptions.push(
- <option
- key={'code-theme-key' + codeThemeIndex}
- value={codeTheme.id}
- >
- {codeTheme.uiName}
- </option>
- );
- });
-
- var popoverContent = (
- <Popover
- bsStyle='info'
- id='code-popover'
- className='code-popover'
- >
- <img
- width='200'
- src={'/static/images/themes/code_themes/' + theme[element.id] + '.png'}
- />
- </Popover>
- );
-
- centerChannelElements.push(
- <div
- className='col-sm-6 form-group'
- key={'custom-theme-key' + index}
- >
- <label className='custom-label'>{formatMessage(messages[element.id])}</label>
- <div
- className='input-group theme-group group--code dropdown'
- id={element.id}
- >
- <select
- className='form-control'
- type='text'
- value={theme[element.id]}
- onChange={this.onInputChange}
- >
- {codeThemeOptions}
- </select>
- <OverlayTrigger
- placement='top'
- overlay={popoverContent}
- ref='headerOverlay'
- >
- <span className='input-group-addon'>
- <img
- src={'/static/images/themes/code_themes/' + theme[element.id] + '.png'}
- />
- </span>
- </OverlayTrigger>
- </div>
- </div>
- );
- } else if (element.group === 'centerChannelElements') {
- centerChannelElements.push(
- <div
- className='col-sm-6 form-group element'
- key={'custom-theme-key' + index}
- >
- <label className='custom-label'>{formatMessage(messages[element.id])}</label>
- <div
- className='input-group color-picker'
- id={element.id}
- >
- <input
- className='form-control'
- type='text'
- value={theme[element.id]}
- onChange={this.onInputChange}
- />
- <span className='input-group-addon'><i></i></span>
- </div>
- </div>
- );
-
- colors += theme[element.id] + ',';
- } else if (element.group === 'sidebarElements') {
- sidebarElements.push(
- <div
- className='col-sm-6 form-group element'
- key={'custom-theme-key' + index}
- >
- <label className='custom-label'>{formatMessage(messages[element.id])}</label>
- <div
- className='input-group color-picker'
- id={element.id}
- >
- <input
- className='form-control'
- type='text'
- value={theme[element.id]}
- onChange={this.onInputChange}
- />
- <span className='input-group-addon'><i></i></span>
- </div>
- </div>
- );
-
- colors += theme[element.id] + ',';
- } else {
- linkAndButtonElements.push(
- <div
- className='col-sm-6 form-group element'
- key={'custom-theme-key' + index}
- >
- <label className='custom-label'>{formatMessage(messages[element.id])}</label>
- <div
- className='input-group color-picker'
- id={element.id}
- >
- <input
- className='form-control'
- type='text'
- value={theme[element.id]}
- onChange={this.onInputChange}
- />
- <span className='input-group-addon'><i></i></span>
- </div>
- </div>
- );
-
- colors += theme[element.id] + ',';
- }
- });
-
- colors += theme.codeTheme;
-
- const pasteBox = (
- <div className='col-sm-12'>
- <label className='custom-label'>
- <FormattedMessage
- id='user.settings.custom_theme.copyPaste'
- defaultMessage='Copy and paste to share theme colors:'
- />
- </label>
- <input
- type='text'
- className='form-control'
- value={colors}
- onChange={this.pasteBoxChange}
- />
- </div>
- );
-
- return (
- <div className='appearance-section padding-top'>
- <div className='theme-elements row'>
- <div
- className='theme-elements__header'
- onClick={this.toggleContent}
- >
- {'Sidebar Styles'}
- <div className='header__icon'>
- <i className='fa fa-plus'></i>
- <i className='fa fa-minus'></i>
- </div>
- </div>
- <div className='theme-elements__body'>
- {sidebarElements}
- </div>
- </div>
- <div className='theme-elements row'>
- <div
- className='theme-elements__header'
- onClick={this.toggleContent}
- >
- {'Center Channel Styles'}
- <div className='header__icon'>
- <i className='fa fa-plus'></i>
- <i className='fa fa-minus'></i>
- </div>
- </div>
- <div className='theme-elements__body'>
- {centerChannelElements}
- </div>
- </div>
- <div className='theme-elements row form-group'>
- <div
- className='theme-elements__header'
- onClick={this.toggleContent}
- >
- {'Link and Button Styles'}
- <div className='header__icon'>
- <i className='fa fa-plus'></i>
- <i className='fa fa-minus'></i>
- </div>
- </div>
- <div className='theme-elements__body'>
- {linkAndButtonElements}
- </div>
- </div>
- <div className='row'>
- {pasteBox}
- </div>
- </div>
- );
- }
-}
-
-CustomThemeChooser.propTypes = {
- intl: intlShape.isRequired,
- theme: React.PropTypes.object.isRequired,
- updateTheme: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(CustomThemeChooser);
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
deleted file mode 100644
index e9e90a936..000000000
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ModalStore from '../../stores/modal_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import * as Client from '../../utils/client.jsx';
-const Modal = ReactBootstrap.Modal;
-
-import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
-import Constants from '../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- submitError: {
- id: 'user.settings.import_theme.submitError',
- defaultMessage: 'Invalid format, please try copying and pasting in again.'
- }
-});
-
-const ActionTypes = Constants.ActionTypes;
-
-class ImportThemeModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateShow = this.updateShow.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleChange = this.handleChange.bind(this);
-
- this.state = {
- inputError: '',
- show: false
- };
- }
- componentDidMount() {
- ModalStore.addModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
- }
- componentWillUnmount() {
- ModalStore.removeModalListener(ActionTypes.TOGGLE_IMPORT_THEME_MODAL, this.updateShow);
- }
- updateShow(show) {
- this.setState({show});
- }
- handleSubmit(e) {
- e.preventDefault();
-
- const text = ReactDOM.findDOMNode(this.refs.input).value;
-
- if (!this.isInputValid(text)) {
- this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
- return;
- }
-
- const colors = text.split(',');
- const theme = {type: 'custom'};
-
- theme.sidebarBg = colors[0];
- theme.sidebarText = colors[5];
- theme.sidebarUnreadText = colors[5];
- theme.sidebarTextHoverBg = colors[4];
- theme.sidebarTextActiveBorder = colors[2];
- theme.sidebarTextActiveColor = colors[3];
- theme.sidebarHeaderBg = colors[1];
- theme.sidebarHeaderTextColor = colors[5];
- theme.onlineIndicator = colors[6];
- theme.awayIndicator = '#E0B333';
- theme.mentionBj = colors[7];
- theme.mentionColor = '#ffffff';
- theme.centerChannelBg = '#ffffff';
- theme.centerChannelColor = '#333333';
- theme.newMessageSeparator = '#F80';
- theme.linkColor = '#2389d7';
- theme.buttonBg = '#26a970';
- theme.buttonColor = '#ffffff';
- theme.mentionHighlightBg = '#fff2bb';
- theme.mentionHighlightLink = '#2f81b7';
- theme.codeTheme = 'github';
-
- let user = UserStore.getCurrentUser();
- user.theme_props = theme;
-
- Client.updateUser(user,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ME,
- me: data
- });
-
- this.setState({show: false});
- Utils.applyTheme(theme);
- },
- (err) => {
- var state = this.getStateFromStores();
- state.serverError = err;
- this.setState(state);
- }
- );
- }
- isInputValid(text) {
- if (text.length === 0) {
- return false;
- }
-
- if (text.indexOf(' ') !== -1) {
- return false;
- }
-
- if (text.length > 0 && text.indexOf(',') === -1) {
- return false;
- }
-
- if (text.length > 0) {
- const colors = text.split(',');
-
- if (colors.length !== 8) {
- return false;
- }
-
- for (let i = 0; i < colors.length; i++) {
- if (colors[i].length !== 7 && colors[i].length !== 4) {
- return false;
- }
-
- if (colors[i].charAt(0) !== '#') {
- return false;
- }
- }
- }
-
- return true;
- }
- handleChange(e) {
- if (this.isInputValid(e.target.value)) {
- this.setState({inputError: null});
- } else {
- this.setState({inputError: this.props.intl.formatMessage(holders.submitError)});
- }
- }
- render() {
- return (
- <span>
- <Modal
- show={this.state.show}
- onHide={() => this.setState({show: false})}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='user.settings.import_theme.importHeader'
- defaultMessage='Import Slack Theme'
- />
- </Modal.Title>
- </Modal.Header>
- <form
- role='form'
- className='form-horizontal'
- >
- <Modal.Body>
- <p>
- <FormattedMessage
- id='user.settings.import_theme.importBody'
- defaultMessage='To import a theme, go to a Slack team and look for ā€œPreferences -> Sidebar Themeā€. Open the custom theme option, copy the theme color values and paste them here:'
- />
- </p>
- <div className='form-group less'>
- <div className='col-sm-9'>
- <input
- ref='input'
- type='text'
- className='form-control'
- onChange={this.handleChange}
- />
- <div className='input__help'>
- {this.state.inputError}
- </div>
- </div>
- </div>
- </Modal.Body>
- <Modal.Footer>
- <button
- type='button'
- className='btn btn-default'
- onClick={() => this.setState({show: false})}
- >
- <FormattedMessage
- id='user.settings.import_theme.cancel'
- defaultMessage='Cancel'
- />
- </button>
- <button
- onClick={this.handleSubmit}
- type='submit'
- className='btn btn-primary'
- tabIndex='3'
- >
- <FormattedMessage
- id='user.settings.import_theme.submit'
- defaultMessage='Submit'
- />
- </button>
- </Modal.Footer>
- </form>
- </Modal>
- </span>
- );
- }
-}
-
-ImportThemeModal.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(ImportThemeModal);
diff --git a/web/react/components/user_settings/manage_command_hooks.jsx b/web/react/components/user_settings/manage_command_hooks.jsx
deleted file mode 100644
index 2947138be..000000000
--- a/web/react/components/user_settings/manage_command_hooks.jsx
+++ /dev/null
@@ -1,679 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoadingScreen from '../loading_screen.jsx';
-
-import * as Client from '../../utils/client.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- requestTypePost: {
- id: 'user.settings.cmds.request_type_post',
- defaultMessage: 'POST'
- },
- requestTypeGet: {
- id: 'user.settings.cmds.request_type_get',
- defaultMessage: 'GET'
- },
- addDisplayNamePlaceholder: {
- id: 'user.settings.cmds.add_display_name.placeholder',
- defaultMessage: 'Example: "Search patient records"'
- },
- addUsernamePlaceholder: {
- id: 'user.settings.cmds.add_username.placeholder',
- defaultMessage: 'Username'
- },
- addTriggerPlaceholder: {
- id: 'user.settings.cmds.add_trigger.placeholder',
- defaultMessage: 'Command trigger e.g. "hello" not including the slash'
- },
- addAutoCompleteDescPlaceholder: {
- id: 'user.settings.cmds.auto_complete_desc.placeholder',
- defaultMessage: 'Example: "Returns search results for patient records"'
- },
- addAutoCompleteHintPlaceholder: {
- id: 'user.settings.cmds.auto_complete_hint.placeholder',
- defaultMessage: 'Example: [Patient Name]'
- },
- adUrlPlaceholder: {
- id: 'user.settings.cmds.url.placeholder',
- defaultMessage: 'Must start with http:// or https://'
- },
- autocompleteYes: {
- id: 'user.settings.cmds.auto_complete.yes',
- defaultMessage: 'yes'
- },
- autocompleteNo: {
- id: 'user.settings.cmds.auto_complete.no',
- defaultMessage: 'no'
- }
-});
-
-export default class ManageCommandCmds extends React.Component {
- constructor() {
- super();
-
- this.getCmds = this.getCmds.bind(this);
- this.addNewCmd = this.addNewCmd.bind(this);
- this.emptyCmd = this.emptyCmd.bind(this);
- this.updateTrigger = this.updateTrigger.bind(this);
- this.updateURL = this.updateURL.bind(this);
- this.updateMethod = this.updateMethod.bind(this);
- this.updateUsername = this.updateUsername.bind(this);
- this.updateIconURL = this.updateIconURL.bind(this);
- this.updateDisplayName = this.updateDisplayName.bind(this);
- this.updateAutoComplete = this.updateAutoComplete.bind(this);
- this.updateAutoCompleteDesc = this.updateAutoCompleteDesc.bind(this);
- this.updateAutoCompleteHint = this.updateAutoCompleteHint.bind(this);
-
- this.state = {cmds: [], cmd: this.emptyCmd(), getCmdsComplete: false};
- }
-
- static propTypes() {
- return {
- intl: intlShape.isRequired
- };
- }
-
- emptyCmd() {
- var cmd = {};
- cmd.url = '';
- cmd.trigger = '';
- cmd.method = 'P';
- cmd.username = '';
- cmd.icon_url = '';
- cmd.auto_complete = false;
- cmd.auto_complete_desc = '';
- cmd.auto_complete_hint = '';
- cmd.display_name = '';
- return cmd;
- }
-
- componentDidMount() {
- this.getCmds();
- }
-
- addNewCmd(e) {
- e.preventDefault();
-
- if (this.state.cmd.trigger === '' || this.state.cmd.url === '') {
- return;
- }
-
- var cmd = this.state.cmd;
- if (cmd.trigger.length !== 0) {
- cmd.trigger = cmd.trigger.trim();
- }
- cmd.url = cmd.url.trim();
-
- Client.addCommand(
- cmd,
- (data) => {
- let cmds = Object.assign([], this.state.cmds);
- if (!cmds) {
- cmds = [];
- }
- cmds.push(data);
- this.setState({cmds, addError: null, cmd: this.emptyCmd()});
- },
- (err) => {
- this.setState({addError: err.message});
- }
- );
- }
-
- removeCmd(id) {
- const data = {};
- data.id = id;
-
- Client.deleteCommand(
- data,
- () => {
- const cmds = this.state.cmds;
- let index = -1;
- for (let i = 0; i < cmds.length; i++) {
- if (cmds[i].id === id) {
- index = i;
- break;
- }
- }
-
- if (index !== -1) {
- cmds.splice(index, 1);
- }
-
- this.setState({cmds});
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
-
- regenToken(id) {
- const regenData = {};
- regenData.id = id;
-
- Client.regenCommandToken(
- regenData,
- (data) => {
- const cmds = Object.assign([], this.state.cmds);
- for (let i = 0; i < cmds.length; i++) {
- if (cmds[i].id === id) {
- cmds[i] = data;
- break;
- }
- }
-
- this.setState({cmds, editError: null});
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
-
- getCmds() {
- Client.listTeamCommands(
- (data) => {
- if (data) {
- this.setState({cmds: data, getCmdsComplete: true, editError: null});
- }
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
-
- updateTrigger(e) {
- var cmd = this.state.cmd;
- cmd.trigger = e.target.value;
- this.setState(cmd);
- }
-
- updateURL(e) {
- var cmd = this.state.cmd;
- cmd.url = e.target.value;
- this.setState(cmd);
- }
-
- updateMethod(e) {
- var cmd = this.state.cmd;
- cmd.method = e.target.value;
- this.setState(cmd);
- }
-
- updateUsername(e) {
- var cmd = this.state.cmd;
- cmd.username = e.target.value;
- this.setState(cmd);
- }
-
- updateIconURL(e) {
- var cmd = this.state.cmd;
- cmd.icon_url = e.target.value;
- this.setState(cmd);
- }
-
- updateDisplayName(e) {
- var cmd = this.state.cmd;
- cmd.display_name = e.target.value;
- this.setState(cmd);
- }
-
- updateAutoComplete(e) {
- var cmd = this.state.cmd;
- cmd.auto_complete = e.target.checked;
- this.setState(cmd);
- }
-
- updateAutoCompleteDesc(e) {
- var cmd = this.state.cmd;
- cmd.auto_complete_desc = e.target.value;
- this.setState(cmd);
- }
-
- updateAutoCompleteHint(e) {
- var cmd = this.state.cmd;
- cmd.auto_complete_hint = e.target.value;
- this.setState(cmd);
- }
-
- render() {
- let addError;
- if (this.state.addError) {
- addError = <label className='has-error'>{this.state.addError}</label>;
- }
-
- let editError;
- if (this.state.editError) {
- addError = <label className='has-error'>{this.state.editError}</label>;
- }
-
- const cmds = [];
- this.state.cmds.forEach((cmd) => {
- let triggerDiv;
- if (cmd.trigger && cmd.trigger.length !== 0) {
- triggerDiv = (
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.trigger'
- defaultMessage='Command Trigger Word: '
- />
- </strong>{cmd.trigger}
- </div>
- );
- }
-
- cmds.push(
- <div
- key={cmd.id}
- className='webhook__item webcmd__item'
- >
- {triggerDiv}
- <div className='padding-top x2 webcmd__url'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.url'
- defaultMessage='Request URL: '
- />
- </strong><span className='word-break--all'>{cmd.url}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.request_type'
- defaultMessage='Request Method: '
- />
- </strong>
- <span className='word-break--all'>
- {
- cmd.method === 'P' ?
- <FormattedMessage
- id='user.settings.cmds.request_type_post'
- defaultMessage='POST'
- /> :
- <FormattedMessage
- id='user.settings.cmds.request_type_get'
- defaultMessage='GET'
- />
- }
- </span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.username'
- defaultMessage='Response Username: '
- />
- </strong><span className='word-break--all'>{cmd.username}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.icon_url'
- defaultMessage='Response Icon: '
- />
- </strong><span className='word-break--all'>{cmd.icon_url}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.auto_complete'
- defaultMessage='Autocomplete: '
- />
- </strong><span className='word-break--all'>{cmd.auto_complete ? this.props.intl.formatMessage(holders.autocompleteYes) : this.props.intl.formatMessage(holders.autocompleteNo)}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint'
- defaultMessage='Autocomplete Hint: '
- />
- </strong><span className='word-break--all'>{cmd.auto_complete_hint}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc'
- defaultMessage='Autocomplete Description: '
- />
- </strong><span className='word-break--all'>{cmd.auto_complete_desc}</span>
- </div>
- <div className='padding-top x2'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.display_name'
- defaultMessage='Descriptive Label: '
- />
- </strong><span className='word-break--all'>{cmd.display_name}</span>
- </div>
- <div className='padding-top'>
- <strong>
- <FormattedMessage
- id='user.settings.cmds.token'
- defaultMessage='Token: '
- />
- </strong>{cmd.token}
- </div>
- <div className='padding-top'>
- <a
- className='text-danger'
- href='#'
- onClick={this.regenToken.bind(this, cmd.id)}
- >
- <FormattedMessage
- id='user.settings.cmds.regen'
- defaultMessage='Regen Token'
- />
- </a>
- <a
- className='webhook__remove webcmd__remove'
- href='#'
- onClick={this.removeCmd.bind(this, cmd.id)}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </a>
- </div>
- <div className='padding-top x2 divider-light'></div>
- </div>
- );
- });
-
- let displayCmds;
- if (!this.state.getCmdsComplete) {
- displayCmds = <LoadingScreen/>;
- } else if (cmds.length > 0) {
- displayCmds = cmds;
- } else {
- displayCmds = (
- <div className='padding-top x2'>
- <FormattedMessage
- id='user.settings.cmds.none'
- defaultMessage='None'
- />
- </div>
- );
- }
-
- const existingCmds = (
- <div className='webhooks__container webcmds__container'>
- <label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.cmds.existing'
- defaultMessage='Existing commands'
- />
- </label>
- <div className='padding-top divider-light'></div>
- <div className='webhooks__list webcmds__list'>
- {displayCmds}
- </div>
- </div>
- );
-
- const disableButton = this.state.cmd.trigger === '' || this.state.cmd.url === '';
-
- return (
- <div key='addCommandCmd'>
- <FormattedHTMLMessage
- id='user.settings.cmds.add_desc'
- defaultMessage='Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name ā€œJoe Smithā€. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.'
- />
- <div><label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.cmds.add_new'
- defaultMessage='Add a new command'
- />
- </label></div>
- <div className='padding-top divider-light'></div>
- <div className='padding-top'>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.trigger'
- defaultMessage='Command Trigger Word: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='trigger'
- className='form-control'
- value={this.state.cmd.trigger}
- onChange={this.updateTrigger}
- placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.trigger_desc'
- defaultMessage='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.url'
- defaultMessage='Request URL: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='URL'
- className='form-control'
- value={this.state.cmd.url}
- rows={1}
- onChange={this.updateURL}
- placeholder={this.props.intl.formatMessage(holders.adUrlPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.url_desc'
- defaultMessage='The callback URL to receive the HTTP POST or GET event request when the slash command is run.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.request_type'
- defaultMessage='Request Method: '
- />
- </label>
- <div className='padding-top'>
- <select
- ref='method'
- className='form-control'
- value={this.state.cmd.method}
- onChange={this.updateMethod}
- >
- <option value='P'>
- {this.props.intl.formatMessage(holders.requestTypePost)}
- </option>
- <option value='G'>
- {this.props.intl.formatMessage(holders.requestTypeGet)}
- </option>
- </select>
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.request_type_desc'
- defaultMessage='The type of command request issued to the Request URL.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.username'
- defaultMessage='Response Username: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='username'
- className='form-control'
- value={this.state.cmd.username}
- onChange={this.updateUsername}
- placeholder={this.props.intl.formatMessage(holders.addUsernamePlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.username_desc'
- defaultMessage='Choose a username override for responses for this slash command. Usernames can consist of up to 22 characters consisting of lowercase letters, numbers and they symbols "-", "_", and "." .'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.icon_url'
- defaultMessage='Response Icon: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='iconURL'
- className='form-control'
- value={this.state.cmd.icon_url}
- onChange={this.updateIconURL}
- placeholder='https://www.example.com/myicon.png'
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.icon_url_desc'
- defaultMessage='Choose a profile picture override for the post responses to this slash command. Enter the URL of a .png or .jpg file at least 128 pixels by 128 pixels.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete'
- defaultMessage='Autocomplete: '
- />
- </label>
- <div className='padding-top'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.cmd.auto_complete}
- onChange={this.updateAutoComplete}
- />
- <FormattedMessage
- id='user.settings.cmds.auto_complete_help'
- defaultMessage=' Show this command in the autocomplete list.'
- />
- </label>
- </div>
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint'
- defaultMessage='Autocomplete Hint: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteHint'
- className='form-control'
- value={this.state.cmd.auto_complete_hint}
- onChange={this.updateAutoCompleteHint}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint_desc'
- defaultMessage='Optional hint in the autocomplete list about parameters needed for command.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc'
- defaultMessage='Autocomplete Description: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteDesc'
- className='form-control'
- value={this.state.cmd.auto_complete_desc}
- onChange={this.updateAutoCompleteDesc}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc_desc'
- defaultMessage='Optional short description of slash command for the autocomplete list.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.display_name'
- defaultMessage='Descriptive Label: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='displayName'
- className='form-control'
- value={this.state.cmd.display_name}
- onChange={this.updateDisplayName}
- placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.cmd_display_name'
- defaultMessage='Brief description of slash command to show in listings.'
- />
- </div>
- {addError}
- </div>
-
- <div className='padding-top x2 padding-bottom'>
- <a
- className={'btn btn-sm btn-primary'}
- href='#'
- disabled={disableButton}
- onClick={this.addNewCmd}
- >
- <FormattedMessage
- id='user.settings.cmds.add'
- defaultMessage='Add'
- />
- </a>
- </div>
- </div>
- {existingCmds}
- {editError}
- </div>
- );
- }
-}
-
-export default injectIntl(ManageCommandCmds);
diff --git a/web/react/components/user_settings/manage_incoming_hooks.jsx b/web/react/components/user_settings/manage_incoming_hooks.jsx
deleted file mode 100644
index 79a71b5ac..000000000
--- a/web/react/components/user_settings/manage_incoming_hooks.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import Constants from '../../utils/constants.jsx';
-import ChannelStore from '../../stores/channel_store.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-export default class ManageIncomingHooks extends React.Component {
- constructor() {
- super();
-
- this.getHooks = this.getHooks.bind(this);
- this.addNewHook = this.addNewHook.bind(this);
- this.updateChannelId = this.updateChannelId.bind(this);
-
- this.state = {hooks: [], channelId: ChannelStore.getByName(Constants.DEFAULT_CHANNEL).id, getHooksComplete: false};
- }
- componentDidMount() {
- this.getHooks();
- }
- addNewHook() {
- const hook = {};
- hook.channel_id = this.state.channelId;
-
- Client.addIncomingHook(
- hook,
- (data) => {
- let hooks = this.state.hooks;
- if (!hooks) {
- hooks = [];
- }
- hooks.push(data);
- this.setState({hooks});
- },
- (err) => {
- this.setState({serverError: err});
- }
- );
- }
- removeHook(id) {
- const data = {};
- data.id = id;
-
- Client.deleteIncomingHook(
- data,
- () => {
- const hooks = this.state.hooks;
- let index = -1;
- for (let i = 0; i < hooks.length; i++) {
- if (hooks[i].id === id) {
- index = i;
- break;
- }
- }
-
- if (index !== -1) {
- hooks.splice(index, 1);
- }
-
- this.setState({hooks});
- },
- (err) => {
- this.setState({serverError: err});
- }
- );
- }
- getHooks() {
- Client.listIncomingHooks(
- (data) => {
- const state = this.state;
-
- if (data) {
- state.hooks = data;
- }
-
- state.getHooksComplete = true;
- this.setState(state);
- },
- (err) => {
- this.setState({serverError: err});
- }
- );
- }
- updateChannelId(e) {
- this.setState({channelId: e.target.value});
- }
- render() {
- let serverError;
- if (this.state.serverError) {
- serverError = <label className='has-error'>{this.state.serverError}</label>;
- }
-
- const channels = ChannelStore.getAll();
- const options = [];
- channels.forEach((channel) => {
- if (channel.type !== Constants.DM_CHANNEL) {
- options.push(
- <option
- key={'incoming-hook' + channel.id}
- value={channel.id}
- >
- {channel.display_name}
- </option>
- );
- }
- });
-
- let disableButton = '';
- if (this.state.channelId === '') {
- disableButton = ' disable';
- }
-
- const hooks = [];
- this.state.hooks.forEach((hook) => {
- const c = ChannelStore.get(hook.channel_id);
- if (c) {
- hooks.push(
- <div
- key={hook.id}
- className='webhook__item'
- >
- <div className='padding-top x2 webhook__url'>
- <strong>{'URL: '}</strong>
- <span className='word-break--all'>{Utils.getWindowLocationOrigin() + '/hooks/' + hook.id}</span>
- </div>
- <div className='padding-top'>
- <strong>
- <FormattedMessage
- id='user.settings.hooks_in.channel'
- defaultMessage='Channel: '
- />
- </strong>{c.display_name}
- </div>
- <a
- className={'webhook__remove'}
- href='#'
- onClick={this.removeHook.bind(this, hook.id)}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </a>
- <div className='padding-top x2 divider-light'></div>
- </div>
- );
- }
- });
-
- let displayHooks;
- if (!this.state.getHooksComplete) {
- displayHooks = <LoadingScreen/>;
- } else if (hooks.length > 0) {
- displayHooks = hooks;
- } else {
- displayHooks = (
- <div className='padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_in.none'
- defaultMessage='None'
- />
- </div>
- );
- }
-
- const existingHooks = (
- <div className='webhooks__container'>
- <label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_in.existing'
- defaultMessage='Existing incoming webhooks'
- />
- </label>
- <div className='padding-top divider-light'></div>
- <div className='webhooks__list'>
- {displayHooks}
- </div>
- </div>
- );
-
- return (
- <div key='addIncomingHook'>
- <FormattedHTMLMessage
- id='user.settings.hooks_in.description'
- defaultMessage='Create webhook URLs for use in external integrations. Please see <a href="http://docs.mattermost.com/developer/webhooks-incoming.html" target="_blank">incoming webhooks documentation</a> to learn more. View all incoming webhooks configured on this team below.'
- />
- <div><label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_in.addTitle'
- defaultMessage='Add a new incoming webhook'
- />
- </label></div>
- <div className='row padding-top'>
- <div className='col-sm-10 padding-bottom'>
- <select
- ref='channelName'
- className='form-control'
- value={this.state.channelId}
- onChange={this.updateChannelId}
- >
- {options}
- </select>
- {serverError}
- </div>
- <div className='col-sm-2 col-xs-4 no-padding--left padding-bottom'>
- <a
- className={'btn form-control no-padding btn-sm btn-primary' + disableButton}
- href='#'
- onClick={this.addNewHook}
- >
- <FormattedMessage
- id='user.settings.hooks_in.add'
- defaultMessage='Add'
- />
- </a>
- </div>
- </div>
- {existingHooks}
- </div>
- );
- }
-}
diff --git a/web/react/components/user_settings/manage_languages.jsx b/web/react/components/user_settings/manage_languages.jsx
deleted file mode 100644
index 6b00a65c7..000000000
--- a/web/react/components/user_settings/manage_languages.jsx
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMax from '../setting_item_max.jsx';
-
-import * as Client from '../../utils/client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ManageLanguage extends React.Component {
- constructor(props) {
- super(props);
-
- this.setupInitialState = this.setupInitialState.bind(this);
- this.setLanguage = this.setLanguage.bind(this);
- this.changeLanguage = this.changeLanguage.bind(this);
- this.submitUser = this.submitUser.bind(this);
- this.state = this.setupInitialState(props);
- }
- setupInitialState(props) {
- var user = props.user;
- return {
- languages: Utils.languages(),
- locale: user.locale
- };
- }
- setLanguage(e) {
- this.setState({locale: e.target.value});
- }
- changeLanguage(e) {
- e.preventDefault();
-
- var user = this.props.user;
- var locale = this.state.locale;
-
- user.locale = locale;
-
- this.submitUser(user);
- }
- submitUser(user) {
- Client.updateUser(user,
- () => {
- GlobalActions.newLocalizationSelected(user.locale);
- },
- (err) => {
- let serverError;
- if (err.message) {
- serverError = err.message;
- } else {
- serverError = err;
- }
- this.setState({serverError});
- }
- );
- }
- render() {
- let serverError;
- if (this.state.serverError) {
- serverError = <label className='has-error'>{this.state.serverError}</label>;
- }
-
- const options = [];
- this.state.languages.forEach((lang) => {
- options.push(
- <option
- key={lang.value}
- value={lang.value}
- >
- {lang.name}
- </option>);
- });
-
- const input = (
- <div key='changeLanguage'>
- <br/>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.languages.change'
- defaultMessage='Change interface language'
- />
- </label>
- <div className='padding-top'>
- <select
- ref='language'
- className='form-control'
- value={this.state.locale}
- onChange={this.setLanguage}
- >
- {options}
- </select>
- {serverError}
- </div>
- </div>
- );
-
- return (
- <SettingItemMax
- title={
- <FormattedMessage
- id='user.settings.display.language'
- defaultMessage='Language'
- />
- }
- width='medium'
- submit={this.changeLanguage}
- inputs={[input]}
- updateSection={this.props.updateSection}
- />
- );
- }
-}
-
-ManageLanguage.propTypes = {
- user: React.PropTypes.object.isRequired,
- updateSection: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx
deleted file mode 100644
index 487254d15..000000000
--- a/web/react/components/user_settings/manage_outgoing_hooks.jsx
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LoadingScreen from '../loading_screen.jsx';
-
-import ChannelStore from '../../stores/channel_store.jsx';
-
-import * as Client from '../../utils/client.jsx';
-import Constants from '../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
-
-const holders = defineMessages({
- optional: {
- id: 'user.settings.hooks_out.optional',
- defaultMessage: 'Optional if channel selected'
- },
- callbackHolder: {
- id: 'user.settings.hooks_out.callbackHolder',
- defaultMessage: 'Each URL must start with http:// or https://'
- },
- select: {
- id: 'user.settings.hooks_out.select',
- defaultMessage: '--- Select a channel ---'
- }
-});
-
-class ManageOutgoingHooks extends React.Component {
- constructor() {
- super();
-
- this.getHooks = this.getHooks.bind(this);
- this.addNewHook = this.addNewHook.bind(this);
- this.updateChannelId = this.updateChannelId.bind(this);
- this.updateTriggerWords = this.updateTriggerWords.bind(this);
- this.updateCallbackURLs = this.updateCallbackURLs.bind(this);
-
- this.state = {hooks: [], channelId: '', triggerWords: '', callbackURLs: '', getHooksComplete: false};
- }
- componentDidMount() {
- this.getHooks();
- }
- addNewHook(e) {
- e.preventDefault();
-
- if ((this.state.channelId === '' && this.state.triggerWords === '') ||
- this.state.callbackURLs === '') {
- return;
- }
-
- const hook = {};
- hook.channel_id = this.state.channelId;
- if (this.state.triggerWords.length !== 0) {
- hook.trigger_words = this.state.triggerWords.trim().split(',');
- }
- hook.callback_urls = this.state.callbackURLs.split('\n').map((url) => url.trim());
-
- Client.addOutgoingHook(
- hook,
- (data) => {
- let hooks = Object.assign([], this.state.hooks);
- if (!hooks) {
- hooks = [];
- }
- hooks.push(data);
- this.setState({hooks, addError: null, channelId: '', triggerWords: '', callbackURLs: ''});
- },
- (err) => {
- this.setState({addError: err.message});
- }
- );
- }
- removeHook(id) {
- const data = {};
- data.id = id;
-
- Client.deleteOutgoingHook(
- data,
- () => {
- const hooks = this.state.hooks;
- let index = -1;
- for (let i = 0; i < hooks.length; i++) {
- if (hooks[i].id === id) {
- index = i;
- break;
- }
- }
-
- if (index !== -1) {
- hooks.splice(index, 1);
- }
-
- this.setState({hooks});
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
- regenToken(id) {
- const regenData = {};
- regenData.id = id;
-
- Client.regenOutgoingHookToken(
- regenData,
- (data) => {
- const hooks = Object.assign([], this.state.hooks);
- for (let i = 0; i < hooks.length; i++) {
- if (hooks[i].id === id) {
- hooks[i] = data;
- break;
- }
- }
-
- this.setState({hooks, editError: null});
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
- getHooks() {
- Client.listOutgoingHooks(
- (data) => {
- if (data) {
- this.setState({hooks: data, getHooksComplete: true, editError: null});
- }
- },
- (err) => {
- this.setState({editError: err.message});
- }
- );
- }
- updateChannelId(e) {
- this.setState({channelId: e.target.value});
- }
- updateTriggerWords(e) {
- this.setState({triggerWords: e.target.value});
- }
- updateCallbackURLs(e) {
- this.setState({callbackURLs: e.target.value});
- }
- render() {
- let addError;
- if (this.state.addError) {
- addError = <label className='has-error'>{this.state.addError}</label>;
- }
- let editError;
- if (this.state.editError) {
- addError = <label className='has-error'>{this.state.editError}</label>;
- }
-
- const channels = ChannelStore.getAll();
- const options = [];
- options.push(
- <option
- key='select-channel'
- value=''
- >
- {this.props.intl.formatMessage(holders.select)}
- </option>
- );
-
- channels.forEach((channel) => {
- if (channel.type === Constants.OPEN_CHANNEL) {
- options.push(
- <option
- key={'outgoing-hook' + channel.id}
- value={channel.id}
- >
- {channel.display_name}
- </option>
- );
- }
- });
-
- const hooks = [];
- this.state.hooks.forEach((hook) => {
- const c = ChannelStore.get(hook.channel_id);
-
- if (!c && hook.channel_id && hook.channel_id.length !== 0) {
- return;
- }
-
- let channelDiv;
- if (c) {
- channelDiv = (
- <div className='padding-top'>
- <strong>
- <FormattedMessage
- id='user.settings.hooks_out.channel'
- defaultMessage='Channel: '
- />
- </strong>{c.display_name}
- </div>
- );
- }
-
- let triggerDiv;
- if (hook.trigger_words && hook.trigger_words.length !== 0) {
- triggerDiv = (
- <div className='padding-top'>
- <strong>
- <FormattedMessage
- id='user.settings.hooks_out.trigger'
- defaultMessage='Trigger Words: '
- />
- </strong>{hook.trigger_words.join(', ')}
- </div>
- );
- }
-
- hooks.push(
- <div
- key={hook.id}
- className='webhook__item'
- >
- <div className='padding-top x2 webhook__url'>
- <strong>{'URLs: '}</strong><span className='word-break--all'>{hook.callback_urls.join(', ')}</span>
- </div>
- {channelDiv}
- {triggerDiv}
- <div className='padding-top'>
- <strong>{'Token: '}</strong>{hook.token}
- </div>
- <div className='padding-top'>
- <a
- className='text-danger'
- href='#'
- onClick={this.regenToken.bind(this, hook.id)}
- >
- <FormattedMessage
- id='user.settings.hooks_out.regen'
- defaultMessage='Regen Token'
- />
- </a>
- <a
- className='webhook__remove'
- href='#'
- onClick={this.removeHook.bind(this, hook.id)}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </a>
- </div>
- <div className='padding-top x2 divider-light'></div>
- </div>
- );
- });
-
- let displayHooks;
- if (!this.state.getHooksComplete) {
- displayHooks = <LoadingScreen/>;
- } else if (hooks.length > 0) {
- displayHooks = hooks;
- } else {
- displayHooks = (
- <div className='padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_out.none'
- defaultMessage='None'
- />
- </div>
- );
- }
-
- const existingHooks = (
- <div className='webhooks__container'>
- <label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_out.existing'
- defaultMessage='Existing outgoing webhooks'
- />
- </label>
- <div className='padding-top divider-light'></div>
- <div className='webhooks__list'>
- {displayHooks}
- </div>
- </div>
- );
-
- const disableButton = (this.state.channelId === '' && this.state.triggerWords === '') || this.state.callbackURLs === '';
-
- return (
- <div key='addOutgoingHook'>
- <FormattedHTMLMessage
- id='user.settings.hooks_out.addDescription'
- defaultMessage='Create webhooks to send new message events to an external integration. Please see <a href="http://docs.mattermost.com/developer/webhooks-outgoing.html" target="_blank">outgoing webhooks documentation</a> to learn more. View all outgoing webhooks configured on this team below.'
- />
- <div><label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.hooks_out.addTitle'
- defaultMessage='Add a new outgoing webhook'
- />
- </label></div>
- <div className='padding-top divider-light'></div>
- <div className='padding-top'>
- <div>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.hooks_out.channel'
- defaultMessage='Channel: '
- />
- </label>
- <div className='padding-top'>
- <select
- ref='channelName'
- className='form-control'
- value={this.state.channelId}
- onChange={this.updateChannelId}
- >
- {options}
- </select>
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.hooks_out.only'
- defaultMessage='Only public channels can be used'
- />
- </div>
- </div>
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.hooks_out.trigger'
- defaultMessage='Trigger Words: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='triggerWords'
- className='form-control'
- value={this.state.triggerWords}
- onChange={this.updateTriggerWords}
- placeholder={this.props.intl.formatMessage(holders.optional)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.hooks_out.comma'
- defaultMessage='Comma separated words to trigger on'
- />
- </div>
- </div>
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.hooks_out.callback'
- defaultMessage='Callback URLs: '
- />
- </label>
- <div className='padding-top'>
- <textarea
- ref='callbackURLs'
- className='form-control no-resize'
- value={this.state.callbackURLs}
- resize={false}
- rows={3}
- onChange={this.updateCallbackURLs}
- placeholder={this.props.intl.formatMessage(holders.callbackHolder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.hooks_out.callbackDesc'
- defaultMessage='New line separated URLs that will receive the HTTP POST event'
- />
- </div>
- {addError}
- </div>
- <div className='padding-top padding-bottom'>
- <a
- className={'btn btn-sm btn-primary'}
- href='#'
- disabled={disableButton}
- onClick={this.addNewHook}
- >
- <FormattedMessage
- id='user.settings.hooks_out.add'
- defaultMessage='Add'
- />
- </a>
- </div>
- </div>
- {existingHooks}
- {editError}
- </div>
- );
- }
-}
-
-ManageOutgoingHooks.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(ManageOutgoingHooks);
diff --git a/web/react/components/user_settings/premade_theme_chooser.jsx b/web/react/components/user_settings/premade_theme_chooser.jsx
deleted file mode 100644
index 80ff8c4de..000000000
--- a/web/react/components/user_settings/premade_theme_chooser.jsx
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from '../../utils/utils.jsx';
-import Constants from '../../utils/constants.jsx';
-
-export default class PremadeThemeChooser extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- const theme = this.props.theme;
-
- const premadeThemes = [];
- for (const k in Constants.THEMES) {
- if (Constants.THEMES.hasOwnProperty(k)) {
- const premadeTheme = $.extend(true, {}, Constants.THEMES[k]);
-
- let activeClass = '';
- if (premadeTheme.type === theme.type) {
- activeClass = 'active';
- }
-
- premadeThemes.push(
- <div
- className='col-xs-6 col-sm-3 premade-themes'
- key={'premade-theme-key' + k}
- >
- <div
- className={activeClass}
- onClick={() => this.props.updateTheme(premadeTheme)}
- >
- <label>
- <img
- className='img-responsive'
- src={'/static/images/themes/' + premadeTheme.type.toLowerCase() + '.png'}
- />
- <div className='theme-label'>{Utils.toTitleCase(premadeTheme.type)}</div>
- </label>
- </div>
- </div>
- );
- }
- }
-
- return (
- <div className='row appearance-section'>
- {premadeThemes}
- </div>
- );
- }
-}
-
-PremadeThemeChooser.propTypes = {
- theme: React.PropTypes.object.isRequired,
- updateTheme: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx
deleted file mode 100644
index 4da51fa5f..000000000
--- a/web/react/components/user_settings/user_settings.jsx
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../../stores/user_store.jsx';
-import * as utils from '../../utils/utils.jsx';
-import NotificationsTab from './user_settings_notifications.jsx';
-import SecurityTab from './user_settings_security.jsx';
-import GeneralTab from './user_settings_general.jsx';
-import DeveloperTab from './user_settings_developer.jsx';
-import IntegrationsTab from './user_settings_integrations.jsx';
-import DisplayTab from './user_settings_display.jsx';
-import AdvancedTab from './user_settings_advanced.jsx';
-
-export default class UserSettings extends React.Component {
- constructor(props) {
- super(props);
-
- this.getActiveTab = this.getActiveTab.bind(this);
- this.onListenerChange = this.onListenerChange.bind(this);
-
- this.state = {user: UserStore.getCurrentUser()};
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onListenerChange);
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onListenerChange);
- }
-
- getActiveTab() {
- return this.refs.activeTab;
- }
-
- onListenerChange() {
- var user = UserStore.getCurrentUser();
- if (!utils.areObjectsEqual(this.state.user, user)) {
- this.setState({user});
- }
- }
-
- render() {
- if (this.props.activeTab === 'general') {
- return (
- <div>
- <GeneralTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- />
- </div>
- );
- } else if (this.props.activeTab === 'security') {
- return (
- <div>
- <SecurityTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- setEnforceFocus={this.props.setEnforceFocus}
- />
- </div>
- );
- } else if (this.props.activeTab === 'notifications') {
- return (
- <div>
- <NotificationsTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- />
- </div>
- );
- } else if (this.props.activeTab === 'developer') {
- return (
- <div>
- <DeveloperTab
- ref='activeTab'
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- />
- </div>
- );
- } else if (this.props.activeTab === 'integrations') {
- return (
- <div>
- <IntegrationsTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- />
- </div>
- );
- } else if (this.props.activeTab === 'display') {
- return (
- <div>
- <DisplayTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- setEnforceFocus={this.props.setEnforceFocus}
- setRequireConfirm={this.props.setRequireConfirm}
- />
- </div>
- );
- } else if (this.props.activeTab === 'advanced') {
- return (
- <div>
- <AdvancedTab
- ref='activeTab'
- user={this.state.user}
- activeSection={this.props.activeSection}
- updateSection={this.props.updateSection}
- updateTab={this.props.updateTab}
- closeModal={this.props.closeModal}
- collapseModal={this.props.collapseModal}
- />
- </div>
- );
- }
-
- return <div/>;
- }
-}
-
-UserSettings.propTypes = {
- activeTab: React.PropTypes.string,
- activeSection: React.PropTypes.string,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired,
- setEnforceFocus: React.PropTypes.func.isRequired,
- setRequireConfirm: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/user_settings/user_settings_advanced.jsx b/web/react/components/user_settings/user_settings_advanced.jsx
deleted file mode 100644
index cdaa5fd8a..000000000
--- a/web/react/components/user_settings/user_settings_advanced.jsx
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Client from '../../utils/client.jsx';
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import Constants from '../../utils/constants.jsx';
-import PreferenceStore from '../../stores/preference_store.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
-
-const holders = defineMessages({
- sendTitle: {
- id: 'user.settings.advance.sendTitle',
- defaultMessage: 'Send messages on Ctrl + Enter'
- },
- on: {
- id: 'user.settings.advance.on',
- defaultMessage: 'On'
- },
- off: {
- id: 'user.settings.advance.off',
- defaultMessage: 'Off'
- },
- preReleaseTitle: {
- id: 'user.settings.advance.preReleaseTitle',
- defaultMessage: 'Preview pre-release features'
- },
- feature: {
- id: 'user.settings.advance.feature',
- defaultMessage: ' Feature '
- },
- features: {
- id: 'user.settings.advance.features',
- defaultMessage: ' Features '
- },
- enabled: {
- id: 'user.settings.advance.enabled',
- defaultMessage: 'enabled'
- },
- MARKDOWN_PREVIEW: {
- id: 'user.settings.advance.markdown_preview',
- defaultMessage: 'Show markdown preview option in message input box'
- },
- EMBED_PREVIEW: {
- id: 'user.settings.advance.embed_preview',
- defaultMessage: 'Show preview snippet of links below message'
- },
- EMBED_TOGGLE: {
- id: 'user.settings.advance.embed_toggle',
- defaultMessage: 'Show toggle for all embed previews'
- }
-});
-
-class AdvancedSettingsDisplay extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
- this.updateSetting = this.updateSetting.bind(this);
- this.toggleFeature = this.toggleFeature.bind(this);
- this.saveEnabledFeatures = this.saveEnabledFeatures.bind(this);
-
- const preReleaseFeaturesKeys = Object.keys(PreReleaseFeatures);
- const advancedSettings = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS);
- const settings = {
- send_on_ctrl_enter: PreferenceStore.getPreference(
- Constants.Preferences.CATEGORY_ADVANCED_SETTINGS,
- 'send_on_ctrl_enter',
- {value: 'false'}
- ).value
- };
-
- let enabledFeatures = 0;
- advancedSettings.forEach((setting) => {
- preReleaseFeaturesKeys.forEach((key) => {
- const feature = PreReleaseFeatures[key];
- if (setting.name === Constants.FeatureTogglePrefix + feature.label) {
- settings[setting.name] = setting.value;
- if (setting.value === 'true') {
- enabledFeatures++;
- }
- }
- });
- });
-
- this.state = {preReleaseFeatures: PreReleaseFeatures, settings, preReleaseFeaturesKeys, enabledFeatures};
- }
-
- updateSetting(setting, value) {
- const settings = this.state.settings;
- settings[setting] = value;
- this.setState(settings);
- }
-
- toggleFeature(feature, checked) {
- const settings = this.state.settings;
- settings[Constants.FeatureTogglePrefix + feature] = String(checked);
-
- let enabledFeatures = 0;
- Object.keys(this.state.settings).forEach((setting) => {
- if (setting.lastIndexOf(Constants.FeatureTogglePrefix) === 0 && this.state.settings[setting] === 'true') {
- enabledFeatures++;
- }
- });
-
- this.setState({settings, enabledFeatures});
- }
-
- saveEnabledFeatures() {
- const features = [];
- Object.keys(this.state.settings).forEach((setting) => {
- if (setting.lastIndexOf(Constants.FeatureTogglePrefix) === 0) {
- features.push(setting);
- }
- });
-
- this.handleSubmit(features);
- }
-
- handleSubmit(settings) {
- const preferences = [];
-
- (Array.isArray(settings) ? settings : [settings]).forEach((setting) => {
- preferences.push(
- PreferenceStore.setPreference(
- Constants.Preferences.CATEGORY_ADVANCED_SETTINGS,
- setting,
- String(this.state.settings[setting])
- )
- );
- });
-
- Client.savePreferences(preferences,
- () => {
- PreferenceStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
-
- updateSection(section) {
- this.props.updateSection(section);
- }
-
- render() {
- const serverError = this.state.serverError || null;
- const {formatMessage} = this.props.intl;
- let ctrlSendSection;
-
- if (this.props.activeSection === 'advancedCtrlSend') {
- const ctrlSendActive = [
- this.state.settings.send_on_ctrl_enter === 'true',
- this.state.settings.send_on_ctrl_enter === 'false'
- ];
-
- const inputs = [
- <div key='ctrlSendSetting'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={ctrlSendActive[0]}
- onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'true')}
- />
- <FormattedMessage
- id='user.settings.advance.on'
- defaultMessage='On'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={ctrlSendActive[1]}
- onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'false')}
- />
- <FormattedMessage
- id='user.settings.advance.off'
- defaultMessage='Off'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='user.settings.advance.sendDesc'
- defaultMessage="If enabled 'Enter' inserts a new line and 'Ctrl + Enter' submits the message."
- />
- </div>
- </div>
- ];
-
- ctrlSendSection = (
- <SettingItemMax
- title={formatMessage(holders.sendTitle)}
- inputs={inputs}
- submit={() => this.handleSubmit('send_on_ctrl_enter')}
- server_error={serverError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- ctrlSendSection = (
- <SettingItemMin
- title={formatMessage(holders.sendTitle)}
- describe={this.state.settings.send_on_ctrl_enter === 'true' ? formatMessage(holders.on) : formatMessage(holders.off)}
- updateSection={() => this.props.updateSection('advancedCtrlSend')}
- />
- );
- }
-
- let previewFeaturesSection;
- let previewFeaturesSectionDivider;
- if (this.state.preReleaseFeaturesKeys.length > 0) {
- previewFeaturesSectionDivider = (
- <div className='divider-light'/>
- );
-
- if (this.props.activeSection === 'advancedPreviewFeatures') {
- const inputs = [];
-
- this.state.preReleaseFeaturesKeys.forEach((key) => {
- const feature = this.state.preReleaseFeatures[key];
- inputs.push(
- <div key={'advancedPreviewFeatures_' + feature.label}>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.settings[Constants.FeatureTogglePrefix + feature.label] === 'true'}
- onChange={(e) => {
- this.toggleFeature(feature.label, e.target.checked);
- }}
- />
- {formatMessage(holders[key])}
- </label>
- </div>
- </div>
- );
- });
-
- inputs.push(
- <div key='advancedPreviewFeatures_helptext'>
- <br/>
- <FormattedMessage
- id='user.settings.advance.preReleaseDesc'
- defaultMessage="Check any pre-released features you'd like to preview. You may also need to refresh the page before the setting will take effect."
- />
- </div>
- );
-
- previewFeaturesSection = (
- <SettingItemMax
- title={formatMessage(holders.preReleaseTitle)}
- inputs={inputs}
- submit={this.saveEnabledFeatures}
- server_error={serverError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- previewFeaturesSection = (
- <SettingItemMin
- title={formatMessage(holders.preReleaseTitle)}
- describe={this.state.enabledFeatures + (this.state.enabledFeatures === 1 ? formatMessage(holders.feature) : formatMessage(holders.features)) + formatMessage(holders.enabled)}
- updateSection={() => this.props.updateSection('advancedPreviewFeatures')}
- />
- );
- }
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.advance.title'
- defaultMessage='Advanced Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.advance.title'
- defaultMessage='Advanced Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {ctrlSendSection}
- {previewFeaturesSectionDivider}
- {previewFeaturesSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-AdvancedSettingsDisplay.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(AdvancedSettingsDisplay);
diff --git a/web/react/components/user_settings/user_settings_developer.jsx b/web/react/components/user_settings/user_settings_developer.jsx
deleted file mode 100644
index 1dd564c8d..000000000
--- a/web/react/components/user_settings/user_settings_developer.jsx
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- applicationsPreview: {
- id: 'user.settings.developer.applicationsPreview',
- defaultMessage: 'Applications (Preview)'
- },
- thirdParty: {
- id: 'user.settings.developer.thirdParty',
- defaultMessage: 'Open to register a new third-party application'
- }
-});
-
-class DeveloperTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.register = this.register.bind(this);
-
- this.state = {};
- }
- register() {
- this.props.closeModal();
- GlobalActions.showRegisterAppModal();
- }
- render() {
- var appSection;
- var self = this;
- const {formatMessage} = this.props.intl;
- if (this.props.activeSection === 'app') {
- var inputs = [];
-
- inputs.push(
- <div
- key='registerbtn'
- className='form-group'
- >
- <div className='col-sm-7'>
- <a
- className='btn btn-sm btn-primary'
- onClick={this.register}
- >
- <FormattedMessage
- id='user.settings.developer.register'
- defaultMessage='Register New Application'
- />
- </a>
- </div>
- </div>
- );
-
- appSection = (
- <SettingItemMax
- title={formatMessage(holders.applicationsPreview)}
- inputs={inputs}
- updateSection={function updateSection(e) {
- self.props.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- appSection = (
- <SettingItemMin
- title={formatMessage(holders.applicationsPreview)}
- describe={formatMessage(holders.thirdParty)}
- updateSection={function updateSection() {
- self.props.updateSection('app');
- }}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.developer.title'
- defaultMessage='Developer Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.developer.title'
- defaultMessage='Developer Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {appSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-DeveloperTab.defaultProps = {
- activeSection: ''
-};
-DeveloperTab.propTypes = {
- intl: intlShape.isRequired,
- activeSection: React.PropTypes.string,
- updateSection: React.PropTypes.func,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(DeveloperTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx
deleted file mode 100644
index b0e64b0aa..000000000
--- a/web/react/components/user_settings/user_settings_display.jsx
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import ManageLanguages from './manage_languages.jsx';
-import ThemeSetting from './user_settings_theme.jsx';
-
-import PreferenceStore from '../../stores/preference_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {savePreferences} from '../../utils/client.jsx';
-import {FormattedMessage} from 'mm-intl';
-
-function getDisplayStateFromStores() {
- const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'});
- const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'username'});
- const selectedFont = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', {value: Constants.DEFAULT_FONT});
-
- return {
- militaryTime: militaryTime.value,
- nameFormat: nameFormat.value,
- selectedFont: selectedFont.value
- };
-}
-
-export default class UserSettingsDisplay extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleClockRadio = this.handleClockRadio.bind(this);
- this.handleNameRadio = this.handleNameRadio.bind(this);
- this.handleFont = this.handleFont.bind(this);
- this.updateSection = this.updateSection.bind(this);
- this.updateState = this.updateState.bind(this);
- this.deactivate = this.deactivate.bind(this);
-
- this.state = getDisplayStateFromStores();
- }
- handleSubmit() {
- const timePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime);
- const namePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', this.state.nameFormat);
- const fontPreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', this.state.selectedFont);
-
- savePreferences([timePreference, namePreference, fontPreference],
- () => {
- PreferenceStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- this.setState({serverError: err.message});
- }
- );
- }
- handleClockRadio(militaryTime) {
- this.setState({militaryTime});
- }
- handleNameRadio(nameFormat) {
- this.setState({nameFormat});
- }
- handleFont(selectedFont) {
- Utils.applyFont(selectedFont);
- this.setState({selectedFont});
- }
- updateSection(section) {
- this.updateState();
- this.props.updateSection(section);
- }
- updateState() {
- const newState = getDisplayStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.handleFont(newState.selectedFont);
- this.setState(newState);
- }
- }
- deactivate() {
- this.updateState();
- }
- render() {
- const serverError = this.state.serverError || null;
- let clockSection;
- let nameFormatSection;
- let fontSection;
- let languagesSection;
-
- if (this.props.activeSection === 'clock') {
- const clockFormat = [false, false];
- if (this.state.militaryTime === 'true') {
- clockFormat[1] = true;
- } else {
- clockFormat[0] = true;
- }
-
- const handleUpdateClockSection = (e) => {
- this.updateSection('');
- e.preventDefault();
- };
-
- const inputs = [
- <div key='userDisplayClockOptions'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={clockFormat[0]}
- onChange={this.handleClockRadio.bind(this, 'false')}
- />
- <FormattedMessage
- id='user.settings.display.normalClock'
- defaultMessage='12-hour clock (example: 4:00 PM)'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={clockFormat[1]}
- onChange={this.handleClockRadio.bind(this, 'true')}
- />
- <FormattedMessage
- id='user.settings.display.militaryClock'
- defaultMessage='24-hour clock (example: 16:00)'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='user.settings.display.preferTime'
- defaultMessage='Select how you prefer time displayed.'
- />
- </div>
- </div>
- ];
-
- clockSection = (
- <SettingItemMax
- title={
- <FormattedMessage
- id='user.settings.display.clockDisplay'
- defaultMessage='Clock Display'
- />
- }
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={handleUpdateClockSection}
- />
- );
- } else {
- let describe;
- if (this.state.militaryTime === 'true') {
- describe = (
- <FormattedMessage
- id='user.settings.display.militaryClock'
- defaultMessage='24-hour clock (example: 16:00)'
- />
- );
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.display.normalClock'
- defaultMessage='12-hour clock (example: 4:00 PM)'
- />
- );
- }
-
- const handleUpdateClockSection = () => {
- this.props.updateSection('clock');
- };
-
- clockSection = (
- <SettingItemMin
- title={
- <FormattedMessage
- id='user.settings.display.clockDisplay'
- defaultMessage='Clock Display'
- />
- }
- describe={describe}
- updateSection={handleUpdateClockSection}
- />
- );
- }
-
- const showUsername = (
- <FormattedMessage
- id='user.settings.display.showUsername'
- defaultMessage='Show username (team default)'
- />
- );
- const showNickname = (
- <FormattedMessage
- id='user.settings.display.showNickname'
- defaultMessage='Show nickname if one exists, otherwise show first and last name'
- />
- );
- const showFullName = (
- <FormattedMessage
- id='user.settings.display.showFullname'
- defaultMessage='Show first and last name'
- />
- );
- if (this.props.activeSection === 'name_format') {
- const nameFormat = [false, false, false];
- if (this.state.nameFormat === 'nickname_full_name') {
- nameFormat[0] = true;
- } else if (this.state.nameFormat === 'full_name') {
- nameFormat[2] = true;
- } else {
- nameFormat[1] = true;
- }
-
- const inputs = [
- <div key='userDisplayNameOptions'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={nameFormat[1]}
- onChange={this.handleNameRadio.bind(this, 'username')}
- />
- {showUsername}
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={nameFormat[0]}
- onChange={this.handleNameRadio.bind(this, 'nickname_full_name')}
- />
- {showNickname}
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={nameFormat[2]}
- onChange={this.handleNameRadio.bind(this, 'full_name')}
- />
- {showFullName}
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='user.settings.display.nameOptsDesc'
- defaultMessage="Set how to display other user's names in posts and the Direct Messages list."
- />
- </div>
- </div>
- ];
-
- nameFormatSection = (
- <SettingItemMax
- title={
- <FormattedMessage
- id='user.settings.display.teammateDisplay'
- defaultMessage='Teammate Name Display'
- />
- }
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- let describe;
- if (this.state.nameFormat === 'username') {
- describe = (
- <FormattedMessage
- id='user.settings.display.showUsername'
- defaultMessage='Show username (team default)'
- />
- );
- } else if (this.state.nameFormat === 'full_name') {
- describe = (
- <FormattedMessage
- id='user.settings.display.showFullname'
- defaultMessage='Show first and last name'
- />
- );
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.display.showNickname'
- defaultMessage='Show nickname if one exists, otherwise show first and last name'
- />
- );
- }
-
- nameFormatSection = (
- <SettingItemMin
- title={
- <FormattedMessage
- id='user.settings.display.teammateDisplay'
- defaultMessage='Teammate Name Display'
- />
- }
- describe={describe}
- updateSection={() => {
- this.props.updateSection('name_format');
- }}
- />
- );
- }
-
- if (this.props.activeSection === 'font') {
- const options = [];
- Object.keys(Constants.FONTS).forEach((fontName, idx) => {
- const className = Constants.FONTS[fontName];
- options.push(
- <option
- key={'font_' + idx}
- value={fontName}
- className={className}
- >
- {fontName}
- </option>
- );
- });
-
- const inputs = [
- <div key='userDisplayNameOptions'>
- <div
- className='dropdown'
- >
- <select
- className='form-control'
- type='text'
- value={this.state.selectedFont}
- onChange={(e) => this.handleFont(e.target.value)}
- >
- {options}
- </select>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='user.settings.display.fontDesc'
- defaultMessage='Select the font displayed in the Mattermost user interface.'
- />
- </div>
- </div>
- ];
-
- fontSection = (
- <SettingItemMax
- title={
- <FormattedMessage
- id='user.settings.display.fontTitle'
- defaultMessage='Display Font'
- />
- }
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- fontSection = (
- <SettingItemMin
- title={
- <FormattedMessage
- id='user.settings.display.fontTitle'
- defaultMessage='Display Font'
- />
- }
- describe={this.state.selectedFont}
- updateSection={() => {
- this.props.updateSection('font');
- }}
- />
- );
- }
-
- if (this.props.activeSection === 'languages') {
- languagesSection = (
- <ManageLanguages
- user={this.props.user}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- var locale = 'English';
- Utils.languages().forEach((l) => {
- if (l.value === this.props.user.locale) {
- locale = l.name;
- }
- });
-
- languagesSection = (
- <SettingItemMin
- title={
- <FormattedMessage
- id='user.settings.display.language'
- defaultMessage='Language'
- />
- }
- width='medium'
- describe={locale}
- updateSection={() => {
- this.updateSection('languages');
- }}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.display.title'
- defaultMessage='Display Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.display.title'
- defaultMessage='Display Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- <ThemeSetting
- selected={this.props.activeSection === 'theme'}
- updateSection={this.updateSection}
- setRequireConfirm={this.props.setRequireConfirm}
- setEnforceFocus={this.props.setEnforceFocus}
- />
- <div className='divider-dark'/>
- {fontSection}
- <div className='divider-dark'/>
- {clockSection}
- <div className='divider-dark'/>
- {nameFormatSection}
- <div className='divider-dark'/>
- {languagesSection}
- </div>
- </div>
- );
- }
-}
-
-UserSettingsDisplay.propTypes = {
- user: React.PropTypes.object,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired,
- setRequireConfirm: React.PropTypes.func.isRequired,
- setEnforceFocus: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
deleted file mode 100644
index 235892819..000000000
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ /dev/null
@@ -1,815 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import SettingPicture from '../setting_picture.jsx';
-
-import UserStore from '../../stores/user_store.jsx';
-import ErrorStore from '../../stores/error_store.jsx';
-
-import * as Client from '../../utils/client.jsx';
-import Constants from '../../utils/constants.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl';
-
-const holders = defineMessages({
- usernameReserved: {
- id: 'user.settings.general.usernameReserved',
- defaultMessage: 'This username is reserved, please choose a new one.'
- },
- usernameRestrictions: {
- id: 'user.settings.general.usernameRestrictions',
- defaultMessage: "Username must begin with a letter, and contain between {min} to {max} lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."
- },
- validEmail: {
- id: 'user.settings.general.validEmail',
- defaultMessage: 'Please enter a valid email address'
- },
- emailMatch: {
- id: 'user.settings.general.emailMatch',
- defaultMessage: 'The new emails you entered do not match.'
- },
- checkEmail: {
- id: 'user.settings.general.checkEmail',
- defaultMessage: 'Check your email at {email} to verify the address.'
- },
- newAddress: {
- id: 'user.settings.general.newAddress',
- defaultMessage: 'New Address: {email}<br />Check your email to verify the above address.'
- },
- checkEmailNoAddress: {
- id: 'user.settings.general.checkEmailNoAddress',
- defaultMessage: 'Check your email to verify your new address'
- },
- loginGitlab: {
- id: 'user.settings.general.loginGitlab',
- defaultMessage: 'Log in done through GitLab'
- },
- validImage: {
- id: 'user.settings.general.validImage',
- defaultMessage: 'Only JPG or PNG images may be used for profile pictures'
- },
- imageTooLarge: {
- id: 'user.settings.general.imageTooLarge',
- defaultMessage: 'Unable to upload profile image. File is too large.'
- },
- uploadImage: {
- id: 'user.settings.general.uploadImage',
- defaultMessage: "Click 'Edit' to upload an image."
- },
- imageUpdated: {
- id: 'user.settings.general.imageUpdated',
- defaultMessage: 'Image last updated {date}'
- },
- fullName: {
- id: 'user.settings.general.fullName',
- defaultMessage: 'Full Name'
- },
- nickname: {
- id: 'user.settings.general.nickname',
- defaultMessage: 'Nickname'
- },
- username: {
- id: 'user.settings.general.username',
- defaultMessage: 'Username'
- },
- email: {
- id: 'user.settings.general.email',
- defaultMessage: 'Email'
- },
- profilePicture: {
- id: 'user.settings.general.profilePicture',
- defaultMessage: 'Profile Picture'
- },
- close: {
- id: 'user.settings.general.close',
- defaultMessage: 'Close'
- }
-});
-
-class UserSettingsGeneralTab extends React.Component {
- constructor(props) {
- super(props);
- this.submitActive = false;
-
- this.submitUsername = this.submitUsername.bind(this);
- this.submitNickname = this.submitNickname.bind(this);
- this.submitName = this.submitName.bind(this);
- this.submitEmail = this.submitEmail.bind(this);
- this.submitUser = this.submitUser.bind(this);
- this.submitPicture = this.submitPicture.bind(this);
-
- this.updateUsername = this.updateUsername.bind(this);
- this.updateFirstName = this.updateFirstName.bind(this);
- this.updateLastName = this.updateLastName.bind(this);
- this.updateNickname = this.updateNickname.bind(this);
- this.updateEmail = this.updateEmail.bind(this);
- this.updateConfirmEmail = this.updateConfirmEmail.bind(this);
- this.updatePicture = this.updatePicture.bind(this);
- this.updateSection = this.updateSection.bind(this);
-
- this.state = this.setupInitialState(props);
- }
- submitUsername(e) {
- e.preventDefault();
-
- const user = Object.assign({}, this.props.user);
- const username = this.state.username.trim().toLowerCase();
-
- const {formatMessage} = this.props.intl;
- const usernameError = Utils.isValidUsername(username);
- if (usernameError === 'Cannot use a reserved word as a username.') {
- this.setState({clientError: formatMessage(holders.usernameReserved)});
- return;
- } else if (usernameError) {
- this.setState({clientError: formatMessage(holders.usernameRestrictions, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
- return;
- }
-
- if (user.username === username) {
- this.updateSection('');
- return;
- }
-
- user.username = username;
-
- this.submitUser(user, false);
- }
- submitNickname(e) {
- e.preventDefault();
-
- const user = Object.assign({}, this.props.user);
- const nickname = this.state.nickname.trim();
-
- if (user.nickname === nickname) {
- this.updateSection('');
- return;
- }
-
- user.nickname = nickname;
-
- this.submitUser(user, false);
- }
- submitName(e) {
- e.preventDefault();
-
- const user = Object.assign({}, this.props.user);
- const firstName = this.state.firstName.trim();
- const lastName = this.state.lastName.trim();
-
- if (user.first_name === firstName && user.last_name === lastName) {
- this.updateSection('');
- return;
- }
-
- user.first_name = firstName;
- user.last_name = lastName;
-
- this.submitUser(user, false);
- }
- submitEmail(e) {
- e.preventDefault();
-
- const user = Object.assign({}, this.props.user);
- const email = this.state.email.trim().toLowerCase();
- const confirmEmail = this.state.confirmEmail.trim().toLowerCase();
-
- const {formatMessage} = this.props.intl;
- if (email === '' || !Utils.isEmail(email)) {
- this.setState({emailError: formatMessage(holders.validEmail), clientError: '', serverError: ''});
- return;
- }
-
- if (email !== confirmEmail) {
- this.setState({emailError: formatMessage(holders.emailMatch), clientError: '', serverError: ''});
- return;
- }
-
- if (user.email === email) {
- this.updateSection('');
- return;
- }
-
- user.email = email;
- this.submitUser(user, true);
- }
- submitUser(user, emailUpdated) {
- Client.updateUser(user,
- () => {
- this.updateSection('');
- AsyncClient.getMe();
- const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated;
-
- if (verificationEnabled) {
- ErrorStore.storeLastError({message: this.props.intl.formatMessage(holders.checkEmail, {email: user.email})});
- ErrorStore.emitChange();
- this.setState({emailChangeInProgress: true});
- }
- },
- (err) => {
- let serverError;
- if (err.message) {
- serverError = err.message;
- } else {
- serverError = err;
- }
- this.setState({serverError, emailError: '', clientError: ''});
- }
- );
- }
- submitPicture(e) {
- e.preventDefault();
-
- if (!this.state.picture) {
- return;
- }
-
- if (!this.submitActive) {
- return;
- }
-
- const {formatMessage} = this.props.intl;
- const picture = this.state.picture;
-
- if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') {
- this.setState({clientError: formatMessage(holders.validImage)});
- return;
- } else if (picture.size > Constants.MAX_FILE_SIZE) {
- this.setState({clientError: formatMessage(holders.imageTooLarge)});
- return;
- }
-
- var formData = new FormData();
- formData.append('image', picture, picture.name);
- this.setState({loadingPicture: true});
-
- Client.uploadProfileImage(formData,
- () => {
- this.submitActive = false;
- AsyncClient.getMe();
- window.location.reload();
- },
- (err) => {
- var state = this.setupInitialState(this.props);
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- updateUsername(e) {
- this.setState({username: e.target.value});
- }
- updateFirstName(e) {
- this.setState({firstName: e.target.value});
- }
- updateLastName(e) {
- this.setState({lastName: e.target.value});
- }
- updateNickname(e) {
- this.setState({nickname: e.target.value});
- }
- updateEmail(e) {
- this.setState({email: e.target.value});
- }
- updateConfirmEmail(e) {
- this.setState({confirmEmail: e.target.value});
- }
- updatePicture(e) {
- if (e.target.files && e.target.files[0]) {
- this.setState({picture: e.target.files[0]});
-
- this.submitActive = true;
- this.setState({clientError: null});
- } else {
- this.setState({picture: null});
- }
- }
- updateSection(section) {
- const emailChangeInProgress = this.state.emailChangeInProgress;
- this.setState(Object.assign({}, this.setupInitialState(this.props), {emailChangeInProgress, clientError: '', serverError: '', emailError: ''}));
- this.submitActive = false;
- this.props.updateSection(section);
- }
- setupInitialState(props) {
- const user = props.user;
-
- return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname,
- email: user.email, confirmEmail: '', picture: null, loadingPicture: false, emailChangeInProgress: false};
- }
- render() {
- const user = this.props.user;
- const {formatMessage, formatHTMLMessage} = this.props.intl;
-
- let clientError = null;
- if (this.state.clientError) {
- clientError = this.state.clientError;
- }
- let serverError = null;
- if (this.state.serverError) {
- serverError = this.state.serverError;
- }
- let emailError = null;
- if (this.state.emailError) {
- emailError = this.state.emailError;
- }
-
- let nameSection;
- const inputs = [];
-
- if (this.props.activeSection === 'name') {
- inputs.push(
- <div
- key='firstNameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.general.firstName'
- defaultMessage='First Name'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateFirstName}
- value={this.state.firstName}
- />
- </div>
- </div>
- );
-
- inputs.push(
- <div
- key='lastNameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.general.lastName'
- defaultMessage='Last Name'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateLastName}
- value={this.state.lastName}
- />
- </div>
- </div>
- );
-
- function notifClick(e) {
- e.preventDefault();
- this.updateSection('');
- this.props.updateTab('notifications');
- }
-
- const notifLink = (
- <a
- href='#'
- onClick={notifClick.bind(this)}
- >
- <FormattedMessage
- id='user.settings.general.notificationsLink'
- defaultMessage='Notifications'
- />
- </a>
- );
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.general.notificationsExtra'
- defaultMessage='By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.'
- values={{
- notify: (notifLink)
- }}
- />
- </span>
- );
-
- nameSection = (
- <SettingItemMax
- title={formatMessage(holders.fullName)}
- inputs={inputs}
- submit={this.submitName}
- server_error={serverError}
- client_error={clientError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- extraInfo={extraInfo}
- />
- );
- } else {
- let fullName = '';
-
- if (user.first_name && user.last_name) {
- fullName = user.first_name + ' ' + user.last_name;
- } else if (user.first_name) {
- fullName = user.first_name;
- } else if (user.last_name) {
- fullName = user.last_name;
- }
-
- nameSection = (
- <SettingItemMin
- title={formatMessage(holders.fullName)}
- describe={fullName}
- updateSection={() => {
- this.updateSection('name');
- }}
- />
- );
- }
-
- let nicknameSection;
- if (this.props.activeSection === 'nickname') {
- let nicknameLabel = (
- <FormattedMessage
- id='user.settings.general.nickname'
- defaultMessage='Nickname'
- />
- );
- if (Utils.isMobile()) {
- nicknameLabel = '';
- }
-
- inputs.push(
- <div
- key='nicknameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{nicknameLabel}</label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateNickname}
- value={this.state.nickname}
- />
- </div>
- </div>
- );
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.general.nicknameExtra'
- defaultMessage='Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'
- />
- </span>
- );
-
- nicknameSection = (
- <SettingItemMax
- title={formatMessage(holders.nickname)}
- inputs={inputs}
- submit={this.submitNickname}
- server_error={serverError}
- client_error={clientError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- extraInfo={extraInfo}
- />
- );
- } else {
- nicknameSection = (
- <SettingItemMin
- title={formatMessage(holders.nickname)}
- describe={UserStore.getCurrentUser().nickname}
- updateSection={() => {
- this.updateSection('nickname');
- }}
- />
- );
- }
-
- let usernameSection;
- if (this.props.activeSection === 'username') {
- let usernameLabel = (
- <FormattedMessage
- id='user.settings.general.username'
- defaultMessage='Username'
- />
- );
- if (Utils.isMobile()) {
- usernameLabel = '';
- }
-
- inputs.push(
- <div
- key='usernameSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{usernameLabel}</label>
- <div className='col-sm-7'>
- <input
- maxLength={Constants.MAX_USERNAME_LENGTH}
- className='form-control'
- type='text'
- onChange={this.updateUsername}
- value={this.state.username}
- />
- </div>
- </div>
- );
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.general.usernameInfo'
- defaultMessage='Pick something easy for teammates to recognize and recall.'
- />
- </span>
- );
-
- usernameSection = (
- <SettingItemMax
- title={formatMessage(holders.username)}
- inputs={inputs}
- submit={this.submitUsername}
- server_error={serverError}
- client_error={clientError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- extraInfo={extraInfo}
- />
- );
- } else {
- usernameSection = (
- <SettingItemMin
- title={formatMessage(holders.username)}
- describe={UserStore.getCurrentUser().username}
- updateSection={() => {
- this.updateSection('username');
- }}
- />
- );
- }
-
- let emailSection;
- if (this.props.activeSection === 'email') {
- const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
- const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
- let helpText = (
- <FormattedMessage
- id='user.settings.general.emailHelp1'
- defaultMessage='Email is used for sign-in, notifications, and password reset. Email requires verification if changed.'
- />
- );
-
- if (!emailEnabled) {
- helpText = (
- <div className='setting-list__hint text-danger'>
- <FormattedMessage
- id='user.settings.general.emailHelp2'
- defaultMessage='Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'
- />
- </div>
- );
- } else if (!emailVerificationEnabled) {
- helpText = (
- <FormattedMessage
- id='user.settings.general.emailHelp3'
- defaultMessage='Email is used for sign-in, notifications, and password reset.'
- />
- );
- } else if (this.state.emailChangeInProgress) {
- const newEmail = UserStore.getCurrentUser().email;
- if (newEmail) {
- helpText = (
- <FormattedMessage
- id='user.settings.general.emailHelp4'
- defaultMessage='A verification email was sent to {email}.'
- values={{
- email: newEmail
- }}
- />
- );
- }
- }
-
- let submit = null;
-
- if (this.props.user.auth_service === '') {
- inputs.push(
- <div key='emailSetting'>
- <div className='form-group'>
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.general.primaryEmail'
- defaultMessage='Primary Email'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateEmail}
- value={this.state.email}
- />
- </div>
- </div>
- </div>
- );
-
- inputs.push(
- <div key='confirmEmailSetting'>
- <div className='form-group'>
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.general.confirmEmail'
- defaultMessage='Confirm Email'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateConfirmEmail}
- value={this.state.confirmEmail}
- />
- </div>
- </div>
- {helpText}
- </div>
- );
-
- submit = this.submitEmail;
- } else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
- inputs.push(
- <div
- key='oauthEmailInfo'
- className='form-group'
- >
- <div className='setting-list__hint'>
- <FormattedMessage
- id='user.settings.general.emailCantUpdate'
- defaultMessage='Log in occurs through GitLab. Email cannot be updated.'
- />
- </div>
- {helpText}
- </div>
- );
- }
-
- emailSection = (
- <SettingItemMax
- title='Email'
- inputs={inputs}
- submit={submit}
- server_error={serverError}
- client_error={emailError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- let describe = '';
- if (this.props.user.auth_service === '') {
- if (this.state.emailChangeInProgress) {
- const newEmail = UserStore.getCurrentUser().email;
- if (newEmail) {
- describe = formatHTMLMessage(holders.newAddress, {email: newEmail});
- } else {
- describe = formatMessage(holders.checkEmailNoAddress);
- }
- } else {
- describe = UserStore.getCurrentUser().email;
- }
- } else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
- describe = formatMessage(holders.loginGitlab);
- }
-
- emailSection = (
- <SettingItemMin
- title={formatMessage(holders.email)}
- describe={describe}
- updateSection={() => {
- this.updateSection('email');
- }}
- />
- );
- }
-
- let pictureSection;
- if (this.props.activeSection === 'picture') {
- pictureSection = (
- <SettingPicture
- title={formatMessage(holders.profilePicture)}
- submit={this.submitPicture}
- src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update}
- server_error={serverError}
- client_error={clientError}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- picture={this.state.picture}
- pictureChange={this.updatePicture}
- submitActive={this.submitActive}
- loadingPicture={this.state.loadingPicture}
- />
- );
- } else {
- let minMessage = formatMessage(holders.uploadImage);
- if (user.last_picture_update) {
- minMessage = formatMessage(holders.imageUpdated, {
- date: (
- <FormattedDate
- value={new Date(user.last_picture_update)}
- day='2-digit'
- month='short'
- year='numeric'
- />
- )
- });
- }
- pictureSection = (
- <SettingItemMin
- title={formatMessage(holders.profilePicture)}
- describe={minMessage}
- updateSection={() => {
- this.updateSection('picture');
- }}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label={formatMessage(holders.close)}
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.general.title'
- defaultMessage='General Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.general.title'
- defaultMessage='General Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {nameSection}
- <div className='divider-light'/>
- {usernameSection}
- <div className='divider-light'/>
- {nicknameSection}
- <div className='divider-light'/>
- {emailSection}
- <div className='divider-light'/>
- {pictureSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-UserSettingsGeneralTab.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object.isRequired,
- updateSection: React.PropTypes.func.isRequired,
- updateTab: React.PropTypes.func.isRequired,
- activeSection: React.PropTypes.string.isRequired,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(UserSettingsGeneralTab);
diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx
deleted file mode 100644
index 7633b2f95..000000000
--- a/web/react/components/user_settings/user_settings_integrations.jsx
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import ManageIncomingHooks from './manage_incoming_hooks.jsx';
-import ManageOutgoingHooks from './manage_outgoing_hooks.jsx';
-import ManageCommandHooks from './manage_command_hooks.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- inName: {
- id: 'user.settings.integrations.incomingWebhooks',
- defaultMessage: 'Incoming Webhooks'
- },
- inDesc: {
- id: 'user.settings.integrations.incomingWebhooksDescription',
- defaultMessage: 'Manage your incoming webhooks'
- },
- outName: {
- id: 'user.settings.integrations.outWebhooks',
- defaultMessage: 'Outgoing Webhooks'
- },
- outDesc: {
- id: 'user.settings.integrations.outWebhooksDescription',
- defaultMessage: 'Manage your outgoing webhooks'
- },
- cmdName: {
- id: 'user.settings.integrations.commands',
- defaultMessage: 'Slash Commands'
- },
- cmdDesc: {
- id: 'user.settings.integrations.commandsDescription',
- defaultMessage: 'Manage your slash commands'
- }
-});
-
-class UserSettingsIntegrationsTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateSection = this.updateSection.bind(this);
-
- this.state = {};
- }
- updateSection(section) {
- this.props.updateSection(section);
- }
- render() {
- let incomingHooksSection;
- let outgoingHooksSection;
- let commandHooksSection;
- var inputs = [];
- const {formatMessage} = this.props.intl;
-
- if (global.window.mm_config.EnableIncomingWebhooks === 'true') {
- if (this.props.activeSection === 'incoming-hooks') {
- inputs.push(
- <ManageIncomingHooks key='incoming-hook-ui'/>
- );
-
- incomingHooksSection = (
- <SettingItemMax
- title={formatMessage(holders.inName)}
- width='medium'
- inputs={inputs}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- incomingHooksSection = (
- <SettingItemMin
- title={formatMessage(holders.inName)}
- width='medium'
- describe={formatMessage(holders.inDesc)}
- updateSection={() => {
- this.updateSection('incoming-hooks');
- }}
- />
- );
- }
- }
-
- if (global.window.mm_config.EnableOutgoingWebhooks === 'true') {
- if (this.props.activeSection === 'outgoing-hooks') {
- inputs.push(
- <ManageOutgoingHooks key='outgoing-hook-ui'/>
- );
-
- outgoingHooksSection = (
- <SettingItemMax
- title={formatMessage(holders.outName)}
- width='medium'
- inputs={inputs}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- outgoingHooksSection = (
- <SettingItemMin
- title={formatMessage(holders.outName)}
- width='medium'
- describe={formatMessage(holders.outDesc)}
- updateSection={() => {
- this.updateSection('outgoing-hooks');
- }}
- />
- );
- }
- }
-
- if (global.window.mm_config.EnableCommands === 'true') {
- if (this.props.activeSection === 'command-hooks') {
- inputs.push(
- <ManageCommandHooks key='command-hook-ui'/>
- );
-
- commandHooksSection = (
- <SettingItemMax
- title={formatMessage(holders.cmdName)}
- width='medium'
- inputs={inputs}
- updateSection={(e) => {
- this.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- commandHooksSection = (
- <SettingItemMin
- title={formatMessage(holders.cmdName)}
- width='medium'
- describe={formatMessage(holders.cmdDesc)}
- updateSection={() => {
- this.updateSection('command-hooks');
- }}
- />
- );
- }
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.integrations.title'
- defaultMessage='Integration Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.integrations.title'
- defaultMessage='Integration Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {incomingHooksSection}
- <div className='divider-light'/>
- {outgoingHooksSection}
- <div className='divider-dark'/>
- {commandHooksSection}
- <div className='divider-dark'/>
- </div>
- </div>
- );
- }
-}
-
-UserSettingsIntegrationsTab.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(UserSettingsIntegrationsTab); \ No newline at end of file
diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx
deleted file mode 100644
index 0c4a3d526..000000000
--- a/web/react/components/user_settings/user_settings_modal.jsx
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ConfirmModal from '../confirm_modal.jsx';
-import UserSettings from './user_settings.jsx';
-import SettingsSidebar from '../settings_sidebar.jsx';
-
-import UserStore from '../../stores/user_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import Constants from '../../utils/constants.jsx';
-
-const Modal = ReactBootstrap.Modal;
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const holders = defineMessages({
- general: {
- id: 'user.settings.modal.general',
- defaultMessage: 'General'
- },
- security: {
- id: 'user.settings.modal.security',
- defaultMessage: 'Security'
- },
- notifications: {
- id: 'user.settings.modal.notifications',
- defaultMessage: 'Notifications'
- },
- developer: {
- id: 'user.settings.modal.developer',
- defaultMessage: 'Developer'
- },
- integrations: {
- id: 'user.settings.modal.integrations',
- defaultMessage: 'Integrations'
- },
- display: {
- id: 'user.settings.modal.display',
- defaultMessage: 'Display'
- },
- advanced: {
- id: 'user.settings.modal.advanced',
- defaultMessage: 'Advanced'
- },
- confirmTitle: {
- id: 'user.settings.modal.confirmTitle',
- defaultMessage: 'Discard Changes?'
- },
- confirmMsg: {
- id: 'user.settings.modal.confirmMsg',
- defaultMessage: 'You have unsaved changes, are you sure you want to discard them?'
- },
- confirmBtns: {
- id: 'user.settings.modal.confirmBtns',
- defaultMessage: 'Yes, Discard'
- }
-});
-
-class UserSettingsModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleShow = this.handleShow.bind(this);
- this.handleHide = this.handleHide.bind(this);
- this.handleHidden = this.handleHidden.bind(this);
- this.handleCollapse = this.handleCollapse.bind(this);
- this.handleConfirm = this.handleConfirm.bind(this);
- this.handleCancelConfirmation = this.handleCancelConfirmation.bind(this);
-
- this.deactivateTab = this.deactivateTab.bind(this);
- this.closeModal = this.closeModal.bind(this);
- this.collapseModal = this.collapseModal.bind(this);
-
- this.updateTab = this.updateTab.bind(this);
- this.updateSection = this.updateSection.bind(this);
- this.onUserChanged = this.onUserChanged.bind(this);
-
- this.state = {
- active_tab: 'general',
- active_section: '',
- showConfirmModal: false,
- enforceFocus: true,
- currentUser: UserStore.getCurrentUser()
- };
-
- this.requireConfirm = false;
- }
-
- onUserChanged() {
- this.setState({currentUser: UserStore.getCurrentUser()});
- }
-
- componentDidMount() {
- if (this.props.show) {
- this.handleShow();
- }
- UserStore.addChangeListener(this.onUserChanged);
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.show && !prevProps.show) {
- this.handleShow();
- }
- UserStore.removeChangeListener(this.onUserChanged);
- }
-
- handleShow() {
- if ($(window).width() > 768) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
- } else {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
- }
- }
-
- // Called when the close button is pressed on the main modal
- handleHide() {
- if (this.requireConfirm) {
- this.afterConfirm = () => this.handleHide();
- this.showConfirmModal();
-
- return;
- }
-
- this.resetTheme();
- this.deactivateTab();
- this.props.onModalDismissed();
- return;
- }
-
- // called after the dialog is fully hidden and faded out
- handleHidden() {
- this.setState({
- active_tab: 'general',
- active_section: ''
- });
- }
-
- // Called to hide the settings pane when on mobile
- handleCollapse() {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).closest('.modal-dialog').removeClass('display--content');
-
- this.deactivateTab();
-
- this.setState({
- active_tab: '',
- active_section: ''
- });
- }
-
- handleConfirm() {
- this.setState({
- showConfirmModal: false,
- enforceFocus: true
- });
-
- this.requireConfirm = false;
-
- if (this.afterConfirm) {
- this.afterConfirm();
- this.afterConfirm = null;
- }
- }
-
- handleCancelConfirmation() {
- this.setState({
- showConfirmModal: false,
- enforceFocus: true
- });
-
- this.afterConfirm = null;
- }
-
- showConfirmModal(afterConfirm) {
- this.setState({
- showConfirmModal: true,
- enforceFocus: false
- });
-
- if (afterConfirm) {
- this.afterConfirm = afterConfirm;
- }
- }
-
- // Called to let settings tab perform cleanup before being closed
- deactivateTab() {
- const activeTab = this.refs.userSettings.getActiveTab();
- if (activeTab && activeTab.deactivate) {
- activeTab.deactivate();
- }
- }
-
- // Called by settings tabs when their close button is pressed
- closeModal() {
- if (this.requireConfirm) {
- this.showConfirmModal(this.closeModal);
- } else {
- this.handleHide();
- }
- }
-
- // Called by settings tabs when their back button is pressed
- collapseModal() {
- if (this.requireConfirm) {
- this.showConfirmModal(this.collapseModal);
- } else {
- this.handleCollapse();
- }
- }
-
- updateTab(tab, skipConfirm) {
- if (!skipConfirm && this.requireConfirm) {
- this.showConfirmModal(() => this.updateTab(tab, true));
- } else {
- this.deactivateTab();
-
- this.setState({
- active_tab: tab,
- active_section: ''
- });
- }
- }
-
- updateSection(section, skipConfirm) {
- if (!skipConfirm && this.requireConfirm) {
- this.showConfirmModal(() => this.updateSection(section, true));
- } else {
- if (this.state.active_section === 'theme' && section !== 'theme') {
- this.resetTheme();
- }
- this.setState({active_section: section});
- }
- }
-
- resetTheme() {
- const user = UserStore.getCurrentUser();
- if (user.theme_props == null) {
- Utils.applyTheme(Constants.THEMES.default);
- } else {
- Utils.applyTheme(user.theme_props);
- }
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- if (this.state.currentUser == null) {
- return (<div/>);
- }
- var isAdmin = Utils.isAdmin(this.state.currentUser.roles);
- var tabs = [];
-
- tabs.push({name: 'general', uiName: formatMessage(holders.general), icon: 'glyphicon glyphicon-cog'});
- tabs.push({name: 'security', uiName: formatMessage(holders.security), icon: 'glyphicon glyphicon-lock'});
- tabs.push({name: 'notifications', uiName: formatMessage(holders.notifications), icon: 'glyphicon glyphicon-exclamation-sign'});
- if (global.window.mm_config.EnableOAuthServiceProvider === 'true') {
- tabs.push({name: 'developer', uiName: formatMessage(holders.developer), icon: 'glyphicon glyphicon-th'});
- }
-
- if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true' || global.window.mm_config.EnableCommands === 'true') {
- var show = global.window.mm_config.EnableOnlyAdminIntegrations !== 'true';
-
- if (global.window.mm_config.EnableOnlyAdminIntegrations === 'true' && isAdmin) {
- show = true;
- }
-
- if (show) {
- tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'});
- }
- }
-
- tabs.push({name: 'display', uiName: formatMessage(holders.display), icon: 'glyphicon glyphicon-eye-open'});
- tabs.push({name: 'advanced', uiName: formatMessage(holders.advanced), icon: 'glyphicon glyphicon-list-alt'});
-
- return (
- <Modal
- dialogClassName='settings-modal'
- show={this.props.show}
- onHide={this.handleHide}
- onExited={this.handleHidden}
- enforceFocus={this.state.enforceFocus}
- >
- <Modal.Header closeButton={true}>
- <Modal.Title>
- <FormattedMessage
- id='user.settings.modal.title'
- defaultMessage='Account Settings'
- />
- </Modal.Title>
- </Modal.Header>
- <Modal.Body ref='modalBody'>
- <div className='settings-table'>
- <div className='settings-links'>
- <SettingsSidebar
- tabs={tabs}
- activeTab={this.state.active_tab}
- updateTab={this.updateTab}
- />
- </div>
- <div className='settings-content minimize-settings'>
- <UserSettings
- ref='userSettings'
- activeTab={this.state.active_tab}
- activeSection={this.state.active_section}
- updateSection={this.updateSection}
- updateTab={this.updateTab}
- closeModal={this.closeModal}
- collapseModal={this.collapseModal}
- setEnforceFocus={(enforceFocus) => this.setState({enforceFocus})}
- setRequireConfirm={
- (requireConfirm) => {
- this.requireConfirm = requireConfirm;
- return;
- }
- }
- />
- </div>
- </div>
- </Modal.Body>
- <ConfirmModal
- title={formatMessage(holders.confirmTitle)}
- message={formatMessage(holders.confirmMsg)}
- confirmButton={formatMessage(holders.confirmBtns)}
- show={this.state.showConfirmModal}
- onConfirm={this.handleConfirm}
- onCancel={this.handleCancelConfirmation}
- />
- </Modal>
- );
- }
-}
-
-UserSettingsModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(UserSettingsModal);
diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx
deleted file mode 100644
index 3ef6435f1..000000000
--- a/web/react/components/user_settings/user_settings_notifications.jsx
+++ /dev/null
@@ -1,831 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-
-import UserStore from '../../stores/user_store.jsx';
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-function getNotificationsStateFromStores() {
- var user = UserStore.getCurrentUser();
- var soundNeeded = !Utils.isBrowserFirefox();
-
- var sound = 'true';
- if (user.notify_props && user.notify_props.desktop_sound) {
- sound = user.notify_props.desktop_sound;
- }
- var desktop = 'default';
- if (user.notify_props && user.notify_props.desktop) {
- desktop = user.notify_props.desktop;
- }
- var email = 'true';
- if (user.notify_props && user.notify_props.email) {
- email = user.notify_props.email;
- }
-
- var usernameKey = false;
- var mentionKey = false;
- var customKeys = '';
- var firstNameKey = false;
- var allKey = false;
- var channelKey = false;
-
- if (user.notify_props) {
- if (user.notify_props.mention_keys) {
- var keys = user.notify_props.mention_keys.split(',');
-
- if (keys.indexOf(user.username) === -1) {
- usernameKey = false;
- } else {
- usernameKey = true;
- keys.splice(keys.indexOf(user.username), 1);
- }
-
- if (keys.indexOf('@' + user.username) === -1) {
- mentionKey = false;
- } else {
- mentionKey = true;
- keys.splice(keys.indexOf('@' + user.username), 1);
- }
-
- customKeys = keys.join(',');
- }
-
- if (user.notify_props.first_name) {
- firstNameKey = user.notify_props.first_name === 'true';
- }
-
- if (user.notify_props.all) {
- allKey = user.notify_props.all === 'true';
- }
-
- if (user.notify_props.channel) {
- channelKey = user.notify_props.channel === 'true';
- }
- }
-
- return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound,
- usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0,
- firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
-}
-
-const holders = defineMessages({
- desktop: {
- id: 'user.settings.notifications.desktop',
- defaultMessage: 'Send desktop notifications'
- },
- desktopSounds: {
- id: 'user.settings.notifications.desktopSounds',
- defaultMessage: 'Desktop notification sounds'
- },
- emailNotifications: {
- id: 'user.settings.notifications.emailNotifications',
- defaultMessage: 'Email notifications'
- },
- wordsTrigger: {
- id: 'user.settings.notifications.wordsTrigger',
- defaultMessage: 'Words that trigger mentions'
- },
- close: {
- id: 'user.settings.notifications.close',
- defaultMessage: 'Close'
- }
-});
-
-class NotificationsTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.handleCancel = this.handleCancel.bind(this);
- this.updateSection = this.updateSection.bind(this);
- this.updateState = this.updateState.bind(this);
- this.onListenerChange = this.onListenerChange.bind(this);
- this.handleNotifyRadio = this.handleNotifyRadio.bind(this);
- this.handleEmailRadio = this.handleEmailRadio.bind(this);
- this.handleSoundRadio = this.handleSoundRadio.bind(this);
- this.updateUsernameKey = this.updateUsernameKey.bind(this);
- this.updateMentionKey = this.updateMentionKey.bind(this);
- this.updateFirstNameKey = this.updateFirstNameKey.bind(this);
- this.updateAllKey = this.updateAllKey.bind(this);
- this.updateChannelKey = this.updateChannelKey.bind(this);
- this.updateCustomMentionKeys = this.updateCustomMentionKeys.bind(this);
- this.onCustomChange = this.onCustomChange.bind(this);
-
- this.state = getNotificationsStateFromStores();
- }
- handleSubmit() {
- var data = {};
- data.user_id = this.props.user.id;
- data.email = this.state.enableEmail;
- data.desktop_sound = this.state.enableSound;
- data.desktop = this.state.notifyLevel;
-
- var mentionKeys = [];
- if (this.state.usernameKey) {
- mentionKeys.push(this.props.user.username);
- }
- if (this.state.mentionKey) {
- mentionKeys.push('@' + this.props.user.username);
- }
-
- var stringKeys = mentionKeys.join(',');
- if (this.state.customKeys.length > 0 && this.state.customKeysChecked) {
- stringKeys += ',' + this.state.customKeys;
- }
-
- data.mention_keys = stringKeys;
- data.first_name = this.state.firstNameKey.toString();
- data.all = this.state.allKey.toString();
- data.channel = this.state.channelKey.toString();
-
- Client.updateUserNotifyProps(data,
- function success() {
- this.props.updateSection('');
- AsyncClient.getMe();
- }.bind(this),
- function failure(err) {
- this.setState({serverError: err.message});
- }.bind(this)
- );
- }
- handleCancel(e) {
- this.updateState();
- this.props.updateSection('');
- e.preventDefault();
- }
- updateSection(section) {
- this.updateState();
- this.props.updateSection(section);
- }
- updateState() {
- const newState = getNotificationsStateFromStores();
- if (!Utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
- componentDidMount() {
- UserStore.addChangeListener(this.onListenerChange);
- }
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onListenerChange);
- }
- onListenerChange() {
- this.updateState();
- }
- handleNotifyRadio(notifyLevel) {
- this.setState({notifyLevel: notifyLevel});
- ReactDOM.findDOMNode(this.refs.wrapper).focus();
- }
- handleEmailRadio(enableEmail) {
- this.setState({enableEmail: enableEmail});
- ReactDOM.findDOMNode(this.refs.wrapper).focus();
- }
- handleSoundRadio(enableSound) {
- this.setState({enableSound: enableSound});
- ReactDOM.findDOMNode(this.refs.wrapper).focus();
- }
- updateUsernameKey(val) {
- this.setState({usernameKey: val});
- }
- updateMentionKey(val) {
- this.setState({mentionKey: val});
- }
- updateFirstNameKey(val) {
- this.setState({firstNameKey: val});
- }
- updateAllKey(val) {
- this.setState({allKey: val});
- }
- updateChannelKey(val) {
- this.setState({channelKey: val});
- }
- updateCustomMentionKeys() {
- var checked = ReactDOM.findDOMNode(this.refs.customcheck).checked;
-
- if (checked) {
- var text = ReactDOM.findDOMNode(this.refs.custommentions).value;
-
- // remove all spaces and split string into individual keys
- this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true});
- } else {
- this.setState({customKeys: '', customKeysChecked: false});
- }
- }
- onCustomChange() {
- ReactDOM.findDOMNode(this.refs.customcheck).checked = true;
- this.updateCustomMentionKeys();
- }
- render() {
- const {formatMessage} = this.props.intl;
- var serverError = null;
- if (this.state.serverError) {
- serverError = this.state.serverError;
- }
-
- var user = this.props.user;
-
- var desktopSection;
- var handleUpdateDesktopSection;
- if (this.props.activeSection === 'desktop') {
- var notifyActive = [false, false, false];
- if (this.state.notifyLevel === 'mention') {
- notifyActive[1] = true;
- } else if (this.state.notifyLevel === 'none') {
- notifyActive[2] = true;
- } else {
- notifyActive[0] = true;
- }
-
- let inputs = [];
-
- inputs.push(
- <div key='userNotificationLevelOption'>
- <div className='radio'>
- <label>
- <input type='radio'
- checked={notifyActive[0]}
- onChange={this.handleNotifyRadio.bind(this, 'all')}
- />
- <FormattedMessage
- id='user.settings.notification.allActivity'
- defaultMessage='For all activity'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[1]}
- onChange={this.handleNotifyRadio.bind(this, 'mention')}
- />
- <FormattedMessage
- id='user.settings.notifications.onlyMentions'
- defaultMessage='Only for mentions and direct messages'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={notifyActive[2]}
- onChange={this.handleNotifyRadio.bind(this, 'none')}
- />
- <FormattedMessage
- id='user.settings.notifications.never'
- defaultMessage='Never'
- />
- </label>
- </div>
- </div>
- );
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.notifications.info'
- defaultMessage='Desktop notifications are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
- />
- </span>
- );
-
- desktopSection = (
- <SettingItemMax
- title={formatMessage(holders.desktop)}
- extraInfo={extraInfo}
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={this.handleCancel}
- />
- );
- } else {
- let describe = '';
- if (this.state.notifyLevel === 'mention') {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.onlyMentions'
- defaultMessage='Only for mentions and direct messages'
- />
- );
- } else if (this.state.notifyLevel === 'none') {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.never'
- defaultMessage='Never'
- />
- );
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.notification.allActivity'
- defaultMessage='For all activity'
- />
- );
- }
-
- handleUpdateDesktopSection = function updateDesktopSection() {
- this.props.updateSection('desktop');
- }.bind(this);
-
- desktopSection = (
- <SettingItemMin
- title={formatMessage(holders.desktop)}
- describe={describe}
- updateSection={handleUpdateDesktopSection}
- />
- );
- }
-
- var soundSection;
- var handleUpdateSoundSection;
- if (this.props.activeSection === 'sound' && this.state.soundNeeded) {
- var soundActive = [false, false];
- if (this.state.enableSound === 'false') {
- soundActive[1] = true;
- } else {
- soundActive[0] = true;
- }
-
- let inputs = [];
-
- inputs.push(
- <div key='userNotificationSoundOptions'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={soundActive[0]}
- onChange={this.handleSoundRadio.bind(this, 'true')}
- />
- <FormattedMessage
- id='user.settings.notifications.on'
- defaultMessage='On'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={soundActive[1]}
- onChange={this.handleSoundRadio.bind(this, 'false')}
- />
- <FormattedMessage
- id='user.settings.notifications.off'
- defaultMessage='Off'
- />
- </label>
- <br/>
- </div>
- </div>
- );
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.notifications.sounds_info'
- defaultMessage='Desktop notifications sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.'
- />
- </span>
- );
-
- soundSection = (
- <SettingItemMax
- title={formatMessage(holders.desktopSounds)}
- extraInfo={extraInfo}
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={this.handleCancel}
- />
- );
- } else {
- let describe = '';
- if (!this.state.soundNeeded) {
- describe = (
- <FormattedMessage
- id='user.settings.notification.soundConfig'
- defaultMessage='Please configure notification sounds in your browser settings'
- />
- );
- } else if (this.state.enableSound === 'false') {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.off'
- defaultMessage='Off'
- />
- );
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.on'
- defaultMessage='On'
- />
- );
- }
-
- handleUpdateSoundSection = function updateSoundSection() {
- this.props.updateSection('sound');
- }.bind(this);
-
- soundSection = (
- <SettingItemMin
- title={formatMessage(holders.desktopSounds)}
- describe={describe}
- updateSection={handleUpdateSoundSection}
- disableOpen={!this.state.soundNeeded}
- />
- );
- }
-
- var emailSection;
- var handleUpdateEmailSection;
- if (this.props.activeSection === 'email') {
- var emailActive = [false, false];
- if (this.state.enableEmail === 'false') {
- emailActive[1] = true;
- } else {
- emailActive[0] = true;
- }
-
- let inputs = [];
-
- inputs.push(
- <div key='userNotificationEmailOptions'>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={emailActive[0]}
- onChange={this.handleEmailRadio.bind(this, 'true')}
- />
- <FormattedMessage
- id='user.settings.notifications.on'
- defaultMessage='On'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- type='radio'
- checked={emailActive[1]}
- onChange={this.handleEmailRadio.bind(this, 'false')}
- />
- <FormattedMessage
- id='user.settings.notifications.off'
- defaultMessage='Off'
- />
- </label>
- <br/>
- </div>
- <div><br/>
- <FormattedMessage
- id='user.settings.notifications.emailInfo'
- defaultMessage='Email notifications are sent for mentions and direct messages after youā€™ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </div>
- </div>
- );
-
- emailSection = (
- <SettingItemMax
- title={formatMessage(holders.emailNotifications)}
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={this.handleCancel}
- />
- );
- } else {
- let describe = '';
- if (this.state.enableEmail === 'false') {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.off'
- defaultMessage='Off'
- />
- );
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.on'
- defaultMessage='On'
- />
- );
- }
-
- handleUpdateEmailSection = function updateEmailSection() {
- this.props.updateSection('email');
- }.bind(this);
-
- emailSection = (
- <SettingItemMin
- title={formatMessage(holders.emailNotifications)}
- describe={describe}
- updateSection={handleUpdateEmailSection}
- />
- );
- }
-
- var keysSection;
- var handleUpdateKeysSection;
- if (this.props.activeSection === 'keys') {
- let inputs = [];
-
- let handleUpdateFirstNameKey;
- let handleUpdateUsernameKey;
- let handleUpdateMentionKey;
- let handleUpdateAllKey;
- let handleUpdateChannelKey;
-
- if (user.first_name) {
- handleUpdateFirstNameKey = function handleFirstNameKeyChange(e) {
- this.updateFirstNameKey(e.target.checked);
- }.bind(this);
- inputs.push(
- <div key='userNotificationFirstNameOption'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.firstNameKey}
- onChange={handleUpdateFirstNameKey}
- />
- <FormattedMessage
- id='user.settings.notifications.sensitiveName'
- defaultMessage='Your case sensitive first name "{first_name}"'
- values={{
- first_name: user.first_name
- }}
- />
- </label>
- </div>
- </div>
- );
- }
-
- handleUpdateUsernameKey = function handleUsernameKeyChange(e) {
- this.updateUsernameKey(e.target.checked);
- }.bind(this);
- inputs.push(
- <div key='userNotificationUsernameOption'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.usernameKey}
- onChange={handleUpdateUsernameKey}
- />
- <FormattedMessage
- id='user.settings.notifications.sensitiveUsername'
- defaultMessage='Your non-case sensitive username "{username}"'
- values={{
- username: user.username
- }}
- />
- </label>
- </div>
- </div>
- );
-
- handleUpdateMentionKey = function handleMentionKeyChange(e) {
- this.updateMentionKey(e.target.checked);
- }.bind(this);
- inputs.push(
- <div key='userNotificationMentionOption'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.mentionKey}
- onChange={handleUpdateMentionKey}
- />
- <FormattedMessage
- id='user.settings.notifications.usernameMention'
- defaultMessage='Your username mentioned "@{username}"'
- values={{
- username: user.username
- }}
- />
- </label>
- </div>
- </div>
- );
-
- handleUpdateAllKey = function handleAllKeyChange(e) {
- this.updateAllKey(e.target.checked);
- }.bind(this);
- inputs.push(
- <div key='userNotificationAllOption'>
- <div className='checkbox hidden'>
- <label>
- <input
- type='checkbox'
- checked={this.state.allKey}
- onChange={handleUpdateAllKey}
- />
- <FormattedMessage
- id='user.settings.notifications.teamWide'
- defaultMessage='Team-wide mentions "@all"'
- />
- </label>
- </div>
- </div>
- );
-
- handleUpdateChannelKey = function handleChannelKeyChange(e) {
- this.updateChannelKey(e.target.checked);
- }.bind(this);
- inputs.push(
- <div key='userNotificationChannelOption'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.channelKey}
- onChange={handleUpdateChannelKey}
- />
- <FormattedMessage
- id='user.settings.notifications.channelWide'
- defaultMessage='Channel-wide mentions "@channel"'
- />
- </label>
- </div>
- </div>
- );
-
- inputs.push(
- <div key='userNotificationCustomOption'>
- <div className='checkbox'>
- <label>
- <input
- ref='customcheck'
- type='checkbox'
- checked={this.state.customKeysChecked}
- onChange={this.updateCustomMentionKeys}
- />
- <FormattedMessage
- id='user.settings.notifications.sensitiveWords'
- defaultMessage='Other non-case sensitive words, separated by commas:'
- />
- </label>
- </div>
- <input
- ref='custommentions'
- className='form-control mentions-input'
- type='text'
- defaultValue={this.state.customKeys}
- onChange={this.onCustomChange}
- />
- </div>
- );
-
- keysSection = (
- <SettingItemMax
- title={formatMessage(holders.wordsTrigger)}
- inputs={inputs}
- submit={this.handleSubmit}
- server_error={serverError}
- updateSection={this.handleCancel}
- />
- );
- } else {
- let keys = [];
- if (this.state.firstNameKey) {
- keys.push(user.first_name);
- }
- if (this.state.usernameKey) {
- keys.push(user.username);
- }
- if (this.state.mentionKey) {
- keys.push('@' + user.username);
- }
-
- // if (this.state.allKey) {
- // keys.push('@all');
- // }
-
- if (this.state.channelKey) {
- keys.push('@channel');
- }
- if (this.state.customKeys.length > 0) {
- keys = keys.concat(this.state.customKeys.split(','));
- }
-
- let describe = '';
- for (var i = 0; i < keys.length; i++) {
- describe += '"' + keys[i] + '", ';
- }
-
- if (describe.length > 0) {
- describe = describe.substring(0, describe.length - 2);
- } else {
- describe = (
- <FormattedMessage
- id='user.settings.notifications.noWords'
- defaultMessage='No words configured'
- />
- );
- }
-
- handleUpdateKeysSection = function updateKeysSection() {
- this.props.updateSection('keys');
- }.bind(this);
-
- keysSection = (
- <SettingItemMin
- title={formatMessage(holders.wordsTrigger)}
- describe={describe}
- updateSection={handleUpdateKeysSection}
- />
- );
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label={formatMessage(holders.close)}
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.notifications.title'
- defaultMessage='Notification Settings'
- />
- </h4>
- </div>
- <div
- ref='wrapper'
- className='user-settings'
- >
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.notifications.header'
- defaultMessage='Notifications'
- />
- </h3>
- <div className='divider-dark first'/>
- {desktopSection}
- <div className='divider-light'/>
- {soundSection}
- <div className='divider-light'/>
- {emailSection}
- <div className='divider-light'/>
- {keysSection}
- <div className='divider-dark'/>
- </div>
- </div>
-
- );
- }
-}
-
-NotificationsTab.defaultProps = {
- user: null,
- activeSection: '',
- activeTab: ''
-};
-NotificationsTab.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- activeSection: React.PropTypes.string,
- activeTab: React.PropTypes.string,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(NotificationsTab);
diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx
deleted file mode 100644
index 0b6b6c398..000000000
--- a/web/react/components/user_settings/user_settings_security.jsx
+++ /dev/null
@@ -1,472 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-import AccessHistoryModal from '../access_history_modal.jsx';
-import ActivityLogModal from '../activity_log_modal.jsx';
-import ToggleModalButton from '../toggle_modal_button.jsx';
-
-import TeamStore from '../../stores/team_store.jsx';
-
-import * as Client from '../../utils/client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import Constants from '../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedTime, FormattedDate} from 'mm-intl';
-
-const holders = defineMessages({
- currentPasswordError: {
- id: 'user.settings.security.currentPasswordError',
- defaultMessage: 'Please enter your current password'
- },
- passwordLengthError: {
- id: 'user.settings.security.passwordLengthError',
- defaultMessage: 'New passwords must be at least {chars} characters'
- },
- passwordMatchError: {
- id: 'user.settings.security.passwordMatchError',
- defaultMessage: 'The new passwords you entered do not match'
- },
- password: {
- id: 'user.settings.security.password',
- defaultMessage: 'Password'
- },
- lastUpdated: {
- id: 'user.settings.security.lastUpdated',
- defaultMessage: 'Last updated {date} at {time}'
- },
- method: {
- id: 'user.settings.security.method',
- defaultMessage: 'Sign-in Method'
- },
- close: {
- id: 'user.settings.security.close',
- defaultMessage: 'Close'
- }
-});
-
-class SecurityTab extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitPassword = this.submitPassword.bind(this);
- this.updateCurrentPassword = this.updateCurrentPassword.bind(this);
- this.updateNewPassword = this.updateNewPassword.bind(this);
- this.updateConfirmPassword = this.updateConfirmPassword.bind(this);
- this.getDefaultState = this.getDefaultState.bind(this);
- this.createPasswordSection = this.createPasswordSection.bind(this);
- this.createSignInSection = this.createSignInSection.bind(this);
-
- this.state = this.getDefaultState();
- }
- getDefaultState() {
- return {
- currentPassword: '',
- newPassword: '',
- confirmPassword: '',
- authService: this.props.user.auth_service
- };
- }
- submitPassword(e) {
- e.preventDefault();
-
- var user = this.props.user;
- var currentPassword = this.state.currentPassword;
- var newPassword = this.state.newPassword;
- var confirmPassword = this.state.confirmPassword;
-
- const {formatMessage} = this.props.intl;
- if (currentPassword === '') {
- this.setState({passwordError: formatMessage(holders.currentPasswordError), serverError: ''});
- return;
- }
-
- if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({passwordError: formatMessage(holders.passwordLengthError, {chars: Constants.MIN_PASSWORD_LENGTH}), serverError: ''});
- return;
- }
-
- if (newPassword !== confirmPassword) {
- var defaultState = Object.assign(this.getDefaultState(), {passwordError: formatMessage(holders.passwordMatchError), serverError: ''});
- this.setState(defaultState);
- return;
- }
-
- var data = {};
- data.user_id = user.id;
- data.current_password = currentPassword;
- data.new_password = newPassword;
-
- Client.updatePassword(data,
- () => {
- this.props.updateSection('');
- AsyncClient.getMe();
- this.setState(this.getDefaultState());
- },
- (err) => {
- var state = this.getDefaultState();
- if (err.message) {
- state.serverError = err.message;
- } else {
- state.serverError = err;
- }
- state.passwordError = '';
- this.setState(state);
- }
- );
- }
- updateCurrentPassword(e) {
- this.setState({currentPassword: e.target.value});
- }
- updateNewPassword(e) {
- this.setState({newPassword: e.target.value});
- }
- updateConfirmPassword(e) {
- this.setState({confirmPassword: e.target.value});
- }
- createPasswordSection() {
- let updateSectionStatus;
- const {formatMessage} = this.props.intl;
-
- if (this.props.activeSection === 'password' && this.props.user.auth_service === '') {
- const inputs = [];
-
- inputs.push(
- <div
- key='currentPasswordUpdateForm'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.security.currentPassword'
- defaultMessage='Current Password'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='password'
- onChange={this.updateCurrentPassword}
- value={this.state.currentPassword}
- />
- </div>
- </div>
- );
- inputs.push(
- <div
- key='newPasswordUpdateForm'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.security.newPassword'
- defaultMessage='New Password'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='password'
- onChange={this.updateNewPassword}
- value={this.state.newPassword}
- />
- </div>
- </div>
- );
- inputs.push(
- <div
- key='retypeNewPasswordUpdateForm'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='user.settings.security.retypePassword'
- defaultMessage='Retype New Password'
- />
- </label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='password'
- onChange={this.updateConfirmPassword}
- value={this.state.confirmPassword}
- />
- </div>
- </div>
- );
-
- updateSectionStatus = function resetSection(e) {
- this.props.updateSection('');
- this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null});
- e.preventDefault();
- }.bind(this);
-
- return (
- <SettingItemMax
- title={formatMessage(holders.password)}
- inputs={inputs}
- submit={this.submitPassword}
- server_error={this.state.serverError}
- client_error={this.state.passwordError}
- updateSection={updateSectionStatus}
- />
- );
- }
-
- var describe;
- var d = new Date(this.props.user.last_password_update);
-
- const hours12 = !Utils.isMilitaryTime();
- describe = formatMessage(holders.lastUpdated, {
- date: (
- <FormattedDate
- value={d}
- day='2-digit'
- month='short'
- year='numeric'
- />
- ),
- time: (
- <FormattedTime
- value={d}
- hour12={hours12}
- hour='2-digit'
- minute='2-digit'
- />
- )
- });
-
- updateSectionStatus = function updateSection() {
- this.props.updateSection('password');
- }.bind(this);
-
- return (
- <SettingItemMin
- title={formatMessage(holders.password)}
- describe={describe}
- updateSection={updateSectionStatus}
- />
- );
- }
- createSignInSection() {
- let updateSectionStatus;
- const user = this.props.user;
-
- if (this.props.activeSection === 'signin') {
- const inputs = [];
- const teamName = TeamStore.getCurrent().name;
-
- let emailOption;
- if (global.window.mm_config.EnableSignUpWithEmail === 'true' && user.auth_service !== '') {
- emailOption = (
- <div>
- <a
- className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service}
- >
- <FormattedMessage
- id='user.settings.security.switchEmail'
- defaultMessage='Switch to using email and password'
- />
- </a>
- <br/>
- </div>
- );
- }
-
- let gitlabOption;
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true' && user.auth_service === '') {
- gitlabOption = (
- <div>
- <a
- className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
- >
- <FormattedMessage
- id='user.settings.security.switchGitlab'
- defaultMessage='Switch to using GitLab SSO'
- />
- </a>
- <br/>
- </div>
- );
- }
-
- let googleOption;
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true' && user.auth_service === '') {
- googleOption = (
- <div>
- <a
- className='btn btn-primary'
- href={'/' + teamName + '/claim?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GOOGLE_SERVICE}
- >
- <FormattedMessage
- id='user.settings.security.switchGoogle'
- defaultMessage='Switch to using Google SSO'
- />
- </a>
- <br/>
- </div>
- );
- }
-
- inputs.push(
- <div key='userSignInOption'>
- {emailOption}
- {gitlabOption}
- <br/>
- {googleOption}
- </div>
- );
-
- updateSectionStatus = function updateSection(e) {
- this.props.updateSection('');
- this.setState({serverError: null});
- e.preventDefault();
- }.bind(this);
-
- const extraInfo = (
- <span>
- <FormattedMessage
- id='user.settings.security.oneSignin'
- defaultMessage='You may only have one sign-in method at a time. Switching sign-in method will send an email notifying you if the change was successful.'
- />
- </span>
- );
-
- return (
- <SettingItemMax
- title={this.props.intl.formatMessage(holders.method)}
- extraInfo={extraInfo}
- inputs={inputs}
- server_error={this.state.serverError}
- updateSection={updateSectionStatus}
- />
- );
- }
-
- updateSectionStatus = function updateSection() {
- this.props.updateSection('signin');
- }.bind(this);
-
- let describe = (
- <FormattedMessage
- id='user.settings.security.emailPwd'
- defaultMessage='Email and Password'
- />
- );
- if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
- describe = (
- <FormattedMessage
- id='user.settings.security.gitlab'
- defaultMessage='GitLab SSO'
- />
- );
- }
-
- return (
- <SettingItemMin
- title={this.props.intl.formatMessage(holders.method)}
- describe={describe}
- updateSection={updateSectionStatus}
- />
- );
- }
- render() {
- const passwordSection = this.createPasswordSection();
- let signInSection;
-
- let numMethods = 0;
- numMethods = global.window.mm_config.EnableSignUpWithGitLab === 'true' ? numMethods + 1 : numMethods;
- numMethods = global.window.mm_config.EnableSignUpWithGoogle === 'true' ? numMethods + 1 : numMethods;
-
- if (global.window.mm_config.EnableSignUpWithEmail && numMethods > 0) {
- signInSection = this.createSignInSection();
- }
-
- return (
- <div>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label={this.props.intl.formatMessage(holders.close)}
- onClick={this.props.closeModal}
- >
- <span aria-hidden='true'>{'Ɨ'}</span>
- </button>
- <h4
- className='modal-title'
- ref='title'
- >
- <div className='modal-back'>
- <i
- className='fa fa-angle-left'
- onClick={this.props.collapseModal}
- />
- </div>
- <FormattedMessage
- id='user.settings.security.title'
- defaultMessage='Security Settings'
- />
- </h4>
- </div>
- <div className='user-settings'>
- <h3 className='tab-header'>
- <FormattedMessage
- id='user.settings.security.title'
- defaultMessage='Security Settings'
- />
- </h3>
- <div className='divider-dark first'/>
- {passwordSection}
- <div className='divider-light'/>
- {signInSection}
- <div className='divider-dark'/>
- <br></br>
- <ToggleModalButton
- className='security-links theme'
- dialogType={AccessHistoryModal}
- >
- <i className='fa fa-clock-o'></i>
- <FormattedMessage
- id='user.settings.security.viewHistory'
- defaultMessage='View Access History'
- />
- </ToggleModalButton>
- <b> </b>
- <ToggleModalButton
- className='security-links theme'
- dialogType={ActivityLogModal}
- >
- <i className='fa fa-clock-o'></i>
- <FormattedMessage
- id='user.settings.security.logoutActiveSessions'
- defaultMessage='View and Logout of Active Sessions'
- />
- </ToggleModalButton>
- </div>
- </div>
- );
- }
-}
-
-SecurityTab.defaultProps = {
- user: {},
- activeSection: ''
-};
-SecurityTab.propTypes = {
- intl: intlShape.isRequired,
- user: React.PropTypes.object,
- activeSection: React.PropTypes.string,
- updateSection: React.PropTypes.func,
- updateTab: React.PropTypes.func,
- closeModal: React.PropTypes.func.isRequired,
- collapseModal: React.PropTypes.func.isRequired,
- setEnforceFocus: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(SecurityTab);
diff --git a/web/react/components/user_settings/user_settings_theme.jsx b/web/react/components/user_settings/user_settings_theme.jsx
deleted file mode 100644
index 74975d115..000000000
--- a/web/react/components/user_settings/user_settings_theme.jsx
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import CustomThemeChooser from './custom_theme_chooser.jsx';
-import PremadeThemeChooser from './premade_theme_chooser.jsx';
-import SettingItemMin from '../setting_item_min.jsx';
-import SettingItemMax from '../setting_item_max.jsx';
-
-import UserStore from '../../stores/user_store.jsx';
-
-import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
-import * as Client from '../../utils/client.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import Constants from '../../utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
-
-const ActionTypes = Constants.ActionTypes;
-
-const holders = defineMessages({
- themeTitle: {
- id: 'user.settings.display.theme.title',
- defaultMessage: 'Theme'
- },
- themeDescribe: {
- id: 'user.settings.display.theme.describe',
- defaultMessage: 'Open to manage your theme'
- }
-});
-
-export default class ThemeSetting extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
- this.submitTheme = this.submitTheme.bind(this);
- this.updateTheme = this.updateTheme.bind(this);
- this.deactivate = this.deactivate.bind(this);
- this.resetFields = this.resetFields.bind(this);
- this.handleImportModal = this.handleImportModal.bind(this);
-
- this.state = this.getStateFromStores();
-
- this.originalTheme = Object.assign({}, this.state.theme);
- }
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
-
- if (this.props.selected) {
- $(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
- }
- }
- componentDidUpdate() {
- if (this.props.selected) {
- $('.color-btn').removeClass('active-border');
- $(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
- }
- }
- componentWillReceiveProps(nextProps) {
- if (!this.props.selected && nextProps.selected) {
- this.resetFields();
- }
- }
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
- getStateFromStores() {
- const user = UserStore.getCurrentUser();
- let theme = null;
-
- if ($.isPlainObject(user.theme_props) && !$.isEmptyObject(user.theme_props)) {
- theme = Object.assign({}, user.theme_props);
- } else {
- theme = $.extend(true, {}, Constants.THEMES.default);
- }
-
- let type = 'premade';
- if (theme.type === 'custom') {
- type = 'custom';
- }
-
- if (!theme.codeTheme) {
- theme.codeTheme = Constants.DEFAULT_CODE_THEME;
- }
-
- return {theme, type};
- }
- onChange() {
- const newState = this.getStateFromStores();
-
- if (!Utils.areObjectsEqual(this.state, newState)) {
- this.setState(newState);
- }
-
- this.props.setEnforceFocus(true);
- }
- scrollToTop() {
- $('.ps-container.modal-body').scrollTop(0);
- $('.ps-container.modal-body').perfectScrollbar('update');
- }
- submitTheme(e) {
- e.preventDefault();
- var user = UserStore.getCurrentUser();
- user.theme_props = this.state.theme;
-
- Client.updateUser(user,
- (data) => {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ME,
- me: data
- });
-
- this.props.setRequireConfirm(false);
- this.originalTheme = Object.assign({}, this.state.theme);
- this.scrollToTop();
- this.props.updateSection('');
- },
- (err) => {
- var state = this.getStateFromStores();
- state.serverError = err;
- this.setState(state);
- }
- );
- }
- updateTheme(theme) {
- let themeChanged = this.state.theme.length === theme.length;
- if (!themeChanged) {
- for (const field in theme) {
- if (theme.hasOwnProperty(field)) {
- if (this.state.theme[field] !== theme[field]) {
- themeChanged = true;
- break;
- }
- }
- }
- }
-
- this.props.setRequireConfirm(themeChanged);
-
- this.setState({theme});
- Utils.applyTheme(theme);
- }
- updateType(type) {
- this.setState({type});
- }
- deactivate() {
- const state = this.getStateFromStores();
-
- Utils.applyTheme(state.theme);
- }
- resetFields() {
- const state = this.getStateFromStores();
- state.serverError = null;
- this.setState(state);
- this.scrollToTop();
-
- Utils.applyTheme(state.theme);
-
- this.props.setRequireConfirm(false);
- }
- handleImportModal() {
- AppDispatcher.handleViewAction({
- type: ActionTypes.TOGGLE_IMPORT_THEME_MODAL,
- value: true
- });
-
- this.props.setEnforceFocus(false);
- }
- render() {
- const {formatMessage} = this.props.intl;
-
- var serverError;
- if (this.state.serverError) {
- serverError = this.state.serverError;
- }
-
- const displayCustom = this.state.type === 'custom';
-
- let custom;
- let premade;
- if (displayCustom) {
- custom = (
- <div key='customThemeChooser'>
- <CustomThemeChooser
- theme={this.state.theme}
- updateTheme={this.updateTheme}
- />
- </div>
- );
- } else {
- premade = (
- <div key='premadeThemeChooser'>
- <br/>
- <PremadeThemeChooser
- theme={this.state.theme}
- updateTheme={this.updateTheme}
- />
- </div>
- );
- }
-
- let themeUI;
- if (this.props.selected) {
- let inputs = [];
-
- inputs.push(
- <div
- className='radio'
- key='premadeThemeColorLabel'
- >
- <label>
- <input type='radio'
- checked={!displayCustom}
- onChange={this.updateType.bind(this, 'premade')}
- />
- <FormattedMessage
- id='user.settings.display.theme.themeColors'
- defaultMessage='Theme Colors'
- />
- </label>
- <br/>
- </div>
- );
-
- inputs.push(premade);
-
- inputs.push(
- <div
- className='radio'
- key='customThemeColorLabel'
- >
- <label>
- <input type='radio'
- checked={displayCustom}
- onChange={this.updateType.bind(this, 'custom')}
- />
- <FormattedMessage
- id='user.settings.display.theme.customTheme'
- defaultMessage='Custom Theme'
- />
- </label>
- </div>
- );
-
- inputs.push(custom);
-
- inputs.push(
- <div key='importSlackThemeButton'>
- <br/>
- <a
- className='theme'
- onClick={this.handleImportModal}
- >
- <FormattedMessage
- id='user.settings.display.theme.import'
- defaultMessage='Import theme colors from Slack'
- />
- </a>
- </div>
- );
-
- themeUI = (
- <SettingItemMax
- inputs={inputs}
- submit={this.submitTheme}
- server_error={serverError}
- width='full'
- updateSection={(e) => {
- this.props.updateSection('');
- e.preventDefault();
- }}
- />
- );
- } else {
- themeUI = (
- <SettingItemMin
- title={formatMessage(holders.themeTitle)}
- describe={formatMessage(holders.themeDescribe)}
- updateSection={() => {
- this.props.updateSection('theme');
- }}
- />
- );
- }
-
- return themeUI;
- }
-}
-
-ThemeSetting.propTypes = {
- intl: intlShape.isRequired,
- selected: React.PropTypes.bool.isRequired,
- updateSection: React.PropTypes.func.isRequired,
- setRequireConfirm: React.PropTypes.func.isRequired,
- setEnforceFocus: React.PropTypes.func.isRequired
-};
-
-export default injectIntl(ThemeSetting);
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
deleted file mode 100644
index 713fbf80b..000000000
--- a/web/react/components/view_image.jsx
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as Client from '../utils/client.jsx';
-import * as Utils from '../utils/utils.jsx';
-import AudioVideoPreview from './audio_video_preview.jsx';
-import Constants from '../utils/constants.jsx';
-import FileInfoPreview from './file_info_preview.jsx';
-import FileStore from '../stores/file_store.jsx';
-import ViewImagePopoverBar from './view_image_popover_bar.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'mm-intl';
-
-const Modal = ReactBootstrap.Modal;
-const KeyCodes = Constants.KeyCodes;
-
-const holders = defineMessages({
- loading: {
- id: 'view_image.loading',
- defaultMessage: 'Loading '
- }
-});
-
-class ViewImageModal extends React.Component {
- constructor(props) {
- super(props);
-
- this.showImage = this.showImage.bind(this);
- this.loadImage = this.loadImage.bind(this);
-
- this.handleNext = this.handleNext.bind(this);
- this.handlePrev = this.handlePrev.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
-
- this.onModalShown = this.onModalShown.bind(this);
- this.onModalHidden = this.onModalHidden.bind(this);
-
- this.onFileStoreChange = this.onFileStoreChange.bind(this);
-
- this.getPublicLink = this.getPublicLink.bind(this);
- this.onMouseEnterImage = this.onMouseEnterImage.bind(this);
- this.onMouseLeaveImage = this.onMouseLeaveImage.bind(this);
-
- this.state = {
- imgId: this.props.startId,
- fileInfo: null,
- imgHeight: '100%',
- loaded: Utils.fillArray(false, this.props.filenames.length),
- progress: Utils.fillArray(0, this.props.filenames.length),
- showFooter: false
- };
- }
-
- handleNext(e) {
- if (e) {
- e.stopPropagation();
- }
- let id = this.state.imgId + 1;
- if (id > this.props.filenames.length - 1) {
- id = 0;
- }
- this.showImage(id);
- }
-
- handlePrev(e) {
- if (e) {
- e.stopPropagation();
- }
- let id = this.state.imgId - 1;
- if (id < 0) {
- id = this.props.filenames.length - 1;
- }
- this.showImage(id);
- }
-
- handleKeyPress(e) {
- if (e.keyCode === KeyCodes.RIGHT) {
- this.handleNext();
- } else if (e.keyCode === KeyCodes.LEFT) {
- this.handlePrev();
- }
- }
-
- onModalShown(nextProps) {
- $(window).on('keyup', this.handleKeyPress);
-
- this.showImage(nextProps.startId);
-
- FileStore.addChangeListener(this.onFileStoreChange);
- }
-
- onModalHidden() {
- $(window).off('keyup', this.handleKeyPress);
-
- if (this.refs.video) {
- this.refs.video.stop();
- }
-
- FileStore.removeChangeListener(this.onFileStoreChange);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.show === true && this.props.show === false) {
- this.onModalShown(nextProps);
- } else if (nextProps.show === false && this.props.show === true) {
- this.onModalHidden();
- }
-
- if (!Utils.areObjectsEqual(this.props.filenames, nextProps.filenames)) {
- this.setState({
- loaded: Utils.fillArray(false, nextProps.filenames.length),
- progress: Utils.fillArray(0, nextProps.filenames.length)
- });
- }
- }
-
- onFileStoreChange(filename) {
- const id = this.props.filenames.indexOf(filename);
-
- if (id !== -1) {
- if (id === this.state.imgId) {
- this.setState({
- fileInfo: FileStore.getInfo(filename)
- });
- }
-
- if (!this.state.loaded[id]) {
- this.loadImage(id, filename);
- }
- }
- }
-
- showImage(id) {
- this.setState({imgId: id});
-
- const imgHeight = $(window).height() - 100;
- this.setState({imgHeight});
-
- const filename = this.props.filenames[id];
-
- if (!FileStore.hasInfo(filename)) {
- // the image will actually be loaded once we know what we need to load
- AsyncClient.getFileInfo(filename);
- return;
- }
-
- this.setState({
- fileInfo: FileStore.getInfo(filename)
- });
-
- if (!this.state.loaded[id]) {
- this.loadImage(id, filename);
- }
- }
-
- loadImage(id, filename) {
- const fileInfo = FileStore.getInfo(filename);
- const fileType = Utils.getFileType(fileInfo.extension);
-
- if (fileType === 'image') {
- let previewUrl;
- if (fileInfo.has_image_preview) {
- previewUrl = Utils.getPreviewImagePath(filename);
- } else {
- // some images (eg animated gifs) just show the file itself and not a preview
- previewUrl = Utils.getFileUrl(filename);
- }
-
- const img = new Image();
- img.load(
- previewUrl,
- () => {
- const progress = this.state.progress;
- progress[id] = img.completedPercentage;
- this.setState({progress});
- }
- );
- img.onload = () => {
- const loaded = this.state.loaded;
- loaded[id] = true;
- this.setState({loaded});
- };
- } else {
- // there's nothing to load for non-image files
- var loaded = this.state.loaded;
- loaded[id] = true;
- this.setState({loaded});
- }
- }
-
- getPublicLink() {
- var data = {};
- data.channel_id = this.props.channelId;
- data.user_id = this.props.userId;
- data.filename = this.props.filenames[this.state.imgId];
- Client.getPublicLink(
- data,
- (serverData) => {
- if (Utils.isMobile()) {
- window.location.href = serverData.public_link;
- } else {
- window.open(serverData.public_link);
- }
- },
- () => {
- //Do Nothing on error
- }
- );
- }
-
- onMouseEnterImage() {
- this.setState({showFooter: true});
- }
-
- onMouseLeaveImage() {
- this.setState({showFooter: false});
- }
-
- render() {
- if (this.props.filenames.length < 1 || this.props.filenames.length - 1 < this.state.imgId) {
- return <div/>;
- }
-
- const filename = this.props.filenames[this.state.imgId];
- const fileUrl = Utils.getFileUrl(filename, true);
-
- var content;
- if (this.state.loaded[this.state.imgId]) {
- // this.state.fileInfo is for the current image and we shoudl have it before we load the image
- const fileInfo = this.state.fileInfo;
- const fileType = Utils.getFileType(fileInfo.extension);
-
- if (fileType === 'image') {
- content = (
- <ImagePreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={fileInfo}
- maxHeight={this.state.imgHeight}
- />
- );
- } else if (fileType === 'video' || fileType === 'audio') {
- content = (
- <AudioVideoPreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={this.state.fileInfo}
- maxHeight={this.state.imgHeight}
- formatMessage={this.props.intl.formatMessage}
- />
- );
- } else {
- content = (
- <FileInfoPreview
- filename={filename}
- fileUrl={fileUrl}
- fileInfo={fileInfo}
- formatMessage={this.props.intl.formatMessage}
- />
- );
- }
- } else {
- // display a progress indicator when the preview for an image is still loading
- const progress = Math.floor(this.state.progress[this.state.imgId]);
-
- content = (
- <LoadingImagePreview
- progress={progress}
- loading={this.props.intl.formatMessage(holders.loading)}
- />
- );
- }
-
- let leftArrow = null;
- let rightArrow = null;
- if (this.props.filenames.length > 1) {
- leftArrow = (
- <a
- ref='previewArrowLeft'
- className='modal-prev-bar'
- href='#'
- onClick={this.handlePrev}
- >
- <i className='image-control image-prev'/>
- </a>
- );
-
- rightArrow = (
- <a
- ref='previewArrowRight'
- className='modal-next-bar'
- href='#'
- onClick={this.handleNext}
- >
- <i className='image-control image-next'/>
- </a>
- );
- }
-
- let closeButtonClass = 'modal-close';
- if (this.state.showFooter) {
- closeButtonClass += ' modal-close--show';
- }
-
- return (
- <Modal
- show={this.props.show}
- onHide={this.props.onModalDismissed}
- className='image_modal'
- dialogClassName='modal-image'
- >
- <Modal.Body
- modalClassName='image-body'
- onClick={this.props.onModalDismissed}
- >
- <div
- className={'image-wrapper'}
- onClick={this.props.onModalDismissed}
- >
- <div
- onMouseEnter={this.onMouseEnterImage}
- onMouseLeave={this.onMouseLeaveImage}
- onClick={(e) => e.stopPropagation()}
- >
- <div
- className={closeButtonClass}
- onClick={this.props.onModalDismissed}
- />
- {content}
- <ViewImagePopoverBar
- show={this.state.showFooter}
- fileId={this.state.imgId}
- totalFiles={this.props.filenames.length}
- filename={name}
- fileURL={fileUrl}
- getPublicLink={this.getPublicLink}
- />
- </div>
- </div>
- {leftArrow}
- {rightArrow}
- </Modal.Body>
- </Modal>
- );
- }
-}
-
-ViewImageModal.defaultProps = {
- show: false,
- filenames: [],
- channelId: '',
- userId: '',
- startId: 0
-};
-ViewImageModal.propTypes = {
- intl: intlShape.isRequired,
- show: React.PropTypes.bool.isRequired,
- onModalDismissed: React.PropTypes.func.isRequired,
- filenames: React.PropTypes.array,
- modalId: React.PropTypes.string,
- channelId: React.PropTypes.string,
- userId: React.PropTypes.string,
- startId: React.PropTypes.number
-};
-
-function LoadingImagePreview({progress, loading}) {
- let progressView = null;
- if (progress) {
- progressView = (
- <span className='loader-percent'>
- {loading + progress + '%'}
- </span>
- );
- }
-
- return (
- <div className='view-image__loading'>
- <img
- className='loader-image'
- src='/static/images/load.gif'
- />
- {progressView}
- </div>
- );
-}
-
-LoadingImagePreview.propTypes = {
- progress: React.PropTypes.number,
- loading: React.PropTypes.string
-};
-
-function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
- let previewUrl;
- if (fileInfo.has_preview_image) {
- previewUrl = Utils.getPreviewImagePath(filename);
- } else {
- previewUrl = fileUrl;
- }
-
- return (
- <a
- href={fileUrl}
- target='_blank'
- download={true}
- >
- <img
- style={{maxHeight}}
- src={previewUrl}
- />
- </a>
- );
-}
-
-ImagePreview.propTypes = {
- filename: React.PropTypes.string.isRequired,
- fileUrl: React.PropTypes.string.isRequired,
- fileInfo: React.PropTypes.object.isRequired,
- maxHeight: React.PropTypes.number.isRequired
-};
-
-export default injectIntl(ViewImageModal);
diff --git a/web/react/components/view_image_popover_bar.jsx b/web/react/components/view_image_popover_bar.jsx
deleted file mode 100644
index 18be5a3c5..000000000
--- a/web/react/components/view_image_popover_bar.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class ViewImagePopoverBar extends React.Component {
- render() {
- var publicLink = '';
- if (global.window.mm_config.EnablePublicLink === 'true') {
- publicLink = (
- <div>
- <a
- href='#'
- className='public-link text'
- data-title='Public Image'
- onClick={this.props.getPublicLink}
- >
- <FormattedMessage
- id='view_image_popover.publicLink'
- defaultMessage='Get Public Link'
- />
- </a>
- <span className='text'>{' | '}</span>
- </div>
- );
- }
-
- var footerClass = 'modal-button-bar';
- if (this.props.show) {
- footerClass += ' footer--show';
- }
-
- return (
- <div
- ref='imageFooter'
- className={footerClass}
- >
- <span className='pull-left text'>
- <FormattedMessage
- id='view_image_popover.file'
- defaultMessage='File {count} of {total}'
- values={{
- count: (this.props.fileId + 1),
- total: this.props.totalFiles
- }}
- />
- </span>
- <div className='image-links'>
- {publicLink}
- <a
- href={this.props.fileURL}
- download={this.props.filename}
- className='text'
- target='_blank'
- >
- <FormattedMessage
- id='view_image_popover.download'
- defaultMessage='Download'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-ViewImagePopoverBar.defaultProps = {
- show: false,
- imgId: 0,
- totalFiles: 0,
- filename: '',
- fileURL: ''
-};
-
-ViewImagePopoverBar.propTypes = {
- show: React.PropTypes.bool.isRequired,
- fileId: React.PropTypes.number.isRequired,
- totalFiles: React.PropTypes.number.isRequired,
- filename: React.PropTypes.string.isRequired,
- fileURL: React.PropTypes.string.isRequired,
- getPublicLink: React.PropTypes.func.isRequired
-};
diff --git a/web/react/components/youtube_video.jsx b/web/react/components/youtube_video.jsx
deleted file mode 100644
index fae846afb..000000000
--- a/web/react/components/youtube_video.jsx
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../stores/channel_store.jsx';
-
-const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#\&\?]*)/;
-
-export default class YoutubeVideo extends React.Component {
- constructor(props) {
- super(props);
-
- this.updateStateFromProps = this.updateStateFromProps.bind(this);
- this.handleReceivedMetadata = this.handleReceivedMetadata.bind(this);
-
- this.play = this.play.bind(this);
- this.stop = this.stop.bind(this);
- this.stopOnChannelChange = this.stopOnChannelChange.bind(this);
-
- this.state = {
- playing: false,
- title: ''
- };
- }
-
- componentWillMount() {
- this.updateStateFromProps(this.props);
- }
-
- componentWillReceiveProps(nextProps) {
- this.updateStateFromProps(nextProps);
- }
-
- updateStateFromProps(props) {
- const link = props.link;
-
- const match = link.trim().match(ytRegex);
- if (!match || match[2].length !== 11) {
- return;
- }
-
- this.setState({
- videoId: match[2],
- time: this.handleYoutubeTime(link)
- });
- }
-
- handleYoutubeTime(link) {
- const timeRegex = /[\\?&]t=([0-9hms]+)/;
-
- const time = link.match(timeRegex);
- if (!time || !time[1]) {
- return '';
- }
-
- const hours = time[1].match(/([0-9]+)h/);
- const minutes = time[1].match(/([0-9]+)m/);
- const seconds = time[1].match(/([0-9]+)s/);
-
- let ticks = 0;
-
- if (hours && hours[1]) {
- ticks += parseInt(hours[1], 10) * 3600;
- }
-
- if (minutes && minutes[1]) {
- ticks += parseInt(minutes[1], 10) * 60;
- }
-
- if (seconds && seconds[1]) {
- ticks += parseInt(seconds[1], 10);
- }
-
- return '&start=' + ticks.toString();
- }
-
- componentDidMount() {
- if (global.window.mm_config.GoogleDeveloperKey) {
- $.ajax({
- async: true,
- url: 'https://www.googleapis.com/youtube/v3/videos',
- type: 'GET',
- data: {part: 'snippet', id: this.state.videoId, key: global.window.mm_config.GoogleDeveloperKey},
- success: this.handleReceivedMetadata
- });
- }
- }
-
- handleReceivedMetadata(data) {
- if (!data.items.length || !data.items[0].snippet) {
- return null;
- }
- var metadata = data.items[0].snippet;
- this.setState({
- receivedYoutubeData: true,
- title: metadata.title
- });
- return null;
- }
-
- play() {
- this.setState({playing: true});
-
- if (ChannelStore.getCurrentId() === this.props.channelId) {
- ChannelStore.addChangeListener(this.stopOnChannelChange);
- }
- }
-
- stop() {
- this.setState({playing: false});
- }
-
- stopOnChannelChange() {
- if (ChannelStore.getCurrentId() !== this.props.channelId) {
- this.stop();
- }
- }
-
- render() {
- let header = 'Youtube';
- if (this.state.title) {
- header = header + ' - ';
- }
-
- let content;
- if (this.state.playing) {
- content = (
- <iframe
- src={'https://www.youtube.com/embed/' + this.state.videoId + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' + this.state.time}
- width='480px'
- height='360px'
- type='text/html'
- frameBorder='0'
- allowFullScreen='allowfullscreen'
- />
- );
- } else {
- content = (
- <div className='embed-responsive embed-responsive-4by3 video-div__placeholder'>
- <div className='video-thumbnail__container'>
- <img
- className='video-thumbnail'
- src={'https://i.ytimg.com/vi/' + this.state.videoId + '/hqdefault.jpg'}
- />
- <div className='block'>
- <span className='play-button'><span/></span>
- </div>
- </div>
- </div>
- );
- }
-
- return (
- <div>
- <h4>
- <span className='video-type'>{header}</span>
- <span className='video-title'><a href={this.props.link}>{this.state.title}</a></span>
- </h4>
- <div
- className='video-div embed-responsive-item'
- onClick={this.play}
- >
- {content}
- </div>
- </div>
- );
- }
-
- static isYoutubeLink(link) {
- return link.trim().match(ytRegex);
- }
-}
-
-YoutubeVideo.propTypes = {
- channelId: React.PropTypes.string.isRequired,
- link: React.PropTypes.string.isRequired
-};
diff --git a/web/react/dispatcher/app_dispatcher.jsx b/web/react/dispatcher/app_dispatcher.jsx
deleted file mode 100644
index 648f0fa0d..000000000
--- a/web/react/dispatcher/app_dispatcher.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Flux from 'flux';
-
-import Constants from '../utils/constants.jsx';
-const PayloadSources = Constants.PayloadSources;
-
-const AppDispatcher = Object.assign(new Flux.Dispatcher(), {
- handleServerAction: function performServerAction(action) {
- var payload = {
- source: PayloadSources.SERVER_ACTION,
- action
- };
- this.dispatch(payload);
- },
-
- handleViewAction: function performViewAction(action) {
- var payload = {
- source: PayloadSources.VIEW_ACTION,
- action
- };
- this.dispatch(payload);
- }
-});
-
-module.exports = AppDispatcher;
diff --git a/web/react/package.json b/web/react/package.json
deleted file mode 100644
index 509c9967b..000000000
--- a/web/react/package.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "name": "mattermost",
- "version": "0.0.1",
- "private": true,
- "dependencies": {
- "autolinker": "0.24.0",
- "fastclick": "1.0.6",
- "flux": "2.1.1",
- "highlight.js": "9.1.0",
- "keymirror": "0.1.1",
- "marked": "mattermost/marked#cb85e5cc81bc7937dbb73c3c53d9532b1b97e3ca",
- "mm-intl": "mattermost/mm-intl#805442fd474fa40cd586ddeda404dbbe8e60626d",
- "object-assign": "4.0.1",
- "react": "0.14.3",
- "react-router": "2.0.0",
- "twemoji": "1.4.1"
- },
- "devDependencies": {
- "babel-eslint": "5.0.0",
- "babel-plugin-transform-runtime": "6.5.2",
- "babel-preset-es2015": "6.5.0",
- "babel-preset-react": "6.5.0",
- "babel-preset-stage-0": "6.5.0",
- "babelify": "7.2.0",
- "browserify": "13.0.0",
- "eslint": "2.2.0",
- "eslint-plugin-react": "4.0.0",
- "exorcist": "0.4.0",
- "uglify-js": "2.6.1",
- "watchify": "3.7.0"
- },
- "scripts": {
- "check": "",
- "build-libs": "browserify -r crypto -r autolinker -r flux -r keymirror -r marked -r object-assign -r twemoji | uglifyjs -c -m --screw-ie8 > ../static/js/libs.min.js",
- "start": "watchify --fast -x crypto -x node -x autolinker -x flux -x keymirror -x marked -x object-assign -x twemoji -o ../static/js/bundle.js -v -d ./**/*.jsx",
- "build": "browserify -x crypto -x autolinker -x flux -x keymirror -x marked -x object-assign -x twemoji -d ./**/*.jsx | exorcist ../static/js/inter.js.map > ../static/js/tmp.js && uglifyjs ../static/js/tmp.js --in-source-map \"../static/js/inter.js.map\" --source-map \"../static/js/bundle.min.js.map\" --source-map-url \"/static/js/bundle.min.js.map\" -c -m --screw-ie8 > ../static/js/bundle.min.js && rm ../static/js/tmp.js && rm ../static/js/inter.js.map"
- },
- "browserify": {
- "transform": [
- [
- "babelify",
- {
- "presets": [
- "es2015",
- "react",
- "stage-0"
- ],
- "plugins": [
- "transform-runtime"
- ]
- }
- ]
- ]
- }
-}
diff --git a/web/react/pages/authorize.jsx b/web/react/pages/authorize.jsx
deleted file mode 100644
index 20fea19f0..000000000
--- a/web/react/pages/authorize.jsx
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Authorize from '../components/authorize.jsx';
-import * as Client from '../utils/client.jsx';
-
-var IntlProvider = ReactIntl.IntlProvider;
-
-class Root extends React.Component {
- constructor() {
- super();
- this.state = {
- translations: null,
- loaded: false
- };
- }
-
- static propTypes() {
- return {
- map: React.PropTypes.object.isRequired
- };
- }
-
- componentWillMount() {
- Client.getTranslations(
- this.props.map.Locale,
- (data) => {
- this.setState({
- translations: data,
- loaded: true
- });
- return;
- },
- () => {
- this.setState({
- loaded: true
- });
- }
- );
- }
-
- render() {
- if (!this.state.loaded) {
- return <div></div>;
- }
-
- return (
- <IntlProvider
- locale={this.props.map.Locale}
- messages={this.state.translations}
- >
- <Authorize
- teamName={this.props.map.TeamName}
- appName={this.props.map.AppName}
- responseType={this.props.map.ResponseType}
- clientId={this.props.map.ClientId}
- redirectUri={this.props.map.RedirectUri}
- scope={this.props.map.Scope}
- state={this.props.map.State}
- />
- </IntlProvider>
- );
- }
-}
-
-global.window.setup_authorize_page = function setup(props) {
- ReactDOM.render(
- <Root map={props}/>,
- document.getElementById('authorize')
- );
-};
diff --git a/web/react/pages/root.jsx b/web/react/pages/root.jsx
deleted file mode 100644
index dcc6fc40e..000000000
--- a/web/react/pages/root.jsx
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {Router, Route, IndexRoute, IndexRedirect, browserHistory} from 'react-router';
-import Root from '../components/root.jsx';
-import Login from '../components/login.jsx';
-import LoggedIn from '../components/logged_in.jsx';
-import NotLoggedIn from '../components/not_logged_in.jsx';
-import NeedsTeam from '../components/needs_team.jsx';
-import PasswordResetSendLink from '../components/password_reset_send_link.jsx';
-import PasswordResetForm from '../components/password_reset_form.jsx';
-import ChannelView from '../components/channel_view.jsx';
-import Sidebar from '../components/sidebar.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import ErrorStore from '../stores/error_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import SignupTeam from '../components/signup_team.jsx';
-import * as Client from '../utils/client.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';
-import ShouldVerifyEmail from '../components/should_verify_email.jsx';
-import DoVerifyEmail from '../components/do_verify_email.jsx';
-import AdminConsole from '../components/admin_console/admin_controller.jsx';
-import ClaimAccount from '../components/claim/claim_account.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';
-import TeamDisplayNamePage from '../components/signup_team_complete/components/team_signup_display_name_page.jsx';
-import TeamURLPage from '../components/signup_team_complete/components/team_signup_url_page.jsx';
-import SendInivtesPage from '../components/signup_team_complete/components/team_signup_send_invites_page.jsx';
-import UsernamePage from '../components/signup_team_complete/components/team_signup_username_page.jsx';
-import PasswordPage from '../components/signup_team_complete/components/team_signup_password_page.jsx';
-import FinishedPage from '../components/signup_team_complete/components/team_signup_finished.jsx';
-
-// This is for anything that needs to be done for ALL react components.
-// This runs before we start to render anything.
-function preRenderSetup(callwhendone) {
- const d1 = Client.getClientConfig(
- (data, textStatus, xhr) => {
- if (!data) {
- return;
- }
-
- global.window.mm_config = data;
-
- var serverVersion = xhr.getResponseHeader('X-Version-ID');
-
- if (serverVersion !== BrowserStore.getLastServerVersion()) {
- if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') {
- BrowserStore.setLastServerVersion(serverVersion);
- } else {
- BrowserStore.setLastServerVersion(serverVersion);
- window.location.reload(true);
- console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
- }
- }
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getClientConfig');
- }
- );
-
- const d2 = Client.getClientLicenceConfig(
- (data) => {
- if (!data) {
- return;
- }
-
- global.window.mm_license = data;
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getClientLicenceConfig');
- }
- );
-
- // Set these here so they don't fail in client.jsx track
- global.window.analytics = [];
- global.window.analytics.page = () => {
- // Do Nothing
- };
- global.window.analytics.track = () => {
- // Do Nothing
- };
-
- $.when(d1, d2).done(callwhendone);
-}
-
-function preLoggedIn(nextState, replace, callback) {
- const d1 = Client.getAllPreferences(
- (data) => {
- if (!data) {
- return;
- }
-
- PreferenceStore.setPreferences(data);
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getAllPreferences');
- }
- );
-
- const d2 = AsyncClient.getChannels();
-
- $.when(d1, d2).done(() => callback());
-}
-
-function onChannelChange(nextState) {
- const channelName = nextState.params.channel;
-
- // Make sure we have all the channels
- AsyncClient.getChannels(true);
-
- // Get our channel's ID
- const channel = ChannelStore.getByName(channelName);
-
- // User clicked channel
- GlobalActions.emitChannelClickEvent(channel);
-}
-
-function onRootEnter(nextState, replace, callback) {
- if (nextState.location.pathname === '/') {
- Client.getMeLoggedIn((data) => {
- if (!data || data.logged_in === 'false') {
- replace({pathname: '/signup_team'});
- callback();
- } else {
- replace({pathname: '/' + data.team_name + '/channels/town-square'});
- callback();
- }
- });
- return;
- }
-
- callback();
-}
-
-function onPermalinkEnter(nextState) {
- const postId = nextState.params.postid;
-
- GlobalActions.emitPostFocusEvent(postId);
-}
-
-function onLoggedOut(nextState) {
- const teamName = nextState.params.team;
- Client.logout(
- () => {
- browserHistory.push('/' + teamName + '/login');
- BrowserStore.signalLogout();
- BrowserStore.clear();
- ErrorStore.clearLastError();
- },
- () => {
- browserHistory.push('/' + teamName + '/login');
- }
- );
-}
-
-function renderRootComponent() {
- ReactDOM.render((
- <Router
- history={browserHistory}
- >
- <Route
- path='/'
- component={Root}
- onEnter={onRootEnter}
- >
- <Route
- component={LoggedIn}
- onEnter={preLoggedIn}
- >
- <Route
- path=':team/channels/:channel'
- onEnter={onChannelChange}
- components={{
- sidebar: Sidebar,
- center: ChannelView
- }}
- />
- <Route
- path=':team/pl/:postid'
- onEnter={onPermalinkEnter}
- components={{
- sidebar: Sidebar,
- center: ChannelView
- }}
- />
- <Route
- path=':team/logout'
- onEnter={onLoggedOut}
- components={{
- sidebar: null,
- center: null
- }}
- />
- <Route
- path='admin_console'
- components={{
- sidebar: null,
- center: AdminConsole
- }}
- />
- </Route>
- <Route component={NotLoggedIn}>
- <Route
- path='signup_team'
- component={SignupTeam}
- />
- <Route
- path='signup_team_complete'
- component={SignupTeamComplete}
- >
- <IndexRoute component={FinishedPage}/>
- <Route
- path='welcome'
- component={WelcomePage}
- />
- <Route
- path='team_display_name'
- component={TeamDisplayNamePage}
- />
- <Route
- path='team_url'
- component={TeamURLPage}
- />
- <Route
- path='invites'
- component={SendInivtesPage}
- />
- <Route
- path='username'
- component={UsernamePage}
- />
- <Route
- path='password'
- component={PasswordPage}
- />
- </Route>
- <Route
- path='signup_user_complete'
- component={SignupUserComplete}
- />
- <Route
- path='signup_team_confirm'
- component={SignupTeamConfirm}
- />
- <Route
- path='should_verify_email'
- component={ShouldVerifyEmail}
- />
- <Route
- path='do_verify_email'
- component={DoVerifyEmail}
- />
- <Route
- path=':team'
- component={NeedsTeam}
- >
- <IndexRedirect to='login'/>
- <Route
- path='login'
- component={Login}
- />
- <Route
- path='claim'
- component={ClaimAccount}
- />
- <Route
- path='reset_password'
- component={PasswordResetSendLink}
- />
- <Route
- path='reset_password_complete'
- component={PasswordResetForm}
- />
- </Route>
- </Route>
- </Route>
- </Router>
- ),
- document.getElementById('root'));
-}
-
-global.window.setup_root = () => {
- // Do the pre-render setup and call renderRootComponent when done
- preRenderSetup(renderRootComponent);
-};
diff --git a/web/react/stores/admin_store.jsx b/web/react/stores/admin_store.jsx
deleted file mode 100644
index 9f7f6e7ff..000000000
--- a/web/react/stores/admin_store.jsx
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import BrowserStore from '../stores/browser_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const LOG_CHANGE_EVENT = 'log_change';
-const SERVER_AUDIT_CHANGE_EVENT = 'server_audit_change';
-const CONFIG_CHANGE_EVENT = 'config_change';
-const ALL_TEAMS_EVENT = 'all_team_change';
-
-class AdminStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.logs = null;
- this.audits = null;
- this.config = null;
- this.teams = null;
-
- this.emitLogChange = this.emitLogChange.bind(this);
- this.addLogChangeListener = this.addLogChangeListener.bind(this);
- this.removeLogChangeListener = this.removeLogChangeListener.bind(this);
-
- this.emitAuditChange = this.emitAuditChange.bind(this);
- this.addAuditChangeListener = this.addAuditChangeListener.bind(this);
- this.removeAuditChangeListener = this.removeAuditChangeListener.bind(this);
-
- this.emitConfigChange = this.emitConfigChange.bind(this);
- this.addConfigChangeListener = this.addConfigChangeListener.bind(this);
- this.removeConfigChangeListener = this.removeConfigChangeListener.bind(this);
-
- this.emitAllTeamsChange = this.emitAllTeamsChange.bind(this);
- this.addAllTeamsChangeListener = this.addAllTeamsChangeListener.bind(this);
- this.removeAllTeamsChangeListener = this.removeAllTeamsChangeListener.bind(this);
- }
-
- emitLogChange() {
- this.emit(LOG_CHANGE_EVENT);
- }
-
- addLogChangeListener(callback) {
- this.on(LOG_CHANGE_EVENT, callback);
- }
-
- removeLogChangeListener(callback) {
- this.removeListener(LOG_CHANGE_EVENT, callback);
- }
-
- emitAuditChange() {
- this.emit(SERVER_AUDIT_CHANGE_EVENT);
- }
-
- addAuditChangeListener(callback) {
- this.on(SERVER_AUDIT_CHANGE_EVENT, callback);
- }
-
- removeAuditChangeListener(callback) {
- this.removeListener(SERVER_AUDIT_CHANGE_EVENT, callback);
- }
-
- emitConfigChange() {
- this.emit(CONFIG_CHANGE_EVENT);
- }
-
- addConfigChangeListener(callback) {
- this.on(CONFIG_CHANGE_EVENT, callback);
- }
-
- removeConfigChangeListener(callback) {
- this.removeListener(CONFIG_CHANGE_EVENT, callback);
- }
-
- emitAllTeamsChange() {
- this.emit(ALL_TEAMS_EVENT);
- }
-
- addAllTeamsChangeListener(callback) {
- this.on(ALL_TEAMS_EVENT, callback);
- }
-
- removeAllTeamsChangeListener(callback) {
- this.removeListener(ALL_TEAMS_EVENT, callback);
- }
-
- getLogs() {
- return this.logs;
- }
-
- saveLogs(logs) {
- this.logs = logs;
- }
-
- getAudits() {
- return this.audits;
- }
-
- saveAudits(audits) {
- this.audits = audits;
- }
-
- getConfig() {
- return this.config;
- }
-
- saveConfig(config) {
- this.config = config;
- }
-
- getAllTeams() {
- return this.teams;
- }
-
- saveAllTeams(teams) {
- this.teams = teams;
- }
-
- getSelectedTeams() {
- const result = BrowserStore.getItem('seleted_teams');
- if (!result) {
- return {};
- }
- return result;
- }
-
- saveSelectedTeams(teams) {
- BrowserStore.setItem('seleted_teams', teams);
- }
-}
-
-var AdminStore = new AdminStoreClass();
-
-AdminStoreClass.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_LOGS:
- AdminStore.saveLogs(action.logs);
- AdminStore.emitLogChange();
- break;
- case ActionTypes.RECEIVED_SERVER_AUDITS:
- AdminStore.saveAudits(action.audits);
- AdminStore.emitAuditChange();
- break;
- case ActionTypes.RECEIVED_CONFIG:
- AdminStore.saveConfig(action.config);
- AdminStore.emitConfigChange();
- break;
- case ActionTypes.RECEIVED_ALL_TEAMS:
- AdminStore.saveAllTeams(action.teams);
- AdminStore.emitAllTeamsChange();
- break;
- default:
- }
-});
-
-export default AdminStore;
diff --git a/web/react/stores/analytics_store.jsx b/web/react/stores/analytics_store.jsx
deleted file mode 100644
index ec827f6d7..000000000
--- a/web/react/stores/analytics_store.jsx
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const CHANGE_EVENT = 'change';
-
-class AnalyticsStoreClass extends EventEmitter {
- constructor() {
- super();
- this.systemStats = {};
- this.teamStats = {};
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- getAllSystem() {
- return JSON.parse(JSON.stringify(this.systemStats));
- }
-
- getAllTeam(id) {
- if (id in this.teamStats) {
- return JSON.parse(JSON.stringify(this.teamStats[id]));
- }
-
- return {};
- }
-
- storeSystemStats(newStats) {
- for (const stat in newStats) {
- if (!newStats.hasOwnProperty(stat)) {
- continue;
- }
- this.systemStats[stat] = newStats[stat];
- }
- }
-
- storeTeamStats(id, newStats) {
- if (!(id in this.teamStats)) {
- this.teamStats[id] = {};
- }
-
- for (const stat in newStats) {
- if (!newStats.hasOwnProperty(stat)) {
- continue;
- }
- this.teamStats[id][stat] = newStats[stat];
- }
- }
-
-}
-
-var AnalyticsStore = new AnalyticsStoreClass();
-
-AnalyticsStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_ANALYTICS:
- if (action.teamId == null) {
- AnalyticsStore.storeSystemStats(action.stats);
- } else {
- AnalyticsStore.storeTeamStats(action.teamId, action.stats);
- }
- AnalyticsStore.emitChange();
- break;
- default:
- }
-});
-
-export default AnalyticsStore;
diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx
deleted file mode 100644
index 3b35916b3..000000000
--- a/web/react/stores/browser_store.jsx
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {generateId} from '../utils/utils.jsx';
-
-function getPrefix() {
- if (global.window.mm_current_user_id) {
- return global.window.mm_current_user_id + '_';
- }
-
- return 'unknown_';
-}
-
-class BrowserStoreClass {
- constructor() {
- this.getItem = this.getItem.bind(this);
- this.setItem = this.setItem.bind(this);
- this.removeItem = this.removeItem.bind(this);
- this.setGlobalItem = this.setGlobalItem.bind(this);
- this.getGlobalItem = this.getGlobalItem.bind(this);
- this.removeGlobalItem = this.removeGlobalItem.bind(this);
- this.actionOnItemsWithPrefix = this.actionOnItemsWithPrefix.bind(this);
- this.actionOnGlobalItemsWithPrefix = this.actionOnGlobalItemsWithPrefix.bind(this);
- this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this);
- this.getLastServerVersion = this.getLastServerVersion.bind(this);
- this.setLastServerVersion = this.setLastServerVersion.bind(this);
- this.clear = this.clear.bind(this);
- this.clearAll = this.clearAll.bind(this);
- this.checkedLocalStorageSupported = '';
- this.signalLogout = this.signalLogout.bind(this);
- this.isSignallingLogout = this.isSignallingLogout.bind(this);
- this.signalLogin = this.signalLogin.bind(this);
- this.isSignallingLogin = this.isSignallingLogin.bind(this);
- }
-
- checkVersion() {
- var currentVersion = sessionStorage.getItem('storage_version');
- if (currentVersion !== global.window.mm_config.Version) {
- sessionStorage.clear();
- try {
- sessionStorage.setItem('storage_version', global.window.mm_config.Version);
- } catch (e) {
- // Do nothing
- }
- }
- }
-
- getItem(name, defaultValue) {
- var result = null;
- try {
- result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
- } catch (err) {
- result = null;
- }
-
- if (result === null && typeof defaultValue !== 'undefined') {
- result = defaultValue;
- }
-
- return result;
- }
-
- setItem(name, value) {
- sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
- }
-
- removeItem(name) {
- sessionStorage.removeItem(getPrefix() + name);
- }
-
- setGlobalItem(name, value) {
- try {
- if (this.isLocalStorageSupported()) {
- localStorage.setItem(getPrefix() + name, JSON.stringify(value));
- } else {
- sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
- }
- } catch (err) {
- console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
- localStorage.clear();
- sessionStorage.clear();
- window.location.reload(true);
- }
- }
-
- getGlobalItem(name, defaultValue) {
- var result = null;
- try {
- if (this.isLocalStorageSupported()) {
- result = JSON.parse(localStorage.getItem(getPrefix() + name));
- } else {
- result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
- }
- } catch (err) {
- result = null;
- }
-
- if (result === null && typeof defaultValue !== 'undefined') {
- result = defaultValue;
- }
-
- return result;
- }
-
- removeGlobalItem(name) {
- if (this.isLocalStorageSupported()) {
- localStorage.removeItem(getPrefix() + name);
- } else {
- sessionStorage.removeItem(getPrefix() + name);
- }
- }
-
- getLastServerVersion() {
- return sessionStorage.getItem('last_server_version');
- }
-
- setLastServerVersion(version) {
- sessionStorage.setItem('last_server_version', version);
- }
-
- signalLogout() {
- if (this.isLocalStorageSupported()) {
- // PLT-1285 store an identifier in session storage so we can catch if the logout came from this tab on IE11
- const logoutId = generateId();
-
- sessionStorage.setItem('__logout__', logoutId);
- localStorage.setItem('__logout__', logoutId);
- localStorage.removeItem('__logout__');
- }
- }
-
- isSignallingLogout(logoutId) {
- return logoutId === sessionStorage.getItem('__logout__');
- }
-
- signalLogin() {
- if (this.isLocalStorageSupported()) {
- // PLT-1285 store an identifier in session storage so we can catch if the logout came from this tab on IE11
- const loginId = generateId();
-
- sessionStorage.setItem('__login__', loginId);
- localStorage.setItem('__login__', loginId);
- localStorage.removeItem('__login__');
- }
- }
-
- isSignallingLogin(loginId) {
- return loginId === sessionStorage.getItem('__login__');
- }
-
- /**
- * Preforms the given action on each item that has the given prefix
- * Signature for action is action(key, value)
- */
- actionOnGlobalItemsWithPrefix(prefix, action) {
- var globalPrefix = getPrefix();
- var globalPrefixiLen = globalPrefix.length;
-
- var storage = sessionStorage;
- if (this.isLocalStorageSupported()) {
- storage = localStorage;
- }
-
- for (var key in storage) {
- if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
- var userkey = key.substring(globalPrefixiLen);
- action(userkey, this.getGlobalItem(key));
- }
- }
- }
-
- actionOnItemsWithPrefix(prefix, action) {
- var globalPrefix = getPrefix();
- var globalPrefixiLen = globalPrefix.length;
- for (var key in sessionStorage) {
- if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
- var userkey = key.substring(globalPrefixiLen);
- action(userkey, this.getGlobalItem(key));
- }
- }
- }
-
- clear() {
- // don't clear the logout id so IE11 can tell which tab sent a logout request
- const logoutId = sessionStorage.getItem('__logout__');
-
- sessionStorage.clear();
-
- if (logoutId) {
- sessionStorage.setItem('__logout__', logoutId);
- }
- }
-
- clearAll() {
- sessionStorage.clear();
- localStorage.clear();
- }
-
- isLocalStorageSupported() {
- if (this.checkedLocalStorageSupported !== '') {
- return this.checkedLocalStorageSupported;
- }
-
- try {
- sessionStorage.setItem('__testSession__', '1');
- sessionStorage.removeItem('__testSession__');
-
- localStorage.setItem('__testLocal__', '1');
- if (localStorage.getItem('__testLocal__') !== '1') {
- this.checkedLocalStorageSupported = false;
- }
- localStorage.removeItem('__testLocal__', '1');
-
- this.checkedLocalStorageSupported = true;
- } catch (e) {
- this.checkedLocalStorageSupported = false;
- }
-
- return this.checkedLocalStorageSupported;
- }
-}
-
-var BrowserStore = new BrowserStoreClass();
-export default BrowserStore;
-window.BrowserStore = BrowserStore;
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
deleted file mode 100644
index 60cb10de7..000000000
--- a/web/react/stores/channel_store.jsx
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-var Utils;
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-const NotificationPrefs = Constants.NotificationPrefs;
-
-const CHANGE_EVENT = 'change';
-const LEAVE_EVENT = 'leave';
-const MORE_CHANGE_EVENT = 'change';
-const EXTRA_INFO_EVENT = 'extra_info';
-
-class ChannelStoreClass extends EventEmitter {
- constructor(props) {
- super(props);
-
- this.setMaxListeners(15);
-
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
- this.emitMoreChange = this.emitMoreChange.bind(this);
- this.addMoreChangeListener = this.addMoreChangeListener.bind(this);
- this.removeMoreChangeListener = this.removeMoreChangeListener.bind(this);
- this.emitExtraInfoChange = this.emitExtraInfoChange.bind(this);
- this.addExtraInfoChangeListener = this.addExtraInfoChangeListener.bind(this);
- this.removeExtraInfoChangeListener = this.removeExtraInfoChangeListener.bind(this);
- this.emitLeave = this.emitLeave.bind(this);
- this.addLeaveListener = this.addLeaveListener.bind(this);
- this.removeLeaveListener = this.removeLeaveListener.bind(this);
- this.findFirstBy = this.findFirstBy.bind(this);
- this.get = this.get.bind(this);
- this.getMember = this.getMember.bind(this);
- this.getByName = this.getByName.bind(this);
- this.setPostMode = this.setPostMode.bind(this);
- this.getPostMode = this.getPostMode.bind(this);
- this.setUnreadCount = this.setUnreadCount.bind(this);
- this.setUnreadCounts = this.setUnreadCounts.bind(this);
- this.getUnreadCount = this.getUnreadCount.bind(this);
- this.getUnreadCounts = this.getUnreadCounts.bind(this);
-
- this.currentId = null;
- this.postMode = this.POST_MODE_CHANNEL;
- this.channels = [];
- this.channelMembers = {};
- this.moreChannels = {};
- this.moreChannels.loading = true;
- this.extraInfos = {};
- this.unreadCounts = {};
- }
- get POST_MODE_CHANNEL() {
- return 1;
- }
- get POST_MODE_FOCUS() {
- return 2;
- }
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
- emitMoreChange() {
- this.emit(MORE_CHANGE_EVENT);
- }
- addMoreChangeListener(callback) {
- this.on(MORE_CHANGE_EVENT, callback);
- }
- removeMoreChangeListener(callback) {
- this.removeListener(MORE_CHANGE_EVENT, callback);
- }
- emitExtraInfoChange() {
- this.emit(EXTRA_INFO_EVENT);
- }
- addExtraInfoChangeListener(callback) {
- this.on(EXTRA_INFO_EVENT, callback);
- }
- removeExtraInfoChangeListener(callback) {
- this.removeListener(EXTRA_INFO_EVENT, callback);
- }
- emitLeave(id) {
- this.emit(LEAVE_EVENT, id);
- }
- addLeaveListener(callback) {
- this.on(LEAVE_EVENT, callback);
- }
- removeLeaveListener(callback) {
- this.removeListener(LEAVE_EVENT, callback);
- }
- findFirstBy(field, value) {
- var channels = this.getChannels();
- for (var i = 0; i < channels.length; i++) {
- if (channels[i][field] === value) {
- return channels[i];
- }
- }
-
- return null;
- }
- get(id) {
- return this.findFirstBy('id', id);
- }
- getMember(id) {
- return this.getAllMembers()[id];
- }
- getByName(name) {
- return this.findFirstBy('name', name);
- }
- getAll() {
- return this.getChannels();
- }
- getAllMembers() {
- return this.getChannelMembers();
- }
- getMoreAll() {
- return this.getMoreChannels();
- }
- setCurrentId(id) {
- this.currentId = id;
- }
- resetCounts(id) {
- const cm = this.channelMembers;
- for (var cmid in cm) {
- if (cm[cmid].channel_id === id) {
- var c = this.get(id);
- if (c) {
- cm[cmid].msg_count = this.get(id).total_msg_count;
- cm[cmid].mention_count = 0;
- this.setUnreadCount(id);
- }
- break;
- }
- }
- }
- getCurrentId() {
- return this.currentId;
- }
- getCurrent() {
- var currentId = this.getCurrentId();
-
- if (currentId) {
- return this.get(currentId);
- }
-
- return null;
- }
- getCurrentMember() {
- var currentId = this.getCurrentId();
-
- if (currentId) {
- return this.getAllMembers()[currentId];
- }
-
- return null;
- }
- setChannelMember(member) {
- var members = this.getChannelMembers();
- members[member.channel_id] = member;
- this.storeChannelMembers(members);
- this.emitChange();
- }
- getCurrentExtraInfo() {
- return this.getExtraInfo(this.getCurrentId());
- }
- getExtraInfo(channelId) {
- var extra = null;
-
- if (channelId) {
- extra = this.getExtraInfos()[channelId];
- }
-
- if (extra) {
- // create a defensive copy
- extra = JSON.parse(JSON.stringify(extra));
- } else {
- extra = {members: []};
- }
-
- return extra;
- }
- pStoreChannel(channel) {
- var channels = this.getChannels();
- var found;
-
- for (var i = 0; i < channels.length; i++) {
- if (channels[i].id === channel.id) {
- channels[i] = channel;
- found = true;
- break;
- }
- }
-
- if (!found) {
- channels.push(channel);
- }
-
- if (!Utils) {
- Utils = require('../utils/utils.jsx'); //eslint-disable-line global-require
- }
-
- channels.sort(Utils.sortByDisplayName);
- this.storeChannels(channels);
- }
- storeChannels(channels) {
- this.channels = channels;
- }
- getChannels() {
- return this.channels;
- }
- pStoreChannelMember(channelMember) {
- var members = this.getChannelMembers();
- members[channelMember.channel_id] = channelMember;
- this.storeChannelMembers(members);
- }
- storeChannelMembers(channelMembers) {
- this.channelMembers = channelMembers;
- }
- getChannelMembers() {
- return this.channelMembers;
- }
- storeMoreChannels(channels) {
- this.moreChannels = channels;
- }
- getMoreChannels() {
- return this.moreChannels;
- }
- storeExtraInfos(extraInfos) {
- this.extraInfos = extraInfos;
- }
- getExtraInfos() {
- return this.extraInfos;
- }
- isDefault(channel) {
- return channel.name === Constants.DEFAULT_CHANNEL;
- }
-
- setPostMode(mode) {
- this.postMode = mode;
- }
-
- getPostMode() {
- return this.postMode;
- }
-
- setUnreadCount(id) {
- const ch = this.get(id);
- const chMember = this.getMember(id);
-
- let chMentionCount = chMember.mention_count;
- let chUnreadCount = ch.total_msg_count - chMember.msg_count - chMentionCount;
-
- if (ch.type === 'D') {
- chMentionCount = chUnreadCount;
- chUnreadCount = 0;
- } else if (chMember.notify_props && chMember.notify_props.mark_unread === NotificationPrefs.MENTION) {
- chUnreadCount = 0;
- }
-
- this.unreadCounts[id] = {msgs: chUnreadCount, mentions: chMentionCount};
- }
-
- setUnreadCounts() {
- const channels = this.getAll();
- channels.forEach((ch) => {
- this.setUnreadCount(ch.id);
- });
- }
-
- getUnreadCount(id) {
- return this.unreadCounts[id] || {msgs: 0, mentions: 0};
- }
-
- getUnreadCounts() {
- return this.unreadCounts;
- }
-}
-
-var ChannelStore = new ChannelStoreClass();
-
-ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
- var currentId;
-
- switch (action.type) {
- case ActionTypes.CLICK_CHANNEL:
- ChannelStore.setCurrentId(action.id);
- ChannelStore.resetCounts(action.id);
- ChannelStore.setPostMode(ChannelStore.POST_MODE_CHANNEL);
- ChannelStore.emitChange();
- break;
-
- case ActionTypes.RECEIVED_FOCUSED_POST: {
- const post = action.post_list.posts[action.postId];
- ChannelStore.setCurrentId(post.channel_id);
- ChannelStore.setPostMode(ChannelStore.POST_MODE_FOCUS);
- ChannelStore.emitChange();
- break;
- }
-
- case ActionTypes.RECEIVED_CHANNELS:
- ChannelStore.storeChannels(action.channels);
- ChannelStore.storeChannelMembers(action.members);
- currentId = ChannelStore.getCurrentId();
- if (currentId && window.isActive) {
- ChannelStore.resetCounts(currentId);
- }
- ChannelStore.setUnreadCounts();
- ChannelStore.emitChange();
- break;
-
- case ActionTypes.RECEIVED_CHANNEL:
- ChannelStore.pStoreChannel(action.channel);
- if (action.member) {
- ChannelStore.pStoreChannelMember(action.member);
- }
- currentId = ChannelStore.getCurrentId();
- if (currentId && window.isActive) {
- ChannelStore.resetCounts(currentId);
- }
- ChannelStore.setUnreadCount(action.channel.id);
- ChannelStore.emitChange();
- break;
-
- case ActionTypes.RECEIVED_MORE_CHANNELS:
- ChannelStore.storeMoreChannels(action.channels);
- ChannelStore.emitMoreChange();
- break;
-
- case ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO:
- var extraInfos = ChannelStore.getExtraInfos();
- extraInfos[action.extra_info.id] = action.extra_info;
- ChannelStore.storeExtraInfos(extraInfos);
- ChannelStore.emitExtraInfoChange();
- break;
-
- case ActionTypes.LEAVE_CHANNEL:
- ChannelStore.emitLeave(action.id);
- break;
-
- default:
- break;
- }
-});
-
-export default ChannelStore;
diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx
deleted file mode 100644
index 6928b1e59..000000000
--- a/web/react/stores/error_store.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-import BrowserStore from '../stores/browser_store.jsx';
-
-const CHANGE_EVENT = 'change';
-
-class ErrorStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
- this.getLastError = this.getLastError.bind(this);
- this.storeLastError = this.storeLastError.bind(this);
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- getLastError() {
- return BrowserStore.getItem('last_error');
- }
-
- storeLastError(error) {
- BrowserStore.setItem('last_error', error);
- }
-
- getConnectionErrorCount() {
- var count = BrowserStore.getItem('last_error_conn');
-
- if (count == null) {
- return 0;
- }
-
- return count;
- }
-
- setConnectionErrorCount(count) {
- BrowserStore.setItem('last_error_conn', count);
- }
-
- clearLastError() {
- BrowserStore.removeItem('last_error');
- BrowserStore.removeItem('last_error_conn');
- }
-}
-
-var ErrorStore = new ErrorStoreClass();
-
-ErrorStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
- switch (action.type) {
- case ActionTypes.RECEIVED_ERROR:
- ErrorStore.storeLastError(action.err);
- ErrorStore.emitChange();
- break;
-
- default:
- }
-});
-
-export default ErrorStore;
-window.ErrorStore = ErrorStore;
diff --git a/web/react/stores/file_store.jsx b/web/react/stores/file_store.jsx
deleted file mode 100644
index 6d7e0f354..000000000
--- a/web/react/stores/file_store.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-import EventEmitter from 'events';
-
-const ActionTypes = Constants.ActionTypes;
-
-const CHANGE_EVENT = 'changed';
-
-class FileStore extends EventEmitter {
- constructor() {
- super();
-
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
- this.emitChange = this.emitChange.bind(this);
-
- this.handleEventPayload = this.handleEventPayload.bind(this);
- this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
-
- this.fileInfo = new Map();
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
- emitChange(filename) {
- this.emit(CHANGE_EVENT, filename);
- }
-
- hasInfo(filename) {
- return this.fileInfo.has(filename);
- }
-
- getInfo(filename) {
- return this.fileInfo.get(filename);
- }
-
- setInfo(filename, info) {
- this.fileInfo.set(filename, info);
- }
-
- handleEventPayload(payload) {
- const action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_FILE_INFO:
- this.setInfo(action.filename, action.info);
- this.emitChange(action.filename);
- break;
- }
- }
-}
-
-export default new FileStore();
diff --git a/web/react/stores/localization_store.jsx b/web/react/stores/localization_store.jsx
deleted file mode 100644
index 0e3a63724..000000000
--- a/web/react/stores/localization_store.jsx
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const CHANGE_EVENT = 'change';
-
-class LocalizationStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.currentLocale = 'en';
- this.currentTranslations = null;
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- setCurrentLocale(locale, translations) {
- this.currentLocale = locale;
- this.currentTranslations = translations;
- }
-
- getLocale() {
- return this.currentLocale;
- }
-
- getTranslations() {
- return this.currentTranslations;
- }
-}
-
-var LocalizationStore = new LocalizationStoreClass();
-LocalizationStore.setMaxListeners(0);
-
-LocalizationStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_LOCALE:
- LocalizationStore.setCurrentLocale(action.locale, action.translations);
- LocalizationStore.emitChange();
- break;
- default:
- }
-});
-
-export default LocalizationStore;
diff --git a/web/react/stores/modal_store.jsx b/web/react/stores/modal_store.jsx
deleted file mode 100644
index 5ea38030b..000000000
--- a/web/react/stores/modal_store.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-class ModalStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.addModalListener = this.addModalListener.bind(this);
- this.removeModalListener = this.removeModalListener.bind(this);
-
- this.handleEventPayload = this.handleEventPayload.bind(this);
- this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
- }
-
- addModalListener(action, callback) {
- this.on(action, callback);
- }
-
- removeModalListener(action, callback) {
- this.removeListener(action, callback);
- }
-
- handleEventPayload(payload) {
- // toggle event handlers should accept a boolean show/hide value and can accept a map of arguments
- const {type, value, ...args} = payload.action; //eslint-disable-line no-use-before-define
-
- switch (type) {
- case ActionTypes.TOGGLE_IMPORT_THEME_MODAL:
- case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL:
- case ActionTypes.TOGGLE_DELETE_POST_MODAL:
- case ActionTypes.TOGGLE_GET_POST_LINK_MODAL:
- case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL:
- case ActionTypes.TOGGLE_REGISTER_APP_MODAL:
- this.emit(type, value, args);
- break;
- }
- }
-}
-
-const ModalStore = new ModalStoreClass();
-export default ModalStore;
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
deleted file mode 100644
index a6dfcd46f..000000000
--- a/web/react/stores/post_store.jsx
+++ /dev/null
@@ -1,610 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import ChannelStore from '../stores/channel_store.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const CHANGE_EVENT = 'change';
-const FOCUSED_POST_CHANGE = 'focused_post_change';
-const EDIT_POST_EVENT = 'edit_post';
-const POSTS_VIEW_JUMP_EVENT = 'post_list_jump';
-const SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
-
-class PostStoreClass extends EventEmitter {
- constructor() {
- super();
- this.selectedPostId = null;
- this.postsInfo = {};
- this.currentFocusedPostId = null;
- }
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- emitPostFocused() {
- this.emit(FOCUSED_POST_CHANGE);
- }
-
- addPostFocusedListener(callback) {
- this.on(FOCUSED_POST_CHANGE, callback);
- }
-
- removePostFocusedListener(callback) {
- this.removeListener(FOCUSED_POST_CHANGE, callback);
- }
-
- emitEditPost(post) {
- this.emit(EDIT_POST_EVENT, post);
- }
-
- addEditPostListener(callback) {
- this.on(EDIT_POST_EVENT, callback);
- }
-
- removeEditPostListner(callback) {
- this.removeListener(EDIT_POST_EVENT, callback);
- }
-
- emitPostsViewJump(type, post) {
- this.emit(POSTS_VIEW_JUMP_EVENT, type, post);
- }
-
- addPostsViewJumpListener(callback) {
- this.on(POSTS_VIEW_JUMP_EVENT, callback);
- }
-
- removePostsViewJumpListener(callback) {
- this.removeListener(POSTS_VIEW_JUMP_EVENT, callback);
- }
-
- jumpPostsViewToBottom() {
- this.emitPostsViewJump(Constants.PostsViewJumpTypes.BOTTOM, null);
- }
-
- jumpPostsViewToPost(post) {
- this.emitPostsViewJump(Constants.PostsViewJumpTypes.POST, post);
- }
-
- jumpPostsViewSidebarOpen() {
- this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null);
- }
-
- // All this does is makes sure the postsInfo is not null for the specified channel
- makePostsInfo(id) {
- if (!this.postsInfo.hasOwnProperty(id)) {
- this.postsInfo[id] = {};
- }
- }
-
- getPost(channelId, postId) {
- const posts = this.postsInfo[channelId].postList;
- let post = null;
-
- if (posts.posts.hasOwnProperty(postId)) {
- post = Object.assign({}, posts.posts[postId]);
- }
-
- return post;
- }
-
- getAllPosts(id) {
- if (this.postsInfo.hasOwnProperty(id)) {
- return Object.assign({}, this.postsInfo[id].postList);
- }
-
- return null;
- }
-
- getEarliestPost(id) {
- if (this.postsInfo.hasOwnProperty(id)) {
- return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[this.postsInfo[id].postList.order.length - 1]];
- }
-
- return null;
- }
-
- getLatestPost(id) {
- if (this.postsInfo.hasOwnProperty(id)) {
- return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[0]];
- }
-
- return null;
- }
-
- getVisiblePosts(id) {
- if (this.postsInfo.hasOwnProperty(id) && this.postsInfo[id].hasOwnProperty('postList')) {
- const postList = JSON.parse(JSON.stringify(this.postsInfo[id].postList));
-
- // Only limit visibility if we are not focused on a post
- if (this.currentFocusedPostId === null) {
- postList.order = postList.order.slice(0, this.postsInfo[id].endVisible);
- }
-
- // Add pending posts
- if (this.postsInfo[id].hasOwnProperty('pendingPosts')) {
- Object.assign(postList.posts, this.postsInfo[id].pendingPosts.posts);
- postList.order = this.postsInfo[id].pendingPosts.order.concat(postList.order);
- }
-
- return postList;
- }
-
- return null;
- }
-
- getVisibilityAtTop(id) {
- if (this.postsInfo.hasOwnProperty(id)) {
- return this.postsInfo[id].atTop && this.postsInfo[id].endVisible >= this.postsInfo[id].postList.order.length;
- }
-
- return false;
- }
-
- getVisibilityAtBottom(id) {
- if (this.postsInfo.hasOwnProperty(id)) {
- return this.postsInfo[id].atBottom;
- }
-
- return false;
- }
-
- // Returns true if posts need to be fetched
- requestVisibilityIncrease(id, ammount) {
- const endVisible = this.postsInfo[id].endVisible;
- const postList = this.postsInfo[id].postList;
- if (this.getVisibilityAtTop(id)) {
- return false;
- }
- this.postsInfo[id].endVisible += ammount;
- this.emitChange();
- return endVisible + ammount > postList.order.length;
- }
-
- getFocusedPostId() {
- return this.currentFocusedPostId;
- }
-
- storePosts(id, newPosts) {
- if (isPostListNull(newPosts)) {
- return;
- }
-
- const combinedPosts = makePostListNonNull(this.getAllPosts(id));
-
- for (const pid in newPosts.posts) {
- if (newPosts.posts.hasOwnProperty(pid)) {
- const np = newPosts.posts[pid];
- if (np.delete_at === 0) {
- combinedPosts.posts[pid] = np;
- if (combinedPosts.order.indexOf(pid) === -1 && newPosts.order.indexOf(pid) !== -1) {
- combinedPosts.order.push(pid);
- }
- }
- }
- }
-
- combinedPosts.order.sort((a, b) => {
- if (combinedPosts.posts[a].create_at > combinedPosts.posts[b].create_at) {
- return -1;
- }
- if (combinedPosts.posts[a].create_at < combinedPosts.posts[b].create_at) {
- return 1;
- }
-
- return 0;
- });
-
- this.makePostsInfo(id);
- this.postsInfo[id].postList = combinedPosts;
- }
-
- storePost(post) {
- const postList = makePostListNonNull(this.getAllPosts(post.channel_id));
-
- if (post.pending_post_id !== '') {
- this.removePendingPost(post.channel_id, post.pending_post_id);
- }
-
- post.pending_post_id = '';
-
- postList.posts[post.id] = post;
- if (postList.order.indexOf(post.id) === -1) {
- postList.order.unshift(post.id);
- }
-
- this.makePostsInfo(post.channel_id);
- this.postsInfo[post.channel_id].postList = postList;
- }
-
- storeFocusedPost(postId, postList) {
- const focusedPost = postList.posts[postId];
- if (!focusedPost) {
- return;
- }
- this.currentFocusedPostId = postId;
- this.storePosts(postId, postList);
- }
-
- checkBounds(id, numRequested, postList, before) {
- if (numRequested > postList.order.length) {
- if (before) {
- this.postsInfo[id].atTop = true;
- } else {
- this.postsInfo[id].atBottom = true;
- }
- }
- }
-
- clearFocusedPost() {
- if (this.currentFocusedPostId != null) {
- Reflect.deleteProperty(this.postsInfo, this.currentFocusedPostId);
- this.currentFocusedPostId = null;
- }
- }
-
- clearChannelVisibility(id, atBottom) {
- this.makePostsInfo(id);
- this.postsInfo[id].endVisible = Constants.POST_CHUNK_SIZE;
- this.postsInfo[id].atTop = false;
- this.postsInfo[id].atBottom = atBottom;
- }
-
- deletePost(post) {
- const postInfo = this.postsInfo[post.channel_id];
- if (!postInfo) {
- // the post that has been deleted is in a channel that we haven't seen so just ignore it
- return;
- }
-
- const postList = this.postsInfo[post.channel_id].postList;
-
- if (isPostListNull(postList)) {
- return;
- }
-
- if (post.id in postList.posts) {
- // make sure to copy the post so that component state changes work properly
- postList.posts[post.id] = Object.assign({}, post, {
- state: Constants.POST_DELETED,
- filenames: []
- });
- }
- }
-
- removePost(post) {
- const channelId = post.channel_id;
- this.makePostsInfo(channelId);
- const postList = this.postsInfo[channelId].postList;
- if (isPostListNull(postList)) {
- return;
- }
-
- if (post.id in postList.posts) {
- Reflect.deleteProperty(postList.posts, post.id);
- }
-
- const index = postList.order.indexOf(post.id);
- if (index !== -1) {
- postList.order.splice(index, 1);
- }
-
- for (const pid in postList.posts) {
- if (!postList.posts.hasOwnProperty(pid)) {
- continue;
- }
-
- if (postList.posts[pid].root_id === post.id) {
- Reflect.deleteProperty(postList.posts, pid);
- const commentIndex = postList.order.indexOf(pid);
- if (commentIndex !== -1) {
- postList.order.splice(commentIndex, 1);
- }
- }
- }
-
- this.postsInfo[channelId].postList = postList;
- }
-
- getPendingPosts(channelId) {
- if (this.postsInfo.hasOwnProperty(channelId)) {
- return this.postsInfo[channelId].pendingPosts;
- }
-
- return null;
- }
-
- storePendingPost(post) {
- const copyPost = JSON.parse(JSON.stringify(post));
- copyPost.state = Constants.POST_LOADING;
-
- const postList = makePostListNonNull(this.getPendingPosts(copyPost.channel_id));
-
- postList.posts[copyPost.pending_post_id] = copyPost;
- postList.order.unshift(copyPost.pending_post_id);
-
- this.makePostsInfo(copyPost.channel_id);
- this.postsInfo[copyPost.channel_id].pendingPosts = postList;
- this.emitChange();
- }
-
- removePendingPost(channelId, pendingPostId) {
- const postList = makePostListNonNull(this.getPendingPosts(channelId));
-
- Reflect.deleteProperty(postList.posts, pendingPostId);
- const index = postList.order.indexOf(pendingPostId);
- if (index === -1) {
- return;
- }
-
- postList.order.splice(index, 1);
-
- this.postsInfo[channelId].pendingPosts = postList;
- this.emitChange();
- }
-
- clearPendingPosts(channelId) {
- if (this.postsInfo.hasOwnProperty(channelId)) {
- Reflect.deleteProperty(this.postsInfo[channelId], 'pendingPosts');
- }
- }
-
- updatePendingPost(post) {
- const copyPost = JSON.parse(JSON.stringify(post));
- const postList = makePostListNonNull(this.getPendingPosts(copyPost.channel_id));
-
- if (postList.order.indexOf(copyPost.pending_post_id) === -1) {
- return;
- }
-
- postList.posts[copyPost.pending_post_id] = copyPost;
- this.postsInfo[copyPost.channel_id].pendingPosts = postList;
- this.emitChange();
- }
-
- storeSelectedPostId(postId) {
- this.selectedPostId = postId;
- }
-
- getSelectedPostId() {
- return this.selectedPostId;
- }
-
- getSelectedPost() {
- if (this.selectedPostId == null) {
- return null;
- }
-
- for (const k in this.postsInfo) {
- if (this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
- return this.postsInfo[k].postList.posts[this.selectedPostId];
- }
- }
-
- return null;
- }
-
- getSelectedPostThread() {
- if (this.selectedPostId == null) {
- return null;
- }
-
- let posts;
- let pendingPosts;
- for (const k in this.postsInfo) {
- if (this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
- posts = this.postsInfo[k].postList.posts;
- if (this.postsInfo[k].pendingPosts != null) {
- pendingPosts = this.postsInfo[k].pendingPosts.posts;
- }
- }
- }
-
- const threadPosts = {};
- const rootId = this.selectedPostId;
- for (const k in posts) {
- if (posts[k].root_id === rootId) {
- threadPosts[k] = JSON.parse(JSON.stringify(posts[k]));
- }
- }
-
- for (const k in pendingPosts) {
- if (pendingPosts[k].root_id === rootId) {
- threadPosts[k] = JSON.parse(JSON.stringify(pendingPosts[k]));
- }
- }
-
- return threadPosts;
- }
-
- emitSelectedPostChange(fromSearch) {
- this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch);
- }
-
- addSelectedPostChangeListener(callback) {
- this.on(SELECTED_POST_CHANGE_EVENT, callback);
- }
-
- removeSelectedPostChangeListener(callback) {
- this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
- }
-
- getCurrentUsersLatestPost(channelId, rootId) {
- const userId = UserStore.getCurrentId();
- var postList = makePostListNonNull(this.getAllPosts(channelId));
- var i = 0;
- var len = postList.order.length;
- var lastPost = null;
-
- for (i; i < len; i++) {
- const post = postList.posts[postList.order[i]];
- if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) {
- if (rootId) {
- if (post.root_id === rootId || post.id === rootId) {
- lastPost = post;
- break;
- }
- } else {
- lastPost = post;
- break;
- }
- }
- }
-
- return lastPost;
- }
-
- getEmptyDraft() {
- return {message: '', uploadsInProgress: [], previews: []};
- }
- storeCurrentDraft(draft) {
- var channelId = ChannelStore.getCurrentId();
- BrowserStore.setGlobalItem('draft_' + channelId, draft);
- }
- getCurrentDraft() {
- var channelId = ChannelStore.getCurrentId();
- return this.getDraft(channelId);
- }
- storeDraft(channelId, draft) {
- BrowserStore.setGlobalItem('draft_' + channelId, draft);
- }
- getDraft(channelId) {
- return BrowserStore.getGlobalItem('draft_' + channelId, this.getEmptyDraft());
- }
- storeCommentDraft(parentPostId, draft) {
- BrowserStore.setGlobalItem('comment_draft_' + parentPostId, draft);
- }
- getCommentDraft(parentPostId) {
- return BrowserStore.getGlobalItem('comment_draft_' + parentPostId, this.getEmptyDraft());
- }
- clearDraftUploads() {
- BrowserStore.actionOnGlobalItemsWithPrefix('draft_', (key, value) => {
- if (value) {
- value.uploadsInProgress = [];
- BrowserStore.setItem(key, value);
- }
- });
- }
- clearCommentDraftUploads() {
- BrowserStore.actionOnGlobalItemsWithPrefix('comment_draft_', (key, value) => {
- if (value) {
- value.uploadsInProgress = [];
- BrowserStore.setItem(key, value);
- }
- });
- }
- getCommentCount(post) {
- const posts = this.getAllPosts(post.channel_id).posts;
-
- let commentCount = 0;
- for (const id in posts) {
- if (posts.hasOwnProperty(id)) {
- if (posts[id].root_id === post.id) {
- commentCount += 1;
- }
- }
- }
-
- return commentCount;
- }
-}
-
-var PostStore = new PostStoreClass();
-
-PostStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_POSTS: {
- const id = PostStore.currentFocusedPostId == null ? action.id : PostStore.currentFocusedPostId;
- PostStore.checkBounds(id, action.numRequested, makePostListNonNull(action.post_list), action.before);
- PostStore.storePosts(id, makePostListNonNull(action.post_list));
- PostStore.emitChange();
- break;
- }
- case ActionTypes.RECEIVED_FOCUSED_POST:
- PostStore.clearChannelVisibility(action.postId, false);
- PostStore.storeFocusedPost(action.postId, makePostListNonNull(action.post_list));
- PostStore.emitChange();
- break;
- case ActionTypes.RECEIVED_POST:
- PostStore.storePost(action.post);
- PostStore.emitChange();
- break;
- case ActionTypes.RECEIVED_EDIT_POST:
- PostStore.emitEditPost(action);
- PostStore.emitChange();
- break;
- case ActionTypes.CLICK_CHANNEL:
- PostStore.clearFocusedPost();
- PostStore.clearChannelVisibility(action.id, true);
- break;
- case ActionTypes.CREATE_POST:
- PostStore.storePendingPost(action.post);
- PostStore.storeDraft(action.post.channel_id, null);
- PostStore.jumpPostsViewToBottom();
- break;
- case ActionTypes.POST_DELETED:
- PostStore.deletePost(action.post);
- PostStore.emitChange();
- break;
- case ActionTypes.REMOVE_POST:
- PostStore.removePost(action.post);
- PostStore.emitChange();
- break;
- case ActionTypes.RECEIVED_POST_SELECTED:
- PostStore.storeSelectedPostId(action.postId);
- PostStore.emitSelectedPostChange(action.from_search);
- break;
- default:
- }
-});
-
-export default PostStore;
-
-function makePostListNonNull(pl) {
- var postList = pl;
- if (postList == null) {
- postList = {order: [], posts: {}};
- }
-
- if (postList.order == null) {
- postList.order = [];
- }
-
- if (postList.posts == null) {
- postList.posts = {};
- }
-
- return postList;
-}
-
-function isPostListNull(pl) {
- if (pl == null) {
- return true;
- }
-
- if (pl.posts == null) {
- return true;
- }
-
- if (pl.order == null) {
- return true;
- }
-
- return false;
-}
diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx
deleted file mode 100644
index 30f5468c2..000000000
--- a/web/react/stores/preference_store.jsx
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import BrowserStore from './browser_store.jsx';
-import EventEmitter from 'events';
-import UserStore from '../stores/user_store.jsx';
-
-const CHANGE_EVENT = 'change';
-
-function getPreferenceKey(category, name) {
- return `${category}-${name}`;
-}
-
-function getPreferenceKeyForModel(preference) {
- return `${preference.category}-${preference.name}`;
-}
-
-class PreferenceStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.getAllPreferences = this.getAllPreferences.bind(this);
- this.get = this.get.bind(this);
- this.getBool = this.getBool.bind(this);
- this.getInt = this.getInt.bind(this);
- this.getPreference = this.getPreference.bind(this);
- this.getCategory = this.getCategory.bind(this);
- this.getPreferencesWhere = this.getPreferencesWhere.bind(this);
- this.setAllPreferences = this.setAllPreferences.bind(this);
- this.setPreference = this.setPreference.bind(this);
-
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
-
- this.handleEventPayload = this.handleEventPayload.bind(this);
- this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
- }
-
- getAllPreferences() {
- return new Map(BrowserStore.getItem('preferences', []));
- }
-
- get(category, name, defaultValue = '') {
- const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
-
- if (!preference) {
- return defaultValue;
- }
-
- return preference.value || defaultValue;
- }
-
- getBool(category, name, defaultValue = false) {
- const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
-
- if (!preference) {
- return defaultValue;
- }
-
- // prevent a non-false default value from being returned instead of an actual false value
- if (preference.value === 'false') {
- return false;
- }
-
- return (preference.value !== 'false') || defaultValue;
- }
-
- getInt(category, name, defaultValue = 0) {
- const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
-
- if (!preference) {
- return defaultValue;
- }
-
- // prevent a non-zero default value from being returned instead of an actual 0 value
- if (preference.value === '0') {
- return 0;
- }
-
- return parseInt(preference.value, 10) || defaultValue;
- }
-
- getPreference(category, name, defaultValue = {}) {
- return this.getAllPreferences().get(getPreferenceKey(category, name)) || defaultValue;
- }
-
- getCategory(category) {
- return this.getPreferencesWhere((preference) => (preference.category === category));
- }
-
- getPreferencesWhere(pred) {
- const all = this.getAllPreferences();
- const preferences = [];
-
- for (const [, preference] of all) {
- if (pred(preference)) {
- preferences.push(preference);
- }
- }
-
- return preferences;
- }
-
- setAllPreferences(preferences) {
- // note that we store the preferences as an array of key-value pairs so that we can deserialize
- // it as a proper Map instead of an object
- BrowserStore.setItem('preferences', [...preferences]);
- }
-
- setPreference(category, name, value) {
- const preferences = this.getAllPreferences();
-
- const key = getPreferenceKey(category, name);
- let preference = preferences.get(key);
-
- if (!preference) {
- preference = {
- user_id: UserStore.getCurrentId(),
- category,
- name
- };
- }
- preference.value = value;
-
- preferences.set(key, preference);
-
- this.setAllPreferences(preferences);
-
- return preference;
- }
-
- setPreferences(newPreferences) {
- const preferences = this.getAllPreferences();
-
- for (const preference of newPreferences) {
- preferences.set(getPreferenceKeyForModel(preference), preference);
- }
-
- this.setAllPreferences(preferences);
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- handleEventPayload(payload) {
- const action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_PREFERENCE: {
- const preference = action.preference;
- this.setPreference(preference.category, preference.name, preference.value);
- this.emitChange();
- break;
- }
- case ActionTypes.RECEIVED_PREFERENCES:
- this.setPreferences(action.preferences);
- this.emitChange();
- break;
- }
- }
-}
-
-const PreferenceStore = new PreferenceStoreClass();
-export default PreferenceStore;
-window.PreferenceStore = PreferenceStore;
diff --git a/web/react/stores/search_store.jsx b/web/react/stores/search_store.jsx
deleted file mode 100644
index 549f355ef..000000000
--- a/web/react/stores/search_store.jsx
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import BrowserStore from '../stores/browser_store.jsx';
-
-import Constants from '../utils/constants.jsx';
-var ActionTypes = Constants.ActionTypes;
-
-var CHANGE_EVENT = 'change';
-var SEARCH_CHANGE_EVENT = 'search_change';
-var SEARCH_TERM_CHANGE_EVENT = 'search_term_change';
-var SHOW_SEARCH_EVENT = 'show_search';
-
-class SearchStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
-
- this.emitSearchChange = this.emitSearchChange.bind(this);
- this.addSearchChangeListener = this.addSearchChangeListener.bind(this);
- this.removeSearchChangeListener = this.removeSearchChangeListener.bind(this);
-
- this.emitSearchTermChange = this.emitSearchTermChange.bind(this);
- this.addSearchTermChangeListener = this.addSearchTermChangeListener.bind(this);
- this.removeSearchTermChangeListener = this.removeSearchTermChangeListener.bind(this);
-
- this.emitShowSearch = this.emitShowSearch.bind(this);
- this.addShowSearchListener = this.addShowSearchListener.bind(this);
- this.removeShowSearchListener = this.removeShowSearchListener.bind(this);
-
- this.getSearchResults = this.getSearchResults.bind(this);
- this.getIsMentionSearch = this.getIsMentionSearch.bind(this);
-
- this.storeSearchTerm = this.storeSearchTerm.bind(this);
- this.getSearchTerm = this.getSearchTerm.bind(this);
-
- this.storeSearchResults = this.storeSearchResults.bind(this);
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- emitSearchChange() {
- this.emit(SEARCH_CHANGE_EVENT);
- }
-
- addSearchChangeListener(callback) {
- this.on(SEARCH_CHANGE_EVENT, callback);
- }
-
- removeSearchChangeListener(callback) {
- this.removeListener(SEARCH_CHANGE_EVENT, callback);
- }
-
- emitSearchTermChange(doSearch, isMentionSearch) {
- this.emit(SEARCH_TERM_CHANGE_EVENT, doSearch, isMentionSearch);
- }
-
- addSearchTermChangeListener(callback) {
- this.on(SEARCH_TERM_CHANGE_EVENT, callback);
- }
-
- removeSearchTermChangeListener(callback) {
- this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback);
- }
-
- emitShowSearch() {
- this.emit(SHOW_SEARCH_EVENT);
- }
-
- addShowSearchListener(callback) {
- this.on(SHOW_SEARCH_EVENT, callback);
- }
-
- removeShowSearchListener(callback) {
- this.removeListener(SHOW_SEARCH_EVENT, callback);
- }
-
- getSearchResults() {
- return BrowserStore.getItem('search_results');
- }
-
- getIsMentionSearch() {
- return BrowserStore.getItem('is_mention_search');
- }
-
- storeSearchTerm(term) {
- BrowserStore.setItem('search_term', term);
- }
-
- getSearchTerm() {
- return BrowserStore.getItem('search_term');
- }
-
- storeSearchResults(results, isMentionSearch) {
- BrowserStore.setItem('search_results', results);
- BrowserStore.setItem('is_mention_search', Boolean(isMentionSearch));
- }
-}
-
-var SearchStore = new SearchStoreClass();
-
-SearchStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_SEARCH:
- SearchStore.storeSearchResults(action.results, action.is_mention_search);
- SearchStore.emitSearchChange();
- break;
- case ActionTypes.RECEIVED_SEARCH_TERM:
- SearchStore.storeSearchTerm(action.term);
- SearchStore.emitSearchTermChange(action.do_search, action.is_mention_search);
- break;
- case ActionTypes.SHOW_SEARCH:
- SearchStore.emitShowSearch();
- break;
- default:
- }
-});
-
-export default SearchStore;
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
deleted file mode 100644
index 181de53d7..000000000
--- a/web/react/stores/socket_store.jsx
+++ /dev/null
@@ -1,342 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from './user_store.jsx';
-import PostStore from './post_store.jsx';
-import ChannelStore from './channel_store.jsx';
-import BrowserStore from './browser_store.jsx';
-import ErrorStore from './error_store.jsx';
-import EventEmitter from 'events';
-
-import * as Utils from '../utils/utils.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import Constants from '../utils/constants.jsx';
-const SocketEvents = Constants.SocketEvents;
-
-const CHANGE_EVENT = 'change';
-
-var conn;
-
-class SocketStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.initialize = this.initialize.bind(this);
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
- this.sendMessage = this.sendMessage.bind(this);
- this.close = this.close.bind(this);
-
- this.failCount = 0;
- this.isInitialize = false;
-
- this.translations = this.getDefaultTranslations();
-
- this.initialize();
- }
-
- initialize() {
- if (!UserStore.getCurrentId()) {
- return;
- }
-
- this.setMaxListeners(0);
-
- if (window.WebSocket && !conn) {
- var protocol = 'ws://';
- if (window.location.protocol === 'https:') {
- protocol = 'wss://';
- }
-
- var connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + '/api/v1/websocket';
-
- if (this.failCount === 0) {
- console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
- }
-
- conn = new WebSocket(connUrl);
-
- conn.onopen = () => {
- if (this.failCount > 0) {
- console.log('websocket re-established connection'); //eslint-disable-line no-console
- AsyncClient.getChannels();
- AsyncClient.getPosts(ChannelStore.getCurrentId());
- }
-
- if (this.isInitialize) {
- ErrorStore.clearLastError();
- ErrorStore.emitChange();
- }
-
- this.isInitialize = true;
- this.failCount = 0;
- };
-
- conn.onclose = () => {
- conn = null;
-
- if (this.failCount === 0) {
- console.log('websocket closed'); //eslint-disable-line no-console
- }
-
- this.failCount = this.failCount + 1;
-
- if (this.failCount > 7) {
- ErrorStore.storeLastError({message: this.translations.socketError});
- }
-
- ErrorStore.setConnectionErrorCount(this.failCount);
- ErrorStore.emitChange();
-
- setTimeout(
- () => {
- this.initialize();
- },
- 3000
- );
- };
-
- conn.onerror = (evt) => {
- if (this.failCount <= 1) {
- console.log('websocket error'); //eslint-disable-line no-console
- console.log(evt); //eslint-disable-line no-console
- }
- };
-
- conn.onmessage = (evt) => {
- const msg = JSON.parse(evt.data);
- this.handleMessage(msg);
- this.emitChange(msg);
- };
- }
- }
-
- emitChange(msg) {
- this.emit(CHANGE_EVENT, msg);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- handleMessage(msg) {
- switch (msg.action) {
- case SocketEvents.POSTED:
- case SocketEvents.EPHEMERAL_MESSAGE:
- handleNewPostEvent(msg, this.translations);
- break;
-
- case SocketEvents.POST_EDITED:
- handlePostEditEvent(msg);
- break;
-
- case SocketEvents.POST_DELETED:
- handlePostDeleteEvent(msg);
- break;
-
- case SocketEvents.NEW_USER:
- handleNewUserEvent();
- break;
-
- case SocketEvents.USER_ADDED:
- handleUserAddedEvent(msg);
- break;
-
- case SocketEvents.USER_REMOVED:
- handleUserRemovedEvent(msg);
- break;
-
- case SocketEvents.CHANNEL_VIEWED:
- handleChannelViewedEvent(msg);
- break;
-
- case SocketEvents.PREFERENCE_CHANGED:
- handlePreferenceChangedEvent(msg);
- break;
-
- default:
- }
- }
-
- sendMessage(msg) {
- if (conn && conn.readyState === WebSocket.OPEN) {
- conn.send(JSON.stringify(msg));
- } else if (!conn || conn.readyState === WebSocket.Closed) {
- conn = null;
- this.initialize();
- }
- }
-
- setTranslations(messages) {
- this.translations = messages;
- }
-
- getDefaultTranslations() {
- return ({
- socketError: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.',
- someone: 'Someone',
- posted: 'Posted',
- uploadedImage: ' uploaded an image',
- uploadedFile: ' uploaded a file',
- something: ' did something new',
- wrote: ' wrote: '
- });
- }
-
- close() {
- if (conn && conn.readyState === WebSocket.OPEN) {
- conn.close();
- }
- }
-}
-
-function handleNewPostEvent(msg, translations) {
- // Store post
- const post = JSON.parse(msg.props.post);
- GlobalActions.emitPostRecievedEvent(post);
-
- // Update channel state
- if (ChannelStore.getCurrentId() === msg.channel_id) {
- if (window.isActive) {
- AsyncClient.updateLastViewedAt();
- } else {
- AsyncClient.getChannel(msg.channel_id);
- }
- } else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) {
- AsyncClient.getChannel(msg.channel_id);
- }
-
- // Send desktop notification
- if ((UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) {
- const msgProps = msg.props;
-
- let mentions = [];
- if (msgProps.mentions) {
- mentions = JSON.parse(msg.props.mentions);
- }
-
- const channel = ChannelStore.get(msg.channel_id);
- const user = UserStore.getCurrentUser();
- const member = ChannelStore.getMember(msg.channel_id);
-
- let notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
- if (notifyLevel === 'default') {
- notifyLevel = user.notify_props.desktop;
- }
-
- if (notifyLevel === 'none') {
- return;
- } else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== Constants.DM_CHANNEL) {
- return;
- }
-
- let username = translations.someone;
- if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
- username = post.props.override_username;
- } else if (UserStore.hasProfile(msg.user_id)) {
- username = UserStore.getProfile(msg.user_id).username;
- }
-
- let title = translations.posted;
- if (channel) {
- title = channel.display_name;
- }
-
- let notifyText = post.message.replace(/\n+/g, ' ');
- if (notifyText.length > 50) {
- notifyText = notifyText.substring(0, 49) + '...';
- }
-
- if (notifyText.length === 0) {
- if (msgProps.image) {
- Utils.notifyMe(title, username + translations.uploadedImage, channel);
- } else if (msgProps.otherFile) {
- Utils.notifyMe(title, username + translations.uploadedFile, channel);
- } else {
- Utils.notifyMe(title, username + translations.something, channel);
- }
- } else {
- Utils.notifyMe(title, username + translations.wrote + notifyText, channel);
- }
- if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
- Utils.ding();
- }
- }
-}
-
-function handlePostEditEvent(msg) {
- // Store post
- const post = JSON.parse(msg.props.post);
- PostStore.storePost(post);
- PostStore.emitChange();
-
- // Update channel state
- if (ChannelStore.getCurrentId() === msg.channel_id) {
- if (window.isActive) {
- AsyncClient.updateLastViewedAt();
- }
- }
-}
-
-function handlePostDeleteEvent(msg) {
- const post = JSON.parse(msg.props.post);
- GlobalActions.emitPostDeletedEvent(post);
-}
-
-function handleNewUserEvent() {
- AsyncClient.getProfiles();
- AsyncClient.getChannelExtraInfo();
-}
-
-function handleUserAddedEvent(msg) {
- if (ChannelStore.getCurrentId() === msg.channel_id) {
- AsyncClient.getChannelExtraInfo();
- }
-
- if (UserStore.getCurrentId() === msg.user_id) {
- AsyncClient.getChannel(msg.channel_id);
- }
-}
-
-function handleUserRemovedEvent(msg) {
- if (UserStore.getCurrentId() === msg.user_id) {
- AsyncClient.getChannels();
-
- if (msg.props.remover_id !== msg.user_id &&
- msg.channel_id === ChannelStore.getCurrentId() &&
- $('#removed_from_channel').length > 0) {
- var sentState = {};
- sentState.channelName = ChannelStore.getCurrent().display_name;
- sentState.remover = UserStore.getProfile(msg.props.remover_id).username;
-
- BrowserStore.setItem('channel-removed-state', sentState);
- $('#removed_from_channel').modal('show');
- }
- } else if (ChannelStore.getCurrentId() === msg.channel_id) {
- AsyncClient.getChannelExtraInfo();
- }
-}
-
-function handleChannelViewedEvent(msg) {
- // Useful for when multiple devices have the app open to different channels
- if (ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
- AsyncClient.getChannel(msg.channel_id);
- }
-}
-
-function handlePreferenceChangedEvent(msg) {
- const preference = JSON.parse(msg.props.preference);
- GlobalActions.emitPreferenceChangedEvent(preference);
-}
-
-var SocketStore = new SocketStoreClass();
-
-export default SocketStore;
-window.SocketStore = SocketStore;
diff --git a/web/react/stores/suggestion_store.jsx b/web/react/stores/suggestion_store.jsx
deleted file mode 100644
index efd2b76ed..000000000
--- a/web/react/stores/suggestion_store.jsx
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import Constants from '../utils/constants.jsx';
-import EventEmitter from 'events';
-
-const ActionTypes = Constants.ActionTypes;
-
-const COMPLETE_WORD_EVENT = 'complete_word';
-const PRETEXT_CHANGED_EVENT = 'pretext_changed';
-const SUGGESTIONS_CHANGED_EVENT = 'suggestions_changed';
-
-class SuggestionStore extends EventEmitter {
- constructor() {
- super();
-
- this.addSuggestionsChangedListener = this.addSuggestionsChangedListener.bind(this);
- this.removeSuggestionsChangedListener = this.removeSuggestionsChangedListener.bind(this);
- this.emitSuggestionsChanged = this.emitSuggestionsChanged.bind(this);
-
- this.addPretextChangedListener = this.addPretextChangedListener.bind(this);
- this.removePretextChangedListener = this.removePretextChangedListener.bind(this);
- this.emitPretextChanged = this.emitPretextChanged.bind(this);
-
- this.addCompleteWordListener = this.addCompleteWordListener.bind(this);
- this.removeCompleteWordListener = this.removeCompleteWordListener.bind(this);
- this.emitCompleteWord = this.emitCompleteWord.bind(this);
-
- this.handleEventPayload = this.handleEventPayload.bind(this);
- this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
-
- // this.suggestions stores the state of all SuggestionBoxes by mapping their unique identifier to an
- // object with the following fields:
- // pretext: the text before the cursor
- // matchedPretext: the text before the cursor that will be replaced if an autocomplete term is selected
- // terms: a list of strings which the previously typed text may be replaced by
- // items: a list of objects backing the terms which may be used in rendering
- // components: a list of react components that can be used to render their corresponding item
- // selection: the term currently selected by the keyboard
- this.suggestions = new Map();
- }
-
- addSuggestionsChangedListener(id, callback) {
- this.on(SUGGESTIONS_CHANGED_EVENT + id, callback);
- }
- removeSuggestionsChangedListener(id, callback) {
- this.removeListener(SUGGESTIONS_CHANGED_EVENT + id, callback);
- }
- emitSuggestionsChanged(id) {
- this.emit(SUGGESTIONS_CHANGED_EVENT + id);
- }
-
- addPretextChangedListener(id, callback) {
- this.on(PRETEXT_CHANGED_EVENT + id, callback);
- }
- removePretextChangedListener(id, callback) {
- this.removeListener(PRETEXT_CHANGED_EVENT + id, callback);
- }
- emitPretextChanged(id, pretext) {
- this.emit(PRETEXT_CHANGED_EVENT + id, pretext);
- }
-
- addCompleteWordListener(id, callback) {
- this.on(COMPLETE_WORD_EVENT + id, callback);
- }
- removeCompleteWordListener(id, callback) {
- this.removeListener(COMPLETE_WORD_EVENT + id, callback);
- }
- emitCompleteWord(id, term) {
- this.emit(COMPLETE_WORD_EVENT + id, term);
- }
-
- registerSuggestionBox(id) {
- this.suggestions.set(id, {
- pretext: '',
- matchedPretext: '',
- terms: [],
- items: [],
- components: [],
- selection: ''
- });
- }
-
- unregisterSuggestionBox(id) {
- this.suggestions.delete(id);
- }
-
- clearSuggestions(id) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.matchedPretext = '';
- suggestion.terms = [];
- suggestion.items = [];
- suggestion.components = [];
- }
-
- clearSelection(id) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.selection = '';
- }
-
- hasSuggestions(id) {
- return this.suggestions.get(id).terms.length > 0;
- }
-
- setPretext(id, pretext) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.pretext = pretext;
- }
-
- setMatchedPretext(id, matchedPretext) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.matchedPretext = matchedPretext;
- }
-
- addSuggestion(id, term, item, component) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.terms.push(term);
- suggestion.items.push(item);
- suggestion.components.push(component);
- }
-
- addSuggestions(id, terms, items, component) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.terms.push(...terms);
- suggestion.items.push(...items);
-
- for (let i = 0; i < terms.length; i++) {
- suggestion.components.push(component);
- }
- }
-
- // make sure that if suggestions exist, then one of them is selected. return true if the selection changes.
- ensureSelectionExists(id) {
- const suggestion = this.suggestions.get(id);
-
- if (suggestion.terms.length > 0) {
- // if the current selection is no longer in the map, select the first term in the list
- if (!suggestion.selection || suggestion.terms.indexOf(suggestion.selection) === -1) {
- suggestion.selection = suggestion.terms[0];
-
- return true;
- }
- } else if (suggestion.selection) {
- suggestion.selection = '';
-
- return true;
- }
-
- return false;
- }
-
- getPretext(id) {
- return this.suggestions.get(id).pretext;
- }
-
- getMatchedPretext(id) {
- return this.suggestions.get(id).matchedPretext;
- }
-
- getItems(id) {
- return this.suggestions.get(id).items;
- }
-
- getTerms(id) {
- return this.suggestions.get(id).terms;
- }
-
- getComponents(id) {
- return this.suggestions.get(id).components;
- }
-
- getSelection(id) {
- return this.suggestions.get(id).selection;
- }
-
- selectNext(id) {
- this.setSelectionByDelta(id, 1);
- }
-
- selectPrevious(id) {
- this.setSelectionByDelta(id, -1);
- }
-
- setSelectionByDelta(id, delta) {
- const suggestion = this.suggestions.get(id);
-
- let selectionIndex = suggestion.terms.indexOf(suggestion.selection);
-
- if (selectionIndex === -1) {
- // this should never happen since selection should always be in terms
- throw new Error('selection is not in terms');
- }
-
- selectionIndex += delta;
-
- if (selectionIndex < 0) {
- selectionIndex = 0;
- } else if (selectionIndex > suggestion.terms.length - 1) {
- selectionIndex = suggestion.terms.length - 1;
- }
-
- suggestion.selection = suggestion.terms[selectionIndex];
- }
-
- handleEventPayload(payload) {
- const {type, id, ...other} = payload.action; // eslint-disable-line no-use-before-define
-
- switch (type) {
- case ActionTypes.SUGGESTION_PRETEXT_CHANGED:
- this.clearSuggestions(id);
-
- this.setPretext(id, other.pretext);
- this.emitPretextChanged(id, other.pretext);
-
- this.ensureSelectionExists(id);
- this.emitSuggestionsChanged(id);
- break;
- case ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS:
- if (this.getMatchedPretext(id) === '') {
- this.setMatchedPretext(id, other.matchedPretext);
-
- // ensure the matched pretext hasn't changed so that we don't receive suggestions for outdated pretext
- this.addSuggestions(id, other.terms, other.items, other.component);
-
- this.ensureSelectionExists(id);
- this.emitSuggestionsChanged(id);
- }
- break;
- case ActionTypes.SUGGESTION_CLEAR_SUGGESTIONS:
- this.clearSuggestions(id);
- this.clearSelection(id);
- this.emitSuggestionsChanged(id);
- break;
- case ActionTypes.SUGGESTION_SELECT_NEXT:
- this.selectNext(id);
- this.emitSuggestionsChanged(id);
- break;
- case ActionTypes.SUGGESTION_SELECT_PREVIOUS:
- this.selectPrevious(id);
- this.emitSuggestionsChanged(id);
- break;
- case ActionTypes.SUGGESTION_COMPLETE_WORD:
- this.emitCompleteWord(id, other.term || this.getSelection(id), this.getMatchedPretext(id));
-
- this.setPretext(id, '');
- this.clearSuggestions(id);
- this.clearSelection(id);
- this.emitSuggestionsChanged(id);
- break;
- }
- }
-}
-
-export default new SuggestionStore();
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
deleted file mode 100644
index 354a07b72..000000000
--- a/web/react/stores/team_store.jsx
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const CHANGE_EVENT = 'change';
-
-var Utils;
-function getWindowLocationOrigin() {
- if (!Utils) {
- Utils = require('../utils/utils.jsx'); //eslint-disable-line global-require
- }
- return Utils.getWindowLocationOrigin();
-}
-
-class TeamStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.emitChange = this.emitChange.bind(this);
- this.addChangeListener = this.addChangeListener.bind(this);
- this.removeChangeListener = this.removeChangeListener.bind(this);
- this.get = this.get.bind(this);
- this.getByName = this.getByName.bind(this);
- this.getAll = this.getAll.bind(this);
- this.getCurrentId = this.getCurrentId.bind(this);
- this.getCurrent = this.getCurrent.bind(this);
- this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this);
- this.getCurrentInviteLink = this.getCurrentInviteLink.bind(this);
- this.saveTeam = this.saveTeam.bind(this);
-
- this.teams = {};
- this.currentTeamId = '';
- }
-
- emitChange() {
- this.emit(CHANGE_EVENT);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- get(id) {
- var c = this.getAll();
- return c[id];
- }
-
- getByName(name) {
- var t = this.getAll();
-
- for (var id in t) {
- if (t[id].name === name) {
- return t[id];
- }
- }
-
- return null;
- }
-
- getAll() {
- return this.teams;
- }
-
- getCurrentId() {
- var team = this.get(this.currentTeamId);
-
- if (team) {
- return team.id;
- }
-
- return null;
- }
-
- getCurrent() {
- const team = this.teams[this.currentTeamId];
-
- if (team) {
- return team;
- }
-
- return null;
- }
-
- getCurrentTeamUrl() {
- if (this.getCurrent()) {
- return getWindowLocationOrigin() + '/' + this.getCurrent().name;
- }
- return null;
- }
-
- getCurrentInviteLink() {
- const current = this.getCurrent();
-
- if (current) {
- return getWindowLocationOrigin() + '/signup_user_complete/?id=' + current.invite_id;
- }
-
- return '';
- }
-
- saveTeam(team) {
- this.teams[team.id] = team;
- }
-
- saveTeams(teams) {
- this.teams = teams;
- }
-
- saveMyTeam(team) {
- this.saveTeam(team);
- this.currentTeamId = team.id;
- }
-}
-
-var TeamStore = new TeamStoreClass();
-
-TeamStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_MY_TEAM:
- TeamStore.saveMyTeam(action.team);
- TeamStore.emitChange();
- break;
- case ActionTypes.RECEIVED_ALL_TEAMS:
- TeamStore.saveTeams(action.teams);
- TeamStore.emitChange();
- break;
- default:
- }
-});
-
-export default TeamStore;
diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx
deleted file mode 100644
index c1e5c75dc..000000000
--- a/web/react/stores/user_store.jsx
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from '../utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-import BrowserStore from './browser_store.jsx';
-
-const CHANGE_EVENT = 'change';
-const CHANGE_EVENT_SESSIONS = 'change_sessions';
-const CHANGE_EVENT_AUDITS = 'change_audits';
-const CHANGE_EVENT_STATUSES = 'change_statuses';
-
-class UserStoreClass extends EventEmitter {
- constructor() {
- super();
- this.profileCache = null;
- this.currentUserId = '';
- }
-
- emitChange(userId) {
- this.emit(CHANGE_EVENT, userId);
- }
-
- addChangeListener(callback) {
- this.on(CHANGE_EVENT, callback);
- }
-
- removeChangeListener(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- }
-
- emitSessionsChange() {
- this.emit(CHANGE_EVENT_SESSIONS);
- }
-
- addSessionsChangeListener(callback) {
- this.on(CHANGE_EVENT_SESSIONS, callback);
- }
-
- removeSessionsChangeListener(callback) {
- this.removeListener(CHANGE_EVENT_SESSIONS, callback);
- }
-
- emitAuditsChange() {
- this.emit(CHANGE_EVENT_AUDITS);
- }
-
- addAuditsChangeListener(callback) {
- this.on(CHANGE_EVENT_AUDITS, callback);
- }
-
- removeAuditsChangeListener(callback) {
- this.removeListener(CHANGE_EVENT_AUDITS, callback);
- }
-
- emitStatusesChange() {
- this.emit(CHANGE_EVENT_STATUSES);
- }
-
- addStatusesChangeListener(callback) {
- this.on(CHANGE_EVENT_STATUSES, callback);
- }
-
- removeStatusesChangeListener(callback) {
- this.removeListener(CHANGE_EVENT_STATUSES, callback);
- }
-
- getCurrentUser() {
- return this.getProfiles()[this.currentUserId];
- }
-
- setCurrentUser(user) {
- this.saveProfile(user);
- this.currentUserId = user.id;
- global.window.mm_current_user_id = this.currentUserId;
- }
-
- getCurrentId() {
- var user = this.getCurrentUser();
-
- if (user) {
- return user.id;
- }
-
- return null;
- }
-
- getLastEmail() {
- return BrowserStore.getGlobalItem('last_email', '');
- }
-
- setLastEmail(email) {
- BrowserStore.setGlobalItem('last_email', email);
- }
-
- getLastUsername() {
- return BrowserStore.getGlobalItem('last_username', '');
- }
-
- setLastUsername(username) {
- BrowserStore.setGlobalItem('last_username', username);
- }
-
- hasProfile(userId) {
- return this.getProfiles()[userId] != null;
- }
-
- getProfile(userId) {
- if (userId === this.getCurrentId()) {
- return this.getCurrentUser();
- }
-
- return this.getProfiles()[userId];
- }
-
- getProfileByUsername(username) {
- return this.getProfilesUsernameMap()[username];
- }
-
- getProfilesUsernameMap() {
- var profileUsernameMap = {};
-
- var profiles = this.getProfiles();
- for (var key in profiles) {
- if (profiles.hasOwnProperty(key)) {
- var profile = profiles[key];
- profileUsernameMap[profile.username] = profile;
- }
- }
-
- return profileUsernameMap;
- }
-
- getProfiles() {
- if (this.profileCache !== null) {
- return this.profileCache;
- }
-
- return BrowserStore.getItem('profiles', {});
- }
-
- getActiveOnlyProfiles(skipCurrent) {
- const active = {};
- const profiles = this.getProfiles();
- const currentId = this.getCurrentId();
-
- for (var key in profiles) {
- if (!(profiles[key].id === currentId && skipCurrent) && profiles[key].delete_at === 0) {
- active[key] = profiles[key];
- }
- }
-
- return active;
- }
-
- getActiveOnlyProfileList() {
- const profileMap = this.getActiveOnlyProfiles();
- const profiles = [];
- const currentId = this.getCurrentId();
-
- for (const id in profileMap) {
- if (profileMap.hasOwnProperty(id) && id !== currentId) {
- profiles.push(profileMap[id]);
- }
- }
-
- return profiles;
- }
-
- saveProfile(profile) {
- var ps = this.getProfiles();
- ps[profile.id] = profile;
- this.profileCache = ps;
- BrowserStore.setItem('profiles', ps);
- }
-
- saveProfiles(profiles) {
- const currentId = this.getCurrentId();
- if (this.profileCache) {
- const currentUser = this.profileCache[currentId];
- if (currentUser) {
- if (currentId in profiles) {
- delete profiles[currentId];
- }
-
- this.profileCache = profiles;
- this.profileCache[currentId] = currentUser;
- } else {
- this.profileCache = profiles;
- }
- } else {
- this.profileCache = profiles;
- }
-
- BrowserStore.setItem('profiles', profiles);
- }
-
- setSessions(sessions) {
- BrowserStore.setItem('sessions', sessions);
- }
-
- getSessions() {
- return BrowserStore.getItem('sessions', {loading: true});
- }
-
- setAudits(audits) {
- BrowserStore.setItem('audits', audits);
- }
-
- getAudits() {
- return BrowserStore.getItem('audits', {loading: true});
- }
-
- getCurrentMentionKeys() {
- return this.getMentionKeys(this.getCurrentId());
- }
-
- getMentionKeys(id) {
- var user = this.getProfile(id);
-
- var keys = [];
-
- if (!user || !user.notify_props) {
- return keys;
- }
-
- if (user.notify_props.mention_keys) {
- keys = keys.concat(user.notify_props.mention_keys.split(','));
- }
-
- if (user.notify_props.first_name === 'true' && user.first_name) {
- keys.push(user.first_name);
- }
-
- if (user.notify_props.all === 'true') {
- keys.push('@all');
- }
-
- if (user.notify_props.channel === 'true') {
- keys.push('@channel');
- }
-
- return keys;
- }
-
- setStatuses(statuses) {
- this.pSetStatuses(statuses);
- this.emitStatusesChange();
- }
-
- pSetStatuses(statuses) {
- BrowserStore.setItem('statuses', statuses);
- }
-
- setStatus(userId, status) {
- var statuses = this.getStatuses();
- statuses[userId] = status;
- this.pSetStatuses(statuses);
- this.emitStatusesChange();
- }
-
- getStatuses() {
- return BrowserStore.getItem('statuses', {});
- }
-
- getStatus(id) {
- return this.getStatuses()[id];
- }
-}
-
-var UserStore = new UserStoreClass();
-UserStore.setMaxListeners(15);
-
-UserStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_PROFILES:
- UserStore.saveProfiles(action.profiles);
- UserStore.emitChange();
- break;
- case ActionTypes.RECEIVED_ME:
- UserStore.setCurrentUser(action.me);
- UserStore.emitChange(action.me.id);
- break;
- case ActionTypes.RECEIVED_SESSIONS:
- UserStore.setSessions(action.sessions);
- UserStore.emitSessionsChange();
- break;
- case ActionTypes.RECEIVED_AUDITS:
- UserStore.setAudits(action.audits);
- UserStore.emitAuditsChange();
- break;
- case ActionTypes.RECEIVED_STATUSES:
- UserStore.pSetStatuses(action.statuses);
- UserStore.emitStatusesChange();
- break;
- default:
- }
-});
-
-export {UserStore as default};
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
deleted file mode 100644
index b9770a6e9..000000000
--- a/web/react/utils/async_client.jsx
+++ /dev/null
@@ -1,1085 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as client from './client.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import BrowserStore from '../stores/browser_store.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import PostStore from '../stores/post_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as utils from './utils.jsx';
-
-import Constants from './constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-const StatTypes = Constants.StatTypes;
-
-// Used to track in progress async calls
-const callTracker = {};
-
-export function dispatchError(err, method) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ERROR,
- err,
- method
- });
-}
-
-function isCallInProgress(callName) {
- if (!(callName in callTracker)) {
- return false;
- }
-
- if (callTracker[callName] === 0) {
- return false;
- }
-
- if (utils.getTimestamp() - callTracker[callName] > 5000) {
- //console.log('AsyncClient call ' + callName + ' expired after more than 5 seconds');
- return false;
- }
-
- return true;
-}
-
-export function getChannels(checkVersion) {
- if (isCallInProgress('getChannels')) {
- return null;
- }
-
- callTracker.getChannels = utils.getTimestamp();
-
- return client.getChannels(
- (data, textStatus, xhr) => {
- callTracker.getChannels = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- if (checkVersion) {
- var serverVersion = xhr.getResponseHeader('X-Version-ID');
-
- if (serverVersion !== BrowserStore.getLastServerVersion()) {
- if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') {
- BrowserStore.setLastServerVersion(serverVersion);
- } else {
- BrowserStore.setLastServerVersion(serverVersion);
- window.location.reload(true);
- console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
- }
- }
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_CHANNELS,
- channels: data.channels,
- members: data.members
- });
- },
- (err) => {
- callTracker.getChannels = 0;
- dispatchError(err, 'getChannels');
- }
- );
-}
-
-export function getChannel(id) {
- if (isCallInProgress('getChannel' + id)) {
- return;
- }
-
- callTracker['getChannel' + id] = utils.getTimestamp();
-
- client.getChannel(id,
- (data, textStatus, xhr) => {
- callTracker['getChannel' + id] = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_CHANNEL,
- channel: data.channel,
- member: data.member
- });
- },
- (err) => {
- callTracker['getChannel' + id] = 0;
- dispatchError(err, 'getChannel');
- }
- );
-}
-
-export function updateLastViewedAt(id) {
- let channelId;
- if (id) {
- channelId = id;
- } else {
- channelId = ChannelStore.getCurrentId();
- }
-
- if (channelId == null) {
- return;
- }
-
- if (isCallInProgress(`updateLastViewed${channelId}`)) {
- return;
- }
-
- callTracker[`updateLastViewed${channelId}`] = utils.getTimestamp();
- client.updateLastViewedAt(
- channelId,
- () => {
- callTracker.updateLastViewed = 0;
- },
- (err) => {
- callTracker.updateLastViewed = 0;
- dispatchError(err, 'updateLastViewedAt');
- }
- );
-}
-
-export function getMoreChannels(force) {
- if (isCallInProgress('getMoreChannels')) {
- return;
- }
-
- if (ChannelStore.getMoreAll().loading || force) {
- callTracker.getMoreChannels = utils.getTimestamp();
- client.getMoreChannels(
- function getMoreChannelsSuccess(data, textStatus, xhr) {
- callTracker.getMoreChannels = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_MORE_CHANNELS,
- channels: data.channels,
- members: data.members
- });
- },
- function getMoreChannelsFailure(err) {
- callTracker.getMoreChannels = 0;
- dispatchError(err, 'getMoreChannels');
- }
- );
- }
-}
-
-export function getChannelExtraInfo(id, memberLimit) {
- let channelId;
- if (id) {
- channelId = id;
- } else {
- channelId = ChannelStore.getCurrentId();
- }
-
- if (channelId != null) {
- if (isCallInProgress('getChannelExtraInfo_' + channelId)) {
- return;
- }
-
- callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp();
-
- client.getChannelExtraInfo(
- channelId,
- memberLimit,
- (data, textStatus, xhr) => {
- callTracker['getChannelExtraInfo_' + channelId] = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO,
- extra_info: data
- });
- },
- (err) => {
- callTracker['getChannelExtraInfo_' + channelId] = 0;
- dispatchError(err, 'getChannelExtraInfo');
- }
- );
- }
-}
-
-export function getProfiles() {
- if (isCallInProgress('getProfiles')) {
- return;
- }
-
- callTracker.getProfiles = utils.getTimestamp();
- client.getProfiles(
- function getProfilesSuccess(data, textStatus, xhr) {
- callTracker.getProfiles = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_PROFILES,
- profiles: data
- });
- },
- function getProfilesFailure(err) {
- callTracker.getProfiles = 0;
- dispatchError(err, 'getProfiles');
- }
- );
-}
-
-export function getSessions() {
- if (isCallInProgress('getSessions')) {
- return;
- }
-
- callTracker.getSessions = utils.getTimestamp();
- client.getSessions(
- UserStore.getCurrentId(),
- function getSessionsSuccess(data, textStatus, xhr) {
- callTracker.getSessions = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SESSIONS,
- sessions: data
- });
- },
- function getSessionsFailure(err) {
- callTracker.getSessions = 0;
- dispatchError(err, 'getSessions');
- }
- );
-}
-
-export function getAudits() {
- if (isCallInProgress('getAudits')) {
- return;
- }
-
- callTracker.getAudits = utils.getTimestamp();
- client.getAudits(
- UserStore.getCurrentId(),
- function getAuditsSuccess(data, textStatus, xhr) {
- callTracker.getAudits = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_AUDITS,
- audits: data
- });
- },
- function getAuditsFailure(err) {
- callTracker.getAudits = 0;
- dispatchError(err, 'getAudits');
- }
- );
-}
-
-export function getLogs() {
- if (isCallInProgress('getLogs')) {
- return;
- }
-
- callTracker.getLogs = utils.getTimestamp();
- client.getLogs(
- (data, textStatus, xhr) => {
- callTracker.getLogs = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_LOGS,
- logs: data
- });
- },
- (err) => {
- callTracker.getLogs = 0;
- dispatchError(err, 'getLogs');
- }
- );
-}
-
-export function getServerAudits() {
- if (isCallInProgress('getServerAudits')) {
- return;
- }
-
- callTracker.getServerAudits = utils.getTimestamp();
- client.getServerAudits(
- (data, textStatus, xhr) => {
- callTracker.getServerAudits = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SERVER_AUDITS,
- audits: data
- });
- },
- (err) => {
- callTracker.getServerAudits = 0;
- dispatchError(err, 'getServerAudits');
- }
- );
-}
-
-export function getConfig() {
- if (isCallInProgress('getConfig')) {
- return;
- }
-
- callTracker.getConfig = utils.getTimestamp();
- client.getConfig(
- (data, textStatus, xhr) => {
- callTracker.getConfig = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_CONFIG,
- config: data
- });
- },
- (err) => {
- callTracker.getConfig = 0;
- dispatchError(err, 'getConfig');
- }
- );
-}
-
-export function getAllTeams() {
- if (isCallInProgress('getAllTeams')) {
- return;
- }
-
- callTracker.getAllTeams = utils.getTimestamp();
- client.getAllTeams(
- (data, textStatus, xhr) => {
- callTracker.getAllTeams = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ALL_TEAMS,
- teams: data
- });
- },
- (err) => {
- callTracker.getAllTeams = 0;
- dispatchError(err, 'getAllTeams');
- }
- );
-}
-
-export function search(terms) {
- if (isCallInProgress('search_' + String(terms))) {
- return;
- }
-
- callTracker['search_' + String(terms)] = utils.getTimestamp();
- client.search(
- terms,
- function searchSuccess(data, textStatus, xhr) {
- callTracker['search_' + String(terms)] = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: data
- });
- },
- function searchFailure(err) {
- callTracker['search_' + String(terms)] = 0;
- dispatchError(err, 'search');
- }
- );
-}
-
-export function getPostsPage(id, maxPosts) {
- let channelId = id;
- if (channelId == null) {
- channelId = ChannelStore.getCurrentId();
- if (channelId == null) {
- return;
- }
- }
-
- if (isCallInProgress('getPostsPage_' + channelId)) {
- return;
- }
-
- var postList = PostStore.getAllPosts(id);
-
- var max = maxPosts;
- if (max == null) {
- max = Constants.POST_CHUNK_SIZE * Constants.MAX_POST_CHUNKS;
- }
-
- // if we already have more than POST_CHUNK_SIZE posts,
- // let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE,
- // with a max at maxPosts
- var numPosts = Math.min(max, Constants.POST_CHUNK_SIZE);
- if (postList && postList.order.length > 0) {
- numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE));
- }
-
- if (channelId != null) {
- callTracker['getPostsPage_' + channelId] = utils.getTimestamp();
-
- client.getPostsPage(
- channelId,
- 0,
- numPosts,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POSTS,
- id: channelId,
- before: true,
- numRequested: numPosts,
- post_list: data
- });
-
- getProfiles();
- },
- (err) => {
- dispatchError(err, 'getPostsPage');
- },
- () => {
- callTracker['getPostsPage_' + channelId] = 0;
- }
- );
- }
-}
-
-export function getPosts(id) {
- let channelId = id;
- if (channelId == null) {
- channelId = ChannelStore.getCurrentId();
- if (channelId == null) {
- return;
- }
- }
-
- if (isCallInProgress('getPosts_' + channelId)) {
- return;
- }
-
- const postList = PostStore.getAllPosts(channelId);
-
- if ($.isEmptyObject(postList) || postList.order.length < Constants.POST_CHUNK_SIZE) {
- getPostsPage(channelId, Constants.POST_CHUNK_SIZE);
- return;
- }
-
- const latestPost = PostStore.getLatestPost(channelId);
- let latestPostTime = 0;
-
- if (latestPost != null && latestPost.update_at != null) {
- latestPostTime = latestPost.create_at;
- }
-
- callTracker['getPosts_' + channelId] = utils.getTimestamp();
-
- client.getPosts(
- channelId,
- latestPostTime,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POSTS,
- id: channelId,
- before: true,
- numRequested: 0,
- post_list: data
- });
-
- getProfiles();
- },
- (err) => {
- dispatchError(err, 'getPosts');
- },
- () => {
- callTracker['getPosts_' + channelId] = 0;
- }
- );
-}
-
-export function getPostsBefore(postId, offset, numPost) {
- const channelId = ChannelStore.getCurrentId();
- if (channelId == null) {
- return;
- }
-
- if (isCallInProgress('getPostsBefore_' + channelId)) {
- return;
- }
-
- client.getPostsBefore(
- channelId,
- postId,
- offset,
- numPost,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POSTS,
- id: channelId,
- before: true,
- numRequested: numPost,
- post_list: data
- });
-
- getProfiles();
- },
- (err) => {
- dispatchError(err, 'getPostsBefore');
- },
- () => {
- callTracker['getPostsBefore_' + channelId] = 0;
- }
- );
-}
-
-export function getPostsAfter(postId, offset, numPost) {
- const channelId = ChannelStore.getCurrentId();
- if (channelId == null) {
- return;
- }
-
- if (isCallInProgress('getPostsAfter_' + channelId)) {
- return;
- }
-
- client.getPostsAfter(
- channelId,
- postId,
- offset,
- numPost,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POSTS,
- id: channelId,
- before: false,
- numRequested: numPost,
- post_list: data
- });
-
- getProfiles();
- },
- (err) => {
- dispatchError(err, 'getPostsAfter');
- },
- () => {
- callTracker['getPostsAfter_' + channelId] = 0;
- }
- );
-}
-
-export function getMe() {
- if (isCallInProgress('getMe')) {
- return null;
- }
-
- callTracker.getMe = utils.getTimestamp();
- return client.getMe(
- (data, textStatus, xhr) => {
- callTracker.getMe = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ME,
- me: data
- });
-
- GlobalActions.newLocalizationSelected(data.locale);
- },
- (err) => {
- callTracker.getMe = 0;
- dispatchError(err, 'getMe');
- }
- );
-}
-
-export function getStatuses() {
- const preferences = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
-
- const teammateIds = [];
- for (const preference of preferences) {
- if (preference.value === 'true') {
- teammateIds.push(preference.name);
- }
- }
-
- if (isCallInProgress('getStatuses') || teammateIds.length === 0) {
- return;
- }
-
- callTracker.getStatuses = utils.getTimestamp();
- client.getStatuses(teammateIds,
- (data, textStatus, xhr) => {
- callTracker.getStatuses = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_STATUSES,
- statuses: data
- });
- },
- (err) => {
- callTracker.getStatuses = 0;
- dispatchError(err, 'getStatuses');
- }
- );
-}
-
-export function getMyTeam() {
- if (isCallInProgress('getMyTeam')) {
- return null;
- }
-
- callTracker.getMyTeam = utils.getTimestamp();
- return client.getMyTeam(
- function getMyTeamSuccess(data, textStatus, xhr) {
- callTracker.getMyTeam = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_MY_TEAM,
- team: data
- });
- },
- function getMyTeamFailure(err) {
- callTracker.getMyTeam = 0;
- dispatchError(err, 'getMyTeam');
- }
- );
-}
-
-export function getAllPreferences() {
- if (isCallInProgress('getAllPreferences')) {
- return;
- }
-
- callTracker.getAllPreferences = utils.getTimestamp();
- client.getAllPreferences(
- (data, textStatus, xhr) => {
- callTracker.getAllPreferences = 0;
-
- if (xhr.status === 304 || !data) {
- return;
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_PREFERENCES,
- preferences: data
- });
- },
- (err) => {
- callTracker.getAllPreferences = 0;
- dispatchError(err, 'getAllPreferences');
- }
- );
-}
-
-export function savePreferences(preferences, success, error) {
- client.savePreferences(
- preferences,
- (data, textStatus, xhr) => {
- if (xhr.status !== 304) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_PREFERENCES,
- preferences
- });
- }
-
- if (success) {
- success(data);
- }
- },
- (err) => {
- dispatchError(err, 'savePreferences');
-
- if (error) {
- error();
- }
- }
- );
-}
-
-export function getSuggestedCommands(command, suggestionId, component) {
- client.listCommands(
- (data) => {
- var matches = [];
- data.forEach((cmd) => {
- if (('/' + cmd.trigger).indexOf(command) === 0) {
- let s = '/' + cmd.trigger;
- let hint = '';
- if (cmd.auto_complete_hint && cmd.auto_complete_hint.length !== 0) {
- hint = cmd.auto_complete_hint;
- }
- matches.push({
- suggestion: s,
- hint,
- description: cmd.auto_complete_desc
- });
- }
- });
-
- matches = matches.sort((a, b) => a.suggestion.localeCompare(b.suggestion));
-
- // pull out the suggested commands from the returned data
- const terms = matches.map((suggestion) => suggestion.suggestion);
-
- if (terms.length > 0) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
- id: suggestionId,
- matchedPretext: command,
- terms,
- items: matches,
- component
- });
- }
- },
- (err) => {
- dispatchError(err, 'getCommandSuggestions');
- }
- );
-}
-
-export function getFileInfo(filename) {
- const callName = 'getFileInfo' + filename;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getFileInfo(
- filename,
- (data) => {
- callTracker[callName] = 0;
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_FILE_INFO,
- filename,
- info: data
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getFileInfo');
- }
- );
-}
-
-export function getStandardAnalytics(teamId) {
- const callName = 'getStandardAnaytics' + teamId;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getAnalytics(
- 'standard',
- teamId,
- (data) => {
- callTracker[callName] = 0;
-
- const stats = {};
-
- for (const index in data) {
- if (data[index].name === 'channel_open_count') {
- stats[StatTypes.TOTAL_PUBLIC_CHANNELS] = data[index].value;
- }
-
- if (data[index].name === 'channel_private_count') {
- stats[StatTypes.TOTAL_PRIVATE_GROUPS] = data[index].value;
- }
-
- if (data[index].name === 'post_count') {
- stats[StatTypes.TOTAL_POSTS] = data[index].value;
- }
-
- if (data[index].name === 'unique_user_count') {
- stats[StatTypes.TOTAL_USERS] = data[index].value;
- }
-
- if (data[index].name === 'team_count' && teamId == null) {
- stats[StatTypes.TOTAL_TEAMS] = data[index].value;
- }
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ANALYTICS,
- teamId,
- stats
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getStandardAnalytics');
- }
- );
-}
-
-export function getAdvancedAnalytics(teamId) {
- const callName = 'getAdvancedAnalytics' + teamId;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getAnalytics(
- 'extra_counts',
- teamId,
- (data) => {
- callTracker[callName] = 0;
-
- const stats = {};
-
- for (const index in data) {
- if (data[index].name === 'file_post_count') {
- stats[StatTypes.TOTAL_FILE_POSTS] = data[index].value;
- }
-
- if (data[index].name === 'hashtag_post_count') {
- stats[StatTypes.TOTAL_HASHTAG_POSTS] = data[index].value;
- }
-
- if (data[index].name === 'incoming_webhook_count') {
- stats[StatTypes.TOTAL_IHOOKS] = data[index].value;
- }
-
- if (data[index].name === 'outgoing_webhook_count') {
- stats[StatTypes.TOTAL_OHOOKS] = data[index].value;
- }
-
- if (data[index].name === 'command_count') {
- stats[StatTypes.TOTAL_COMMANDS] = data[index].value;
- }
-
- if (data[index].name === 'session_count') {
- stats[StatTypes.TOTAL_SESSIONS] = data[index].value;
- }
- }
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ANALYTICS,
- teamId,
- stats
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getAdvancedAnalytics');
- }
- );
-}
-
-export function getPostsPerDayAnalytics(teamId) {
- const callName = 'getPostsPerDayAnalytics' + teamId;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getAnalytics(
- 'post_counts_day',
- teamId,
- (data) => {
- callTracker[callName] = 0;
-
- data.reverse();
-
- const stats = {};
- stats[StatTypes.POST_PER_DAY] = data;
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ANALYTICS,
- teamId,
- stats
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getPostsPerDayAnalytics');
- }
- );
-}
-
-export function getUsersPerDayAnalytics(teamId) {
- const callName = 'getUsersPerDayAnalytics' + teamId;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getAnalytics(
- 'user_counts_with_posts_day',
- teamId,
- (data) => {
- callTracker[callName] = 0;
-
- data.reverse();
-
- const stats = {};
- stats[StatTypes.USERS_WITH_POSTS_PER_DAY] = data;
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ANALYTICS,
- teamId,
- stats
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getUsersPerDayAnalytics');
- }
- );
-}
-
-export function getRecentAndNewUsersAnalytics(teamId) {
- const callName = 'getRecentAndNewUsersAnalytics' + teamId;
-
- if (isCallInProgress(callName)) {
- return;
- }
-
- callTracker[callName] = utils.getTimestamp();
-
- client.getProfilesForTeam(
- teamId,
- (users) => {
- const stats = {};
-
- const usersList = [];
- for (const id in users) {
- if (users.hasOwnProperty(id)) {
- usersList.push(users[id]);
- }
- }
-
- usersList.sort((a, b) => {
- if (a.last_activity_at < b.last_activity_at) {
- return 1;
- }
-
- if (a.last_activity_at > b.last_activity_at) {
- return -1;
- }
-
- return 0;
- });
-
- const recentActive = [];
- for (let i = 0; i < usersList.length; i++) {
- if (usersList[i].last_activity_at == null) {
- continue;
- }
-
- recentActive.push(usersList[i]);
- if (i >= Constants.STAT_MAX_ACTIVE_USERS) {
- break;
- }
- }
-
- stats[StatTypes.RECENTLY_ACTIVE_USERS] = recentActive;
-
- usersList.sort((a, b) => {
- if (a.create_at < b.create_at) {
- return 1;
- }
-
- if (a.create_at > b.create_at) {
- return -1;
- }
-
- return 0;
- });
-
- var newlyCreated = [];
- for (let i = 0; i < usersList.length; i++) {
- newlyCreated.push(usersList[i]);
- if (i >= Constants.STAT_MAX_NEW_USERS) {
- break;
- }
- }
-
- stats[StatTypes.NEWLY_CREATED_USERS] = newlyCreated;
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_ANALYTICS,
- teamId,
- stats
- });
- },
- (err) => {
- callTracker[callName] = 0;
-
- dispatchError(err, 'getRecentAndNewUsersAnalytics');
- }
- );
-}
diff --git a/web/react/utils/channel_intro_messages.jsx b/web/react/utils/channel_intro_messages.jsx
deleted file mode 100644
index 94f3f0ce0..000000000
--- a/web/react/utils/channel_intro_messages.jsx
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as Utils from './utils.jsx';
-import ChannelInviteModal from '../components/channel_invite_modal.jsx';
-import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx';
-import ToggleModalButton from '../components/toggle_modal_button.jsx';
-import UserProfile from '../components/user_profile.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import Constants from '../utils/constants.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage, FormattedDate} from 'mm-intl';
-
-export function createChannelIntroMessage(channel) {
- if (channel.type === 'D') {
- return createDMIntroMessage(channel);
- } else if (ChannelStore.isDefault(channel)) {
- return createDefaultIntroMessage(channel);
- } else if (channel.name === Constants.OFFTOPIC_CHANNEL) {
- return createOffTopicIntroMessage(channel);
- } else if (channel.type === 'O' || channel.type === 'P') {
- return createStandardIntroMessage(channel);
- }
- return null;
-}
-
-export function createDMIntroMessage(channel) {
- var teammate = Utils.getDirectTeammate(channel.id);
-
- if (teammate) {
- var teammateName = teammate.username;
- if (teammate.nickname.length > 0) {
- teammateName = teammate.nickname;
- }
-
- return (
- <div className='channel-intro'>
- <div className='post-profile-img__container channel-intro-img'>
- <img
- className='post-profile-img'
- src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at}
- height='50'
- width='50'
- />
- </div>
- <div className='channel-intro-profile'>
- <strong>
- <UserProfile user={teammate}/>
- </strong>
- </div>
- <p className='channel-intro-text'>
- <FormattedHTMLMessage
- id='intro_messages.DM'
- defaultMessage='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.'
- values={{
- teammate: teammateName
- }}
- />
- </p>
- {createSetHeaderButton(channel)}
- </div>
- );
- }
-
- return (
- <div className='channel-intro'>
- <p className='channel-intro-text'>
- <FormattedMessage
- id='intro_messages.teammate'
- defaultMessage='This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'
- />
- </p>
- </div>
- );
-}
-
-export function createOffTopicIntroMessage(channel) {
- return (
- <div className='channel-intro'>
- <FormattedHTMLMessage
- id='intro_messages.offTopic'
- defaultMessage='<h4 class="channel-intro__title">Beginning of {display_name}</h4><p class="channel-intro__content">This is the start of {display_name}, a channel for non-work-related conversations.<br/></p>'
- values={{
- display_name: channel.display_name
- }}
- />
- {createSetHeaderButton(channel)}
- {createInviteChannelMemberButton(channel, 'channel')}
- </div>
- );
-}
-
-export function createDefaultIntroMessage(channel) {
- const inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- onClick={GlobalActions.showGetTeamInviteLinkModal}
- >
- <i className='fa fa-user-plus'></i>
- <FormattedMessage
- id='intro_messages.inviteOthers'
- defaultMessage='Invite others to this team'
- />
- </a>
- );
-
- return (
- <div className='channel-intro'>
- <FormattedHTMLMessage
- id='intro_messages.default'
- defaultMessage="<h4 class='channel-intro__title'>Beginning of {display_name}</h4><p class='channel-intro__content'><strong>Welcome to {display_name}!</strong><br/><br/>This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.</p>"
- values={{
- display_name: channel.display_name
- }}
- />
- {inviteModalLink}
- {createSetHeaderButton(channel)}
- <br/>
- </div>
- );
-}
-
-export function createStandardIntroMessage(channel) {
- var uiName = channel.display_name;
- var creatorName = '';
-
- var uiType;
- var memberMessage;
- if (channel.type === 'P') {
- uiType = (
- <FormattedMessage
- id='intro_messages.group'
- defaultMessage='private group'
- />
- );
- memberMessage = (
- <FormattedMessage
- id='intro_messages.onlyInvited'
- defaultMessage=' Only invited members can see this private group.'
- />
- );
- } else {
- uiType = (
- <FormattedMessage
- id='intro_messages.channel'
- defaultMessage='channel'
- />
- );
- memberMessage = (
- <FormattedMessage
- id='intro_messages.anyMember'
- defaultMessage=' Any member can join and read this channel.'
- />
- );
- }
-
- const date = (
- <FormattedDate
- value={channel.create_at}
- month='long'
- day='2-digit'
- year='numeric'
- />
- );
-
- var createMessage;
- if (creatorName === '') {
- createMessage = (
- <FormattedMessage
- id='intro_messages.noCreator'
- defaultMessage='This is the start of the {name} {type}, created on {date}.'
- values={{
- name: (uiName),
- type: (uiType),
- date: (date)
- }}
- />
- );
- } else {
- createMessage = (
- <span>
- <FormattedHTMLMessage
- id='intro_messages.creator'
- defaultMessage='This is the start of the <strong>{name}</strong> {type}, created by <strong>{creator}</strong> on <strong>{date}</strong>'
- values={{
- name: (uiName),
- type: (uiType),
- date: (date),
- creator: creatorName
- }}
- />
- </span>
- );
- }
-
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>
- <FormattedMessage
- id='intro_messages.beginning'
- defaultMessage='Beginning of {name}'
- values={{
- name: (uiName)
- }}
- />
- </h4>
- <p className='channel-intro__content'>
- {createMessage}
- {memberMessage}
- <br/>
- </p>
- {createSetHeaderButton(channel)}
- {createInviteChannelMemberButton(channel, uiType)}
- </div>
- );
-}
-
-function createInviteChannelMemberButton(channel, uiType) {
- return (
- <ToggleModalButton
- className='intro-links'
- dialogType={ChannelInviteModal}
- dialogProps={{channel}}
- >
- <i className='fa fa-user-plus'></i>
- <FormattedMessage
- id='intro_messages.invite'
- defaultMessage='Invite others to this {type}'
- values={{
- type: (uiType)
- }}
- />
- </ToggleModalButton>
- );
-}
-
-function createSetHeaderButton(channel) {
- return (
- <ToggleModalButton
- className='intro-links'
- dialogType={EditChannelHeaderModal}
- dialogProps={{channel}}
- >
- <i className='fa fa-pencil'></i>
- <FormattedMessage
- id='intro_messages.setHeader'
- defaultMessage='Set a Header'
- />
- </ToggleModalButton>
- );
-}
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
deleted file mode 100644
index e00f28a14..000000000
--- a/web/react/utils/client.jsx
+++ /dev/null
@@ -1,1650 +0,0 @@
-// See License.txt for license information.
-
-import BrowserStore from '../stores/browser_store.jsx';
-
-import {browserHistory} from 'react-router';
-
-let translations = {
- connectionError: 'There appears to be a problem with your internet connection.',
- unknownError: 'We received an unexpected status code from the server.'
-};
-
-export function setTranslations(messages) {
- translations = messages;
-}
-
-export function track(category, action, label, property, value) {
- global.window.analytics.track(action, {category, label, property, value});
-}
-
-export function trackPage() {
- global.window.analytics.page();
-}
-
-function handleError(methodName, xhr, status, err) {
- var e = null;
- try {
- e = JSON.parse(xhr.responseText);
- } catch (parseError) {
- e = null;
- }
-
- var msg = '';
-
- if (e) {
- msg = 'method=' + methodName + ' msg=' + e.message + ' detail=' + e.detailed_error + ' rid=' + e.request_id;
- } else {
- msg = 'method=' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err;
-
- if (xhr.status === 0) {
- e = {message: translations.connectionError};
- } else {
- e = {message: translations.unknownError + ' (' + xhr.status + ')'};
- }
- }
-
- console.error(msg); //eslint-disable-line no-console
- console.error(e); //eslint-disable-line no-console
-
- track('api', 'api_weberror', methodName, 'message', msg);
-
- if (xhr.status === 401) {
- if (window.location.href.indexOf('/channels') === 0) {
- browserHistory.push('/login?extra=expired&redirect=' + encodeURIComponent(window.location.pathname + window.location.search));
- } else {
- var teamURL = window.location.pathname.split('/channels')[0];
- browserHistory.push(teamURL + '/login?extra=expired&redirect=' + encodeURIComponent(window.location.pathname + window.location.search));
- }
- }
-
- return e;
-}
-
-export function getTranslations(locale, success, error) {
- $.ajax({
- url: '/static/i18n/' + locale + '.json',
- dataType: 'json',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getTranslations', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamFromSignup(teamSignup, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_from_signup',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(teamSignup),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamFromSignup', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamWithLdap(teamSignup, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_with_ldap',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(teamSignup),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamFromSignup', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamWithSSO(team, service, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_with_sso/' + service,
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamWithSSO', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createUser(user, data, emailHash, success, error) {
- $.ajax({
- url: '/api/v1/users/create?d=' + encodeURIComponent(data) + '&h=' + encodeURIComponent(emailHash),
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(user),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createUser', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_create', user.team_id, 'email', user.email);
-}
-
-export function updateUser(user, success, error) {
- $.ajax({
- url: '/api/v1/users/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(user),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateUser', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update');
-}
-
-export function updatePassword(data, success, error) {
- $.ajax({
- url: '/api/v1/users/newpassword',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('newPassword', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_newpassword');
-}
-
-export function updateUserNotifyProps(data, success, error) {
- $.ajax({
- url: '/api/v1/users/update_notify',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateUserNotifyProps', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function updateRoles(data, success, error) {
- $.ajax({
- url: '/api/v1/users/update_roles',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateRoles', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update_roles');
-}
-
-export function updateActive(userId, active, success, error) {
- var data = {};
- data.user_id = userId;
- data.active = '' + active;
-
- $.ajax({
- url: '/api/v1/users/update_active',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateActive', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update_roles');
-}
-
-export function sendPasswordReset(data, success, error) {
- $.ajax({
- url: '/api/v1/users/send_password_reset',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('sendPasswordReset', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_send_password_reset');
-}
-
-export function resetPassword(data, success, error) {
- $.ajax({
- url: '/api/v1/users/reset_password',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('resetPassword', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_reset_password');
-}
-
-export function switchToSSO(data, success, error) {
- $.ajax({
- url: '/api/v1/users/switch_to_sso',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('switchToSSO', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_switch_to_sso');
-}
-
-export function switchToEmail(data, success, error) {
- $.ajax({
- url: '/api/v1/users/switch_to_email',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('switchToEmail', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_switch_to_email');
-}
-
-export function logout(success, error) {
- track('api', 'api_users_logout');
- $.ajax({
- url: '/api/v1/users/logout',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('logout', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByEmail(name, email, password, success, error) {
- $.ajax({
- url: '/api/v1/users/login',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name, email, password}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_login_success', data.team_id, 'email', data.email);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_login_fail', name, 'email', email);
-
- var e = handleError('loginByEmail', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByUsername(name, username, password, success, error) {
- $.ajax({
- url: '/api/v1/users/login',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name, username, password}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_login_success', data.team_id, 'username', data.username);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_login_fail', name, 'username', username);
-
- var e = handleError('loginByUsername', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByLdap(teamName, id, password, success, error) {
- $.ajax({
- url: '/api/v1/users/login_ldap',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({teamName, id, password}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_loginLdap_success', data.team_id, 'id', id);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_loginLdap_fail', teamName, 'id', id);
-
- var e = handleError('loginByLdap', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function revokeSession(altId, success, error) {
- $.ajax({
- url: '/api/v1/users/revoke_session',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({id: altId}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('revokeSession', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getSessions(userId, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/' + userId + '/sessions',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getSessions', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAudits(userId, success, error) {
- $.ajax({
- url: '/api/v1/users/' + userId + '/audits',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getAudits', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getLogs(success, error) {
- $.ajax({
- url: '/api/v1/admin/logs',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getLogs', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getServerAudits(success, error) {
- $.ajax({
- url: '/api/v1/admin/audits',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getServerAudits', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getConfig(success, error) {
- return $.ajax({
- url: '/api/v1/admin/config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAnalytics(name, teamId, success, error) {
- let url = '/api/v1/admin/analytics/';
- if (teamId == null) {
- url += name;
- } else {
- url += teamId + '/' + name;
- }
- $.ajax({
- url,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getSystemAnalytics', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getClientConfig(success, error) {
- return $.ajax({
- url: '/api/v1/admin/client_props',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getClientConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getTeamAnalytics(teamId, name, success, error) {
- $.ajax({
- url: '/api/v1/admin/analytics/' + teamId + '/' + name,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getTeamAnalytics', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function saveConfig(config, success, error) {
- $.ajax({
- url: '/api/v1/admin/save_config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(config),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('saveConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function logClientError(msg) {
- var l = {};
- l.level = 'ERROR';
- l.message = msg;
-
- $.ajax({
- url: '/api/v1/admin/log_client',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(l)
- });
-}
-
-export function testEmail(config, success, error) {
- $.ajax({
- url: '/api/v1/admin/test_email',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(config),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('testEmail', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAllTeams(success, error) {
- $.ajax({
- url: '/api/v1/teams/all',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getAllTeams', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMeLoggedIn(success, error) {
- return $.ajax({
- cache: false,
- url: '/api/v1/users/me_logged_in',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getMeLoggedIn', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMe(success, error) {
- var currentUser = null;
- $.ajax({
- cache: false,
- url: '/api/v1/users/me',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success: function gotUser(data, textStatus, xhr) {
- currentUser = data;
- if (success) {
- success(data, textStatus, xhr);
- }
- },
- error: function onError(xhr, status, err) {
- if (error) {
- var e = handleError('getMe', xhr, status, err);
- error(e);
- }
- }
- });
-
- return currentUser;
-}
-
-export function inviteMembers(data, success, error) {
- $.ajax({
- url: '/api/v1/teams/invite_members',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('inviteMembers', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_invite_members');
-}
-
-export function updateTeam(team, success, error) {
- $.ajax({
- url: '/api/v1/teams/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: (xhr, status, err) => {
- var e = handleError('updateTeam', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_update_name');
-}
-
-export function signupTeam(email, success, error) {
- $.ajax({
- url: '/api/v1/teams/signup',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({email: email}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('singupTeam', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_signup');
-}
-
-export function createTeam(team, success, error) {
- $.ajax({
- url: '/api/v1/teams/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function findTeamByName(teamName, success, error) {
- $.ajax({
- url: '/api/v1/teams/find_team_by_name',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name: teamName}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('findTeamByName', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createChannel(channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(channel),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_create', channel.type, 'name', channel.name);
-}
-
-export function createDirectChannel(channel, userId, success, error) {
- $.ajax({
- url: '/api/v1/channels/create_direct',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({user_id: userId}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createDirectChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_create_direct', channel.type, 'name', channel.name);
-}
-
-export function updateChannel(channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(channel),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_update');
-}
-
-export function updateChannelHeader(channelId, header, success, error) {
- const data = {
- channel_id: channelId,
- channel_header: header
- };
-
- $.ajax({
- url: '/api/v1/channels/update_header',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannelHeader', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_header');
-}
-
-export function updateChannelPurpose(data, success, error) {
- $.ajax({
- url: '/api/v1/channels/update_purpose',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannelPurpose', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_purpose');
-}
-
-export function updateNotifyProps(data, success, error) {
- $.ajax({
- url: '/api/v1/channels/update_notify_props',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateNotifyProps', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function joinChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/join',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('joinChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_join');
-}
-
-export function leaveChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/leave',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('leaveChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_leave');
-}
-
-export function deleteChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('deleteChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_delete');
-}
-
-export function updateLastViewedAt(channelId, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/update_last_viewed_at',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateLastViewedAt', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannels(success, error) {
- return $.ajax({
- cache: false,
- url: '/api/v1/channels/',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannels', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannel(id, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + id + '/',
- dataType: 'json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channel_get');
-}
-
-export function getMoreChannels(success, error) {
- $.ajax({
- url: '/api/v1/channels/more',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getMoreChannels', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannelCounts(success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/counts',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannelCounts', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannelExtraInfo(id, memberLimit, success, error) {
- let url = '/api/v1/channels/' + id + '/extra_info';
-
- if (memberLimit) {
- url += '/' + memberLimit;
- }
-
- return $.ajax({
- url,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannelExtraInfo', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function executeCommand(channelId, command, suggest, success, error) {
- $.ajax({
- url: '/api/v1/commands/execute',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({channelId, command, suggest: '' + suggest}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('executeCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function addCommand(cmd, success, error) {
- $.ajax({
- url: '/api/v1/commands/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(cmd),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteCommand(data, success, error) {
- $.ajax({
- url: '/api/v1/commands/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listTeamCommands(success, error) {
- $.ajax({
- url: '/api/v1/commands/list_team_commands',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listTeamCommands', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function regenCommandToken(data, success, error) {
- $.ajax({
- url: '/api/v1/commands/regen_token',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('regenCommandToken', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listCommands(success, error) {
- $.ajax({
- url: '/api/v1/commands/list',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('listCommands', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPostsPage(channelId, offset, limit, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + channelId + '/posts/' + offset + '/' + limit,
- dataType: 'json',
- type: 'GET',
- ifModified: true,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPosts', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPosts(channelId, since, success, error, complete) {
- return $.ajax({
- url: '/api/v1/channels/' + channelId + '/posts/' + since,
- dataType: 'json',
- type: 'GET',
- ifModified: true,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPosts', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPostsBefore(channelId, post, offset, numPost, success, error, complete) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + post + '/before/' + offset + '/' + numPost,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostsBefore', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPostsAfter(channelId, post, offset, numPost, success, error, complete) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + post + '/after/' + offset + '/' + numPost,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostsAfter', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPost(channelId, postId, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + channelId + '/post/' + postId,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPost', xhr, status, err);
- error(e);
- },
- complete
- });
-}
-
-export function getPostById(postId, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/posts/' + postId,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostById', xhr, status, err);
- error(e);
- },
- complete
- });
-}
-
-export function search(terms, success, error) {
- $.ajax({
- url: '/api/v1/posts/search',
- dataType: 'json',
- type: 'GET',
- data: {terms: terms},
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('search', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_search');
-}
-
-export function deletePost(channelId, id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + id + '/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('deletePost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_delete');
-}
-
-export function createPost(post, channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + post.channel_id + '/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(post),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createPost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_create', channel.name, 'length', post.message.length);
-
- // global.window.analytics.track('api_posts_create', {
- // category: 'api',
- // channel_name: channel.name,
- // channel_type: channel.type,
- // length: post.message.length,
- // files: (post.filenames || []).length,
- // mentions: (post.message.match('/<mention>/g') || []).length
- // });
-}
-
-export function updatePost(post, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + post.channel_id + '/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(post),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updatePost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_update');
-}
-
-export function addChannelMember(id, data, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/add',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('addChannelMember', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_add_member');
-}
-
-export function removeChannelMember(id, data, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/remove',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('removeChannelMember', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_remove_member');
-}
-
-export function getProfiles(success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/profiles',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getProfiles', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getProfilesForTeam(teamId, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/profiles/' + teamId,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getProfilesForTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadFile(formData, success, error) {
- var request = $.ajax({
- url: '/api/v1/files/upload',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- if (err !== 'abort') {
- var e = handleError('uploadFile', xhr, status, err);
- error(e);
- }
- }
- });
-
- track('api', 'api_files_upload');
-
- return request;
-}
-
-export function getFileInfo(filename, success, error) {
- $.ajax({
- url: '/api/v1/files/get_info' + filename,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success: (data) => {
- success(data);
- },
- error: function onError(xhr, status, err) {
- var e = handleError('getFileInfo', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPublicLink(data, success, error) {
- $.ajax({
- url: '/api/v1/files/get_public_link',
- dataType: 'json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPublicLink', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadProfileImage(imageData, success, error) {
- $.ajax({
- url: '/api/v1/users/newimage',
- type: 'POST',
- data: imageData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('uploadProfileImage', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function importSlack(fileData, success, error) {
- $.ajax({
- url: '/api/v1/teams/import_team',
- type: 'POST',
- data: fileData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('importTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function exportTeam(success, error) {
- $.ajax({
- url: '/api/v1/teams/export_team',
- type: 'GET',
- dataType: 'json',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('exportTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getStatuses(ids, success, error) {
- $.ajax({
- url: '/api/v1/users/status',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(ids),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getStatuses', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMyTeam(success, error) {
- return $.ajax({
- url: '/api/v1/teams/me',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getMyTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function registerOAuthApp(app, success, error) {
- $.ajax({
- url: '/api/v1/oauth/register',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(app),
- success: success,
- error: (xhr, status, err) => {
- const e = handleError('registerApp', xhr, status, err);
- error(e);
- }
- });
-
- module.exports.track('api', 'api_apps_register');
-}
-
-export function allowOAuth2(responseType, clientId, redirectUri, state, scope, success, error) {
- $.ajax({
- url: '/api/v1/oauth/allow?response_type=' + responseType + '&client_id=' + clientId + '&redirect_uri=' + redirectUri + '&scope=' + scope + '&state=' + state,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- const e = handleError('allowOAuth2', xhr, status, err);
- error(e);
- }
- });
-
- module.exports.track('api', 'api_users_allow_oauth2');
-}
-
-export function addIncomingHook(hook, success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(hook),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addIncomingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteIncomingHook(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteIncomingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listIncomingHooks(success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/list',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listIncomingHooks', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAllPreferences(success, error) {
- return $.ajax({
- url: '/api/v1/preferences/',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getAllPreferences', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPreferenceCategory(category, success, error) {
- $.ajax({
- url: `/api/v1/preferences/${category}`,
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getPreferenceCategory', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function savePreferences(preferences, success, error) {
- $.ajax({
- url: '/api/v1/preferences/save',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(preferences),
- success,
- error: (xhr, status, err) => {
- var e = handleError('savePreferences', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function addOutgoingHook(hook, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(hook),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addOutgoingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteOutgoingHook(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteOutgoingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listOutgoingHooks(success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/list',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listOutgoingHooks', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function regenOutgoingHookToken(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/regen_token',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('regenOutgoingHookToken', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadLicenseFile(formData, success, error) {
- $.ajax({
- url: '/api/v1/license/add',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('uploadLicenseFile', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_license_upload');
-}
-
-export function removeLicenseFile(success, error) {
- $.ajax({
- url: '/api/v1/license/remove',
- type: 'POST',
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('removeLicenseFile', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_license_upload');
-}
-
-export function getClientLicenceConfig(success, error) {
- return $.ajax({
- url: '/api/v1/license/client_config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getClientLicenceConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getInviteInfo(success, error, id) {
- $.ajax({
- url: '/api/v1/teams/get_invite_info',
- type: 'POST',
- dataType: 'json',
- contentType: 'application/json',
- data: JSON.stringify({invite_id: id}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getInviteInfo', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
-
-export function verifyEmail(success, error, uid, hid) {
- $.ajax({
- url: '/api/v1/users/verify_email',
- type: 'POST',
- contentType: 'application/json',
- dataType: 'text',
- data: JSON.stringify({uid, hid}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('verifyEmail', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
-
-export function resendVerification(success, error, teamName, email) {
- $.ajax({
- url: '/api/v1/users/resend_verification',
- type: 'POST',
- contentType: 'application/json',
- dataType: 'text',
- data: JSON.stringify({team_name: teamName, email}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('resendVerification', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
deleted file mode 100644
index 3de562b7b..000000000
--- a/web/react/utils/constants.jsx
+++ /dev/null
@@ -1,520 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import keyMirror from 'keymirror';
-
-export default {
- ActionTypes: keyMirror({
- RECEIVED_ERROR: null,
-
- CLICK_CHANNEL: null,
- CREATE_CHANNEL: null,
- LEAVE_CHANNEL: null,
- CREATE_POST: null,
- POST_DELETED: null,
- REMOVE_POST: null,
-
- RECEIVED_CHANNELS: null,
- RECEIVED_CHANNEL: null,
- RECEIVED_MORE_CHANNELS: null,
- RECEIVED_CHANNEL_EXTRA_INFO: null,
-
- FOCUS_POST: null,
- RECEIVED_POSTS: null,
- RECEIVED_FOCUSED_POST: null,
- RECEIVED_POST: null,
- RECEIVED_EDIT_POST: null,
- RECEIVED_SEARCH: null,
- RECEIVED_SEARCH_TERM: null,
- RECEIVED_POST_SELECTED: null,
- RECEIVED_MENTION_DATA: null,
- RECEIVED_ADD_MENTION: null,
-
- RECEIVED_PROFILES: null,
- RECEIVED_ME: null,
- RECEIVED_SESSIONS: null,
- RECEIVED_AUDITS: null,
- RECEIVED_TEAMS: null,
- RECEIVED_STATUSES: null,
- RECEIVED_PREFERENCE: null,
- RECEIVED_PREFERENCES: null,
- RECEIVED_FILE_INFO: null,
-
- RECEIVED_MSG: null,
-
- RECEIVED_MY_TEAM: null,
-
- RECEIVED_CONFIG: null,
- RECEIVED_LOGS: null,
- RECEIVED_SERVER_AUDITS: null,
- RECEIVED_ALL_TEAMS: null,
-
- RECEIVED_LOCALE: null,
-
- SHOW_SEARCH: null,
-
- TOGGLE_IMPORT_THEME_MODAL: null,
- TOGGLE_INVITE_MEMBER_MODAL: null,
- TOGGLE_DELETE_POST_MODAL: null,
- TOGGLE_GET_POST_LINK_MODAL: null,
- TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null,
- TOGGLE_REGISTER_APP_MODAL: null,
-
- SUGGESTION_PRETEXT_CHANGED: null,
- SUGGESTION_RECEIVED_SUGGESTIONS: null,
- SUGGESTION_CLEAR_SUGGESTIONS: null,
- SUGGESTION_COMPLETE_WORD: null,
- SUGGESTION_SELECT_NEXT: null,
- SUGGESTION_SELECT_PREVIOUS: null
- }),
-
- PayloadSources: keyMirror({
- SERVER_ACTION: null,
- VIEW_ACTION: null
- }),
-
- StatTypes: keyMirror({
- TOTAL_USERS: null,
- TOTAL_PUBLIC_CHANNELS: null,
- TOTAL_PRIVATE_GROUPS: null,
- TOTAL_POSTS: null,
- TOTAL_TEAMS: null,
- TOTAL_FILE_POSTS: null,
- TOTAL_HASHTAG_POSTS: null,
- TOTAL_IHOOKS: null,
- TOTAL_OHOOKS: null,
- TOTAL_COMMANDS: null,
- TOTAL_SESSIONS: null,
- POST_PER_DAY: null,
- USERS_WITH_POSTS_PER_DAY: null,
- RECENTLY_ACTIVE_USERS: null,
- NEWLY_CREATED_USERS: null
- }),
- STAT_MAX_ACTIVE_USERS: 20,
- STAT_MAX_NEW_USERS: 20,
-
- SocketEvents: {
- POSTED: 'posted',
- POST_EDITED: 'post_edited',
- POST_DELETED: 'post_deleted',
- CHANNEL_VIEWED: 'channel_viewed',
- NEW_USER: 'new_user',
- USER_ADDED: 'user_added',
- USER_REMOVED: 'user_removed',
- TYPING: 'typing',
- PREFERENCE_CHANGED: 'preference_changed',
- EPHEMERAL_MESSAGE: 'ephemeral_message'
- },
-
- //SPECIAL_MENTIONS: ['all', 'channel'],
- SPECIAL_MENTIONS: ['channel'],
- CHARACTER_LIMIT: 4000,
- IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg'],
- AUDIO_TYPES: ['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac', 'ogg'],
- VIDEO_TYPES: ['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv'],
- PRESENTATION_TYPES: ['ppt', 'pptx'],
- SPREADSHEET_TYPES: ['xlsx', 'csv'],
- WORD_TYPES: ['doc', 'docx'],
- CODE_TYPES: ['css', 'html', 'js', 'php', 'rb'],
- PDF_TYPES: ['pdf'],
- PATCH_TYPES: ['patch'],
- ICON_FROM_TYPE: {
- audio: 'audio',
- video: 'video',
- spreadsheet: 'excel',
- presentation: 'ppt',
- pdf: 'pdf',
- code: 'code',
- word: 'word',
- patch: 'patch',
- other: 'generic'
- },
- MAX_DISPLAY_FILES: 5,
- MAX_UPLOAD_FILES: 5,
- MAX_FILE_SIZE: 50000000, // 50 MB
- THUMBNAIL_WIDTH: 128,
- THUMBNAIL_HEIGHT: 100,
- WEB_VIDEO_WIDTH: 640,
- WEB_VIDEO_HEIGHT: 480,
- MOBILE_VIDEO_WIDTH: 480,
- MOBILE_VIDEO_HEIGHT: 360,
- DEFAULT_CHANNEL: 'town-square',
- OFFTOPIC_CHANNEL: 'off-topic',
- GITLAB_SERVICE: 'gitlab',
- GOOGLE_SERVICE: 'google',
- EMAIL_SERVICE: 'email',
- SIGNIN_CHANGE: 'signin_change',
- SIGNIN_VERIFIED: 'verified',
- SESSION_EXPIRED: 'expired',
- POST_CHUNK_SIZE: 60,
- MAX_POST_CHUNKS: 3,
- POST_FOCUS_CONTEXT_RADIUS: 10,
- POST_LOADING: 'loading',
- POST_FAILED: 'failed',
- POST_DELETED: 'deleted',
- POST_TYPE_EPHEMERAL: 'system_ephemeral',
- POST_TYPE_JOIN_LEAVE: 'system_join_leave',
- SYSTEM_MESSAGE_PREFIX: 'system_',
- SYSTEM_MESSAGE_PROFILE_NAME: 'System',
- SYSTEM_MESSAGE_PROFILE_IMAGE: '/static/images/logo_compact.png',
- RESERVED_TEAM_NAMES: [
- 'www',
- 'web',
- 'admin',
- 'support',
- 'notify',
- 'test',
- 'demo',
- 'mail',
- 'team',
- 'channel',
- 'internal',
- 'localhost',
- 'dockerhost',
- 'stag',
- 'post',
- 'cluster',
- 'api'
- ],
- RESERVED_USERNAMES: [
- 'valet',
- 'all',
- 'channel'
- ],
- MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
- MAX_DMS: 20,
- MAX_CHANNEL_POPOVER_COUNT: 100,
- DM_CHANNEL: 'D',
- OPEN_CHANNEL: 'O',
- PRIVATE_CHANNEL: 'P',
- INVITE_TEAM: 'I',
- OPEN_TEAM: 'O',
- MAX_POST_LEN: 4000,
- EMOJI_SIZE: 16,
- 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>",
- MENU_ICON: "<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'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>",
- COMMENT_ICON: "<svg version='1.1' id='Layer_2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='15px' height='15px' viewBox='1 1.5 15 15' enable-background='new 1 1.5 15 15' xml:space='preserve'> <g> <g> <path fill='#211B1B' d='M14,1.5H3c-1.104,0-2,0.896-2,2v8c0,1.104,0.896,2,2,2h1.628l1.884,3l1.866-3H14c1.104,0,2-0.896,2-2v-8 C16,2.396,15.104,1.5,14,1.5z M15,11.5c0,0.553-0.447,1-1,1H8l-1.493,2l-1.504-1.991L5,12.5H3c-0.552,0-1-0.447-1-1v-8 c0-0.552,0.448-1,1-1h11c0.553,0,1,0.448,1,1V11.5z'/> </g> </g> </svg>",
- REPLY_ICON: "<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='-158 242 18 18' style='enable-background:new -158 242 18 18;' xml:space='preserve'> <path d='M-142.2,252.6c-2-3-4.8-4.7-8.3-4.8v-3.3c0-0.2-0.1-0.3-0.2-0.3s-0.3,0-0.4,0.1l-6.9,6.2c-0.1,0.1-0.1,0.2-0.1,0.3 c0,0.1,0,0.2,0.1,0.3l6.9,6.4c0.1,0.1,0.3,0.1,0.4,0.1c0.1-0.1,0.2-0.2,0.2-0.4v-3.8c4.2,0,7.4,0.4,9.6,4.4c0.1,0.1,0.2,0.2,0.3,0.2 c0,0,0.1,0,0.1,0c0.2-0.1,0.3-0.3,0.2-0.4C-140.2,257.3-140.6,255-142.2,252.6z M-150.8,252.5c-0.2,0-0.4,0.2-0.4,0.4v3.3l-6-5.5 l6-5.3v2.8c0,0.2,0.2,0.4,0.4,0.4c3.3,0,6,1.5,8,4.5c0.5,0.8,0.9,1.6,1.2,2.3C-144,252.8-147.1,252.5-150.8,252.5z'/> </svg>",
- SCROLL_BOTTOM_ICON: "<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='-239 239 21 23' style='enable-background:new -239 239 21 23;' xml:space='preserve'> <path d='M-239,241.4l2.4-2.4l8.1,8.2l8.1-8.2l2.4,2.4l-10.5,10.6L-239,241.4z M-228.5,257.2l8.1-8.2l2.4,2.4l-10.5,10.6l-10.5-10.6 l2.4-2.4L-228.5,257.2z'/> </svg>",
- UPDATE_TYPING_MS: 5000,
- THEMES: {
- default: {
- type: 'Organization',
- sidebarBg: '#2071a7',
- sidebarText: '#fff',
- sidebarUnreadText: '#fff',
- sidebarTextHoverBg: '#136197',
- sidebarTextActiveBorder: '#7AB0D6',
- sidebarTextActiveColor: '#FFFFFF',
- sidebarHeaderBg: '#2f81b7',
- sidebarHeaderTextColor: '#FFFFFF',
- onlineIndicator: '#7DBE00',
- awayIndicator: '#DCBD4E',
- mentionBj: '#FBFBFB',
- mentionColor: '#2071A7',
- centerChannelBg: '#f2f4f8',
- centerChannelColor: '#333333',
- newMessageSeparator: '#FF8800',
- linkColor: '#2f81b7',
- buttonBg: '#1dacfc',
- buttonColor: '#FFFFFF',
- mentionHighlightBg: '#fff2bb',
- mentionHighlightLink: '#2f81b7',
- codeTheme: 'github'
- },
- mattermost: {
- type: 'Mattermost',
- sidebarBg: '#fafafa',
- sidebarText: '#333333',
- sidebarUnreadText: '#333333',
- sidebarTextHoverBg: '#e6f2fa',
- sidebarTextActiveBorder: '#378FD2',
- sidebarTextActiveColor: '#111111',
- sidebarHeaderBg: '#2389d7',
- sidebarHeaderTextColor: '#ffffff',
- onlineIndicator: '#7DBE00',
- awayIndicator: '#DCBD4E',
- mentionBj: '#2389d7',
- mentionColor: '#ffffff',
- centerChannelBg: '#ffffff',
- centerChannelColor: '#333333',
- newMessageSeparator: '#FF8800',
- linkColor: '#2389d7',
- buttonBg: '#2389d7',
- buttonColor: '#FFFFFF',
- mentionHighlightBg: '#fff2bb',
- mentionHighlightLink: '#2f81b7',
- codeTheme: 'github'
- },
- mattermostDark: {
- type: 'Mattermost Dark',
- sidebarBg: '#1B2C3E',
- sidebarText: '#fff',
- sidebarUnreadText: '#fff',
- sidebarTextHoverBg: '#4A5664',
- sidebarTextActiveBorder: '#39769C',
- sidebarTextActiveColor: '#FFFFFF',
- sidebarHeaderBg: '#1B2C3E',
- sidebarHeaderTextColor: '#FFFFFF',
- onlineIndicator: '#55C5B2',
- awayIndicator: '#A9A14C',
- mentionBj: '#B74A4A',
- mentionColor: '#FFFFFF',
- centerChannelBg: '#2F3E4E',
- centerChannelColor: '#DDDDDD',
- newMessageSeparator: '#5de5da',
- linkColor: '#A4FFEB',
- buttonBg: '#4CBBA4',
- buttonColor: '#FFFFFF',
- mentionHighlightBg: '#984063',
- mentionHighlightLink: '#A4FFEB',
- codeTheme: 'solarized-dark'
- },
- windows10: {
- type: 'Windows Dark',
- sidebarBg: '#171717',
- sidebarText: '#fff',
- sidebarUnreadText: '#fff',
- sidebarTextHoverBg: '#302e30',
- sidebarTextActiveBorder: '#196CAF',
- sidebarTextActiveColor: '#FFFFFF',
- sidebarHeaderBg: '#1f1f1f',
- sidebarHeaderTextColor: '#FFFFFF',
- onlineIndicator: '#0177e7',
- awayIndicator: '#A9A14C',
- mentionBj: '#0177e7',
- mentionColor: '#FFFFFF',
- centerChannelBg: '#1F1F1F',
- centerChannelColor: '#DDDDDD',
- newMessageSeparator: '#CC992D',
- linkColor: '#0D93FF',
- buttonBg: '#0177e7',
- buttonColor: '#FFFFFF',
- mentionHighlightBg: '#784098',
- mentionHighlightLink: '#A4FFEB',
- codeTheme: 'monokai'
- }
- },
- THEME_ELEMENTS: [
- {
- group: 'sidebarElements',
- id: 'sidebarBg',
- uiName: 'Sidebar BG'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarText',
- uiName: 'Sidebar Text'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarHeaderBg',
- uiName: 'Sidebar Header BG'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarHeaderTextColor',
- uiName: 'Sidebar Header Text'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarUnreadText',
- uiName: 'Sidebar Unread Text'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarTextHoverBg',
- uiName: 'Sidebar Text Hover BG'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarTextActiveBorder',
- uiName: 'Sidebar Text Active Border'
- },
- {
- group: 'sidebarElements',
- id: 'sidebarTextActiveColor',
- uiName: 'Sidebar Text Active Color'
- },
- {
- group: 'sidebarElements',
- id: 'onlineIndicator',
- uiName: 'Online Indicator'
- },
- {
- group: 'sidebarElements',
- id: 'awayIndicator',
- uiName: 'Away Indicator'
- },
- {
- group: 'sidebarElements',
- id: 'mentionBj',
- uiName: 'Mention Jewel BG'
- },
- {
- group: 'sidebarElements',
- id: 'mentionColor',
- uiName: 'Mention Jewel Text'
- },
- {
- group: 'centerChannelElements',
- id: 'centerChannelBg',
- uiName: 'Center Channel BG'
- },
- {
- group: 'centerChannelElements',
- id: 'centerChannelColor',
- uiName: 'Center Channel Text'
- },
- {
- group: 'centerChannelElements',
- id: 'newMessageSeparator',
- uiName: 'New Message Separator'
- },
- {
- group: 'centerChannelElements',
- id: 'mentionHighlightBg',
- uiName: 'Mention Highlight BG'
- },
- {
- group: 'centerChannelElements',
- id: 'mentionHighlightLink',
- uiName: 'Mention Highlight Link'
- },
- {
- group: 'centerChannelElements',
- id: 'codeTheme',
- uiName: 'Code Theme',
- themes: [
- {
- id: 'solarized-dark',
- uiName: 'Solarized Dark'
- },
- {
- id: 'solarized-light',
- uiName: 'Solarized Light'
- },
- {
- id: 'github',
- uiName: 'GitHub'
- },
- {
- id: 'monokai',
- uiName: 'Monokai'
- }
- ]
- },
- {
- group: 'linkAndButtonElements',
- id: 'linkColor',
- uiName: 'Link Color'
- },
- {
- group: 'linkAndButtonElements',
- id: 'buttonBg',
- uiName: 'Button BG'
- },
- {
- group: 'linkAndButtonElements',
- id: 'buttonColor',
- uiName: 'Button Text'
- }
- ],
- DEFAULT_CODE_THEME: 'github',
- FONTS: {
- 'Droid Serif': 'font--droid_serif',
- 'Roboto Slab': 'font--roboto_slab',
- Lora: 'font--lora',
- Arvo: 'font--arvo',
- 'Open Sans': 'font--open_sans',
- Roboto: 'font--roboto',
- 'PT Sans': 'font--pt_sans',
- Lato: 'font--lato',
- 'Source Sans Pro': 'font--source_sans_pro',
- 'Exo 2': 'font--exo_2',
- Ubuntu: 'font--ubuntu'
- },
- DEFAULT_FONT: 'Open Sans',
- Preferences: {
- CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
- CATEGORY_DISPLAY_SETTINGS: 'display_settings',
- DISPLAY_PREFER_NICKNAME: 'nickname_full_name',
- DISPLAY_PREFER_FULL_NAME: 'full_name',
- CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
- TUTORIAL_STEP: 'tutorial_step'
- },
- TutorialSteps: {
- INTRO_SCREENS: 0,
- POST_POPOVER: 1,
- CHANNEL_POPOVER: 2,
- MENU_POPOVER: 3
- },
- KeyCodes: {
- UP: 38,
- DOWN: 40,
- LEFT: 37,
- RIGHT: 39,
- BACKSPACE: 8,
- ENTER: 13,
- ESCAPE: 27,
- SPACE: 32,
- TAB: 9
- },
- HighlightedLanguages: {
- diff: 'Diff',
- apache: 'Apache',
- makefile: 'Makefile',
- http: 'HTTP',
- json: 'JSON',
- markdown: 'Markdown',
- javascript: 'JavaScript',
- css: 'CSS',
- nginx: 'nginx',
- objectivec: 'Objective-C',
- python: 'Python',
- xml: 'XML',
- perl: 'Perl',
- bash: 'Bash',
- php: 'PHP',
- coffeescript: 'CoffeeScript',
- cs: 'C#',
- cpp: 'C++',
- sql: 'SQL',
- go: 'Go',
- ruby: 'Ruby',
- java: 'Java',
- ini: 'ini'
- },
- PostsViewJumpTypes: {
- BOTTOM: 1,
- POST: 2,
- SIDEBAR_OPEN: 3
- },
- NotificationPrefs: {
- MENTION: 'mention'
- },
- FeatureTogglePrefix: 'feature_enabled_',
- PRE_RELEASE_FEATURES: {
- MARKDOWN_PREVIEW: {
- label: 'markdown_preview', // github issue: https://github.com/mattermost/platform/pull/1389
- description: 'Show markdown preview option in message input box'
- },
- EMBED_PREVIEW: {
- label: 'embed_preview',
- description: 'Show preview snippet of links below message'
- },
- EMBED_TOGGLE: {
- label: 'embed_toggle',
- description: 'Show toggle for all embed previews'
- }
- },
- OVERLAY_TIME_DELAY: 400,
- MIN_USERNAME_LENGTH: 3,
- MAX_USERNAME_LENGTH: 64,
- MIN_PASSWORD_LENGTH: 5,
- MAX_PASSWORD_LENGTH: 50,
- TIME_SINCE_UPDATE_INTERVAL: 30000,
- MIN_HASHTAG_LINK_LENGTH: 3
-};
diff --git a/web/react/utils/delayed_action.jsx b/web/react/utils/delayed_action.jsx
deleted file mode 100644
index 4f6239ad0..000000000
--- a/web/react/utils/delayed_action.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-export default class DelayedAction {
- constructor(action) {
- this.action = action;
-
- this.timer = -1;
-
- // bind fire since it doesn't get passed the correct this value with setTimeout
- this.fire = this.fire.bind(this);
- }
-
- fire() {
- this.action();
-
- this.timer = -1;
- }
-
- fireAfter(timeout) {
- if (this.timer >= 0) {
- window.clearTimeout(this.timer);
- }
-
- this.timer = window.setTimeout(this.fire, timeout);
- }
-}
diff --git a/web/react/utils/emoticons.jsx b/web/react/utils/emoticons.jsx
deleted file mode 100644
index bed798b3e..000000000
--- a/web/react/utils/emoticons.jsx
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-const emoticonPatterns = {
- slightly_smiling_face: /(^|\s)(:-?\))(?=$|\s)/g, // :)
- wink: /(^|\s)(;-?\))(?=$|\s)/g, // ;)
- open_mouth: /(^|\s)(:o)(?=$|\s)/gi, // :o
- scream: /(^|\s)(:-o)(?=$|\s)/gi, // :-o
- smirk: /(^|\s)(:-?])(?=$|\s)/g, // :]
- smile: /(^|\s)(:-?d)(?=$|\s)/gi, // :D
- stuck_out_tongue_closed_eyes: /(^|\s)(x-d)(?=$|\s)/gi, // x-d
- stuck_out_tongue: /(^|\s)(:-?p)(?=$|\s)/gi, // :p
- rage: /(^|\s)(:-?[\[@])(?=$|\s)/g, // :@
- slightly_frowning_face: /(^|\s)(:-?\()(?=$|\s)/g, // :(
- cry: /(^|\s)(:['ā€™]-?\(|:&#x27;\(|:&#39;\()(?=$|\s)/g, // :`(
- confused: /(^|\s)(:-?\/)(?=$|\s)/g, // :/
- confounded: /(^|\s)(:-?s)(?=$|\s)/gi, // :s
- neutral_face: /(^|\s)(:-?\|)(?=$|\s)/g, // :|
- flushed: /(^|\s)(:-?\$)(?=$|\s)/g, // :$
- mask: /(^|\s)(:-x)(?=$|\s)/gi, // :-x
- heart: /(^|\s)(<3|&lt;3)(?=$|\s)/g, // <3
- broken_heart: /(^|\s)(<\/3|&lt;&#x2F;3)(?=$|\s)/g, // </3
- thumbsup: /(^|\s)(:\+1:)(?=$|\s)/g, // :+1:
- thumbsdown: /(^|\s)(:\-1:)(?=$|\s)/g // :-1:
-};
-
-function initializeEmoticonMap() {
- const emoticonNames =
- ('+1,-1,100,1234,8ball,a,ab,abc,abcd,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,' +
- 'anger,angry,anguished,ant,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,' +
- 'arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,' +
- 'arrow_lower_right,arrow_right,arrow_right_hook,arrow_up,arrow_up_down,arrow_up_small,arrow_upper_left,' +
- 'arrow_upper_right,arrows_clockwise,arrows_counterclockwise,art,articulated_lorry,astonished,atm,b,baby,' +
- 'baby_bottle,baby_chick,baby_symbol,back,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,' +
- 'bank,bar_chart,barber,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,' +
- 'bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_medium_small_square,black_medium_square,' +
- 'black_large_square,black_nib,black_small_square,black_square,black_square_button,blossom,blowfish,blue_book,' +
- 'blue_car,blue_heart,blush,boar,boat,bomb,book,bookmark,bookmark_tabs,books,boom,boot,bouquet,bow,bowling,bowtie,' +
- 'boy,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,' +
- 'busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,' +
- 'capricorn,car,card_index,carousel_horse,cat,cat2,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,' +
- 'checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,' +
- 'cinema,circus_tent,city_sunrise,city_sunset,cl,clap,clapper,clipboard,clock1,clock10,clock1030,clock11,' +
- 'clock1130,clock12,clock1230,clock130,clock2,clock230,clock3,clock330,clock4,clock430,clock5,clock530,clock6,' +
- 'clock630,clock7,clock730,clock8,clock830,clock9,clock930,closed_book,closed_lock_with_key,closed_umbrella,cloud,' +
- 'clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,' +
- 'construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couple,couple_with_heart,' +
- 'couplekiss,cow,cow2,credit_card,crescent_moon,crocodile,crossed_flags,crown,cry,crying_cat_face,crystal_ball,' +
- 'cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,de,' +
- 'deciduous_tree,department_store,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,' +
- 'dizzy,dizzy_face,do_not_litter,dog,dog2,dollar,dolls,dolphin,donut,door,doughnut,dragon,dragon_face,dress,' +
- 'dromedary_camel,droplet,dvd,e-mail,ear,ear_of_rice,earth_africa,earth_americas,earth_asia,egg,eggplant,eight,' +
- 'eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,email,end,envelope,es,euro,' +
- 'european_castle,european_post_office,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,' +
- 'factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire,' +
- 'fire_engine,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish,fish_cake,fishing_pole_and_fish,fist,' +
- 'five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four,' +
- 'four_leaf_clover,fr,free,fried_shrimp,fries,frog,frowning,fu,fuelpump,full_moon,full_moon_with_face,game_die,gb,' +
- 'gem,gemini,ghost,gift,gift_heart,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,' +
- 'green_book,green_heart,grey_exclamation,grey_question,grimacing,grin,grinning,guardsman,guitar,gun,haircut,' +
- 'hamburger,hammer,hamster,hand,handbag,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heart,' +
- 'heart_decoration,heart_eyes,heart_eyes_cat,heartbeat,heartpulse,hearts,heavy_check_mark,heavy_division_sign,' +
- 'heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,' +
- 'herb,hibiscus,high_brightness,high_heel,hocho,honey_pot,honeybee,horse,horse_racing,hospital,hotel,hotsprings,' +
- 'hourglass,hourglass_flowing_sand,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,id,' +
- 'ideograph_advantage,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,' +
- 'interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japan,japanese_castle,japanese_goblin,japanese_ogre,jeans,' +
- 'joy,joy_cat,jp,key,keycap_ten,kimono,kiss,kissing,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,' +
- 'kissing_smiling_eyes,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,' +
- 'last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,' +
- 'lemon,leo,leopard,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,' +
- 'love_hotel,love_letter,low_brightness,m,mag,mag_right,mahjong,mailbox,mailbox_closed,mailbox_with_mail,' +
- 'mailbox_with_no_mail,man,man_with_gua_pi_mao,man_with_turban,mans_shoe,maple_leaf,mask,massage,meat_on_bone,' +
- 'mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,' +
- 'money_with_wings,moneybag,monkey,monkey_face,monorail,mortar_board,mount_fuji,mountain_bicyclist,' +
- 'mountain_cableway,mountain_railway,mouse,mouse2,movie_camera,moyai,muscle,mushroom,musical_keyboard,' +
- 'musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,' +
- 'neutral_face,new,new_moon,new_moon_with_face,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,' +
- 'no_good,no_mobile_phones,no_mouth,no_pedestrians,no_smoking,non-potable_water,nose,notebook,' +
- 'notebook_with_decorative_cover,notes,nut_and_bolt,o,o2,ocean,octocat,octopus,oden,office,ok,ok_hand,' +
- 'ok_woman,older_man,older_woman,on,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,' +
- 'open_file_folder,open_hands,open_mouth,ophiuchus,orange_book,outbox_tray,ox,package,page_facing_up,' +
- 'page_with_curl,pager,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,' +
- 'passport_control,paw_prints,peach,pear,pencil,pencil2,penguin,pensive,performing_arts,persevere,' +
- 'person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig,pig2,pig_nose,pill,pineapple,pisces,' +
- 'pizza,plus1,point_down,point_left,point_right,point_up,point_up_2,police_car,poodle,poop,post_office,' +
- 'postal_horn,postbox,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,' +
- 'pushpin,put_litter_in_its_place,question,rabbit,rabbit2,racehorse,radio,radio_button,rage,rage1,rage2,rage3,' +
- 'rage4,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ram,ramen,rat,recycle,red_car,red_circle,' +
- 'registered,relaxed,relieved,repeat,repeat_one,restroom,revolving_hearts,rewind,ribbon,rice,rice_ball,' +
- 'rice_cracker,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,ru,' +
- 'rugby_football,runner,running,running_shirt_with_sash,sa,sagittarius,sailboat,sake,sandal,santa,satellite,' +
- 'satisfied,saxophone,school,school_satchel,scissors,scorpius,scream,scream_cat,scroll,seat,secret,see_no_evil,' +
- 'seedling,seven,shaved_ice,sheep,shell,ship,shipit,shirt,shit,shoe,shower,signal_strength,six,six_pointed_star,' +
- 'ski,skull,sleeping,sleepy,slightly_smiling_face,slightly_frowning_face,slot_machine,small_blue_diamond,' +
- 'small_orange_diamond,small_red_triangle,small_red_triangle_down,smile,smile_cat,smiley,smiley_cat,smiling_imp,' +
- 'smirk,smirk_cat,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,' +
- 'spaghetti,sparkle,sparkler,sparkles,sparkling_heart,speak_no_evil,speaker,speech_balloon,speedboat,squirrel,star,' +
- 'star2,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue,' +
- 'stuck_out_tongue_closed_eyes,stuck_out_tongue_winking_eye,sun_with_face,sunflower,sunglasses,sunny,sunrise,' +
- 'sunrise_over_mountains,surfer,sushi,suspect,suspension_railway,sweat,sweat_drops,sweat_smile,sweet_potato,swimmer,' +
- 'symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,' +
- 'thought_balloon,three,thumbsdown,thumbsup,ticket,tiger,tiger2,tired_face,tm,toilet,tokyo_tower,tomato,tongue,top,' +
- 'tophat,tractor,traffic_light,train,train2,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,' +
- 'trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,' +
- 'two,two_hearts,two_men_holding_hands,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,' +
- 'u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,v,vertical_traffic_light,vhs,vibration_mode,' +
- 'video_camera,video_game,violin,virgo,volcano,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,' +
- 'water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale,whale2,' +
- 'wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_medium_small_square,' +
- 'white_medium_square,white_small_square,white_square_button,wind_chime,wine_glass,wink,wolf,woman,' +
- 'womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz').split(',');
-
- // use a map to help make lookups faster instead of having to use indexOf on an array
- const out = new Map();
-
- for (let i = 0; i < emoticonNames.length; i++) {
- out.set(emoticonNames[i], true);
- }
-
- return out;
-}
-
-export const emoticonMap = initializeEmoticonMap();
-
-export function handleEmoticons(text, tokens) {
- let output = text;
-
- function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) {
- if (emoticonMap.has(name)) {
- const index = tokens.size;
- const alias = `MM_EMOTICON${index}`;
-
- tokens.set(alias, {
- value: `<img align="absmiddle" alt="${matchText}" class="emoticon" src="${getImagePathForEmoticon(name)}" title="${matchText}" />`,
- originalText: fullMatch
- });
-
- return prefix + alias;
- }
-
- return fullMatch;
- }
-
- output = output.replace(/(^|\s)(:([a-zA-Z0-9_-]+):)(?=$|\s)/g, (fullMatch, prefix, matchText, name) => replaceEmoticonWithToken(fullMatch, prefix, matchText, name));
-
- $.each(emoticonPatterns, (name, pattern) => {
- // this might look a bit funny, but since the name isn't contained in the actual match
- // like with the named emoticons, we need to add it in manually
- output = output.replace(pattern, (fullMatch, prefix, matchText) => replaceEmoticonWithToken(fullMatch, prefix, matchText, name));
- });
-
- return output;
-}
-
-export function getImagePathForEmoticon(name) {
- if (name) {
- return `/static/images/emoji/${name}.png`;
- }
- return '/static/images/emoji';
-}
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
deleted file mode 100644
index 2b1aed9c0..000000000
--- a/web/react/utils/markdown.jsx
+++ /dev/null
@@ -1,575 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import highlightJs from 'highlight.js/lib/highlight.js';
-import highlightJsDiff from 'highlight.js/lib/languages/diff.js';
-import highlightJsApache from 'highlight.js/lib/languages/apache.js';
-import highlightJsMakefile from 'highlight.js/lib/languages/makefile.js';
-import highlightJsHttp from 'highlight.js/lib/languages/http.js';
-import highlightJsJson from 'highlight.js/lib/languages/json.js';
-import highlightJsMarkdown from 'highlight.js/lib/languages/markdown.js';
-import highlightJsJavascript from 'highlight.js/lib/languages/javascript.js';
-import highlightJsCss from 'highlight.js/lib/languages/css.js';
-import highlightJsNginx from 'highlight.js/lib/languages/nginx.js';
-import highlightJsObjectivec from 'highlight.js/lib/languages/objectivec.js';
-import highlightJsPython from 'highlight.js/lib/languages/python.js';
-import highlightJsXml from 'highlight.js/lib/languages/xml.js';
-import highlightJsPerl from 'highlight.js/lib/languages/perl.js';
-import highlightJsBash from 'highlight.js/lib/languages/bash.js';
-import highlightJsPhp from 'highlight.js/lib/languages/php.js';
-import highlightJsCoffeescript from 'highlight.js/lib/languages/coffeescript.js';
-import highlightJsCs from 'highlight.js/lib/languages/cs.js';
-import highlightJsCpp from 'highlight.js/lib/languages/cpp.js';
-import highlightJsSql from 'highlight.js/lib/languages/sql.js';
-import highlightJsGo from 'highlight.js/lib/languages/go.js';
-import highlightJsRuby from 'highlight.js/lib/languages/ruby.js';
-import highlightJsJava from 'highlight.js/lib/languages/java.js';
-import highlightJsIni from 'highlight.js/lib/languages/ini.js';
-
-highlightJs.registerLanguage('diff', highlightJsDiff);
-highlightJs.registerLanguage('apache', highlightJsApache);
-highlightJs.registerLanguage('makefile', highlightJsMakefile);
-highlightJs.registerLanguage('http', highlightJsHttp);
-highlightJs.registerLanguage('json', highlightJsJson);
-highlightJs.registerLanguage('markdown', highlightJsMarkdown);
-highlightJs.registerLanguage('javascript', highlightJsJavascript);
-highlightJs.registerLanguage('css', highlightJsCss);
-highlightJs.registerLanguage('nginx', highlightJsNginx);
-highlightJs.registerLanguage('objectivec', highlightJsObjectivec);
-highlightJs.registerLanguage('python', highlightJsPython);
-highlightJs.registerLanguage('xml', highlightJsXml);
-highlightJs.registerLanguage('perl', highlightJsPerl);
-highlightJs.registerLanguage('bash', highlightJsBash);
-highlightJs.registerLanguage('php', highlightJsPhp);
-highlightJs.registerLanguage('coffeescript', highlightJsCoffeescript);
-highlightJs.registerLanguage('cs', highlightJsCs);
-highlightJs.registerLanguage('cpp', highlightJsCpp);
-highlightJs.registerLanguage('sql', highlightJsSql);
-highlightJs.registerLanguage('go', highlightJsGo);
-highlightJs.registerLanguage('ruby', highlightJsRuby);
-highlightJs.registerLanguage('java', highlightJsJava);
-highlightJs.registerLanguage('ini', highlightJsIni);
-
-import * as TextFormatting from './text_formatting.jsx';
-import * as Utils from './utils.jsx';
-
-import marked from 'marked';
-
-import Constants from '../utils/constants.jsx';
-const HighlightedLanguages = Constants.HighlightedLanguages;
-
-function markdownImageLoaded(image) {
- image.style.height = 'auto';
-}
-window.markdownImageLoaded = markdownImageLoaded;
-
-class MattermostInlineLexer extends marked.InlineLexer {
- constructor(links, options) {
- super(links, options);
-
- this.rules = Object.assign({}, this.rules);
-
- // modified version of the regex that allows for links starting with www and those surrounded by parentheses
- // the original is /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/| {2,}\n|$)/
- this.rules.text = /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/|www\.|\(| {2,}\n|$)/;
-
- // modified version of the regex that allows links starting with www and those surrounded by parentheses
- // the original is /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/
- this.rules.url = /^(\(?(?:https?:\/\/|www\.)[^\s<.][^\s<]*[^<.,:;"'\]\s])/;
-
- // modified version of the regex that allows <links> starting with www.
- // the original is /^<([^ >]+(@|:\/)[^ >]+)>/
- this.rules.autolink = /^<((?:[^ >]+(@|:\/)|www\.)[^ >]+)>/;
- }
-}
-
-class MattermostParser extends marked.Parser {
- parse(src) {
- this.inline = new MattermostInlineLexer(src.links, this.options, this.renderer);
- this.tokens = src.reverse();
-
- var out = '';
- while (this.next()) {
- out += this.tok();
- }
-
- return out;
- }
-}
-
-class MattermostMarkdownRenderer extends marked.Renderer {
- constructor(options, formattingOptions = {}) {
- super(options);
-
- this.heading = this.heading.bind(this);
- this.paragraph = this.paragraph.bind(this);
- this.text = this.text.bind(this);
-
- this.formattingOptions = formattingOptions;
- }
-
- code(code, language, escaped) {
- let usedLanguage = language || '';
- usedLanguage = usedLanguage.toLowerCase();
-
- // treat html as xml to prevent injection attacks
- if (usedLanguage === 'html') {
- usedLanguage = 'xml';
- }
-
- if (HighlightedLanguages[usedLanguage]) {
- const parsed = highlightJs.highlight(usedLanguage, code);
-
- return (
- '<div class="post-body--code">' +
- '<span class="post-body--code__language">' +
- HighlightedLanguages[usedLanguage] +
- '</span>' +
- '<pre>' +
- '<code class="hljs">' +
- parsed.value +
- '</code>' +
- '</pre>' +
- '</div>'
- );
- } else if (usedLanguage === 'tex' || usedLanguage === 'latex') {
- try {
- const html = katex.renderToString(code, {throwOnError: false, displayMode: true});
-
- return '<div class="post-body--code tex">' + html + '</div>';
- } catch (e) {
- // fall through if latex parsing fails and handle below
- }
- }
-
- return (
- '<pre>' +
- '<code class="hljs">' +
- (escaped ? code : TextFormatting.sanitizeHtml(code)) + '\n' +
- '</code>' +
- '</pre>'
- );
- }
-
- codespan(text) {
- return '<span class="codespan__pre-wrap">' + super.codespan(text) + '</span>';
- }
-
- br() {
- if (this.formattingOptions.singleline) {
- return ' ';
- }
-
- return super.br();
- }
-
- image(href, title, text) {
- let out = '<img src="' + href + '" alt="' + text + '"';
- if (title) {
- out += ' title="' + title + '"';
- }
- out += ' onload="window.markdownImageLoaded(this)" onerror="window.markdownImageLoaded(this)" class="markdown-inline-img"';
- out += this.options.xhtml ? '/>' : '>';
- return out;
- }
-
- heading(text, level, raw) {
- const id = `${this.options.headerPrefix}${raw.toLowerCase().replace(/[^\w]+/g, '-')}`;
- return `<h${level} id="${id}" class="markdown__heading">${text}</h${level}>`;
- }
-
- link(href, title, text) {
- let outHref = href;
- let outText = text;
- let prefix = '';
- let suffix = '';
-
- // some links like https://en.wikipedia.org/wiki/Rendering_(computer_graphics) contain brackets
- // and we try our best to differentiate those from ones just wrapped in brackets when autolinking
- if (outHref.startsWith('(') && outHref.endsWith(')') && text === outHref) {
- prefix = '(';
- suffix = ')';
- outText = text.substring(1, text.length - 1);
- outHref = outHref.substring(1, outHref.length - 1);
- }
-
- try {
- const unescaped = decodeURIComponent(unescape(href)).replace(/[^\w:]/g, '').toLowerCase();
-
- if (unescaped.indexOf('javascript:') === 0 || unescaped.indexOf('vbscript:') === 0) { // eslint-disable-line no-script-url
- return '';
- }
- } catch (e) {
- return '';
- }
-
- if (!(/[a-z+.-]+:/i).test(outHref)) {
- outHref = `http://${outHref}`;
- }
-
- let output = '<a class="theme markdown__link" href="' + outHref + '"';
- if (title) {
- output += ' title="' + title + '"';
- }
-
- if (outHref.lastIndexOf(Utils.getTeamURLFromAddressBar(), 0) === 0) {
- output += '>';
- } else {
- output += ' target="_blank">';
- }
-
- output += outText + '</a>';
-
- return prefix + output + suffix;
- }
-
- paragraph(text) {
- if (this.formattingOptions.singleline) {
- return `<p class="markdown__paragraph-inline">${text}</p>`;
- }
-
- return super.paragraph(text);
- }
-
- table(header, body) {
- return `<div class="table-responsive"><table class="markdown__table"><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
- }
-
- listitem(text) {
- const taskListReg = /^\[([ |xX])\] /;
- const isTaskList = taskListReg.exec(text);
-
- if (isTaskList) {
- return `<li class="list-item--task-list">${'<input type="checkbox" disabled="disabled" ' + (isTaskList[1] === ' ' ? '' : 'checked="checked" ') + '/> '}${text.replace(taskListReg, '')}</li>`;
- }
- return `<li>${text}</li>`;
- }
-
- text(txt) {
- return TextFormatting.doFormatText(txt, this.formattingOptions);
- }
-}
-
-class MattermostLexer extends marked.Lexer {
- token(originalSrc, top, bq) {
- let src = originalSrc.replace(/^ +$/gm, '');
-
- while (src) {
- // newline
- let cap = this.rules.newline.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- if (cap[0].length > 1) {
- this.tokens.push({
- type: 'space'
- });
- }
- }
-
- // code
- cap = this.rules.code.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- cap = cap[0].replace(/^ {4}/gm, '');
- this.tokens.push({
- type: 'code',
- text: this.options.pedantic ? cap : cap.replace(/\n+$/, '')
- });
- continue;
- }
-
- // fences (gfm)
- cap = this.rules.fences.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'code',
- lang: cap[2],
- text: cap[3] || ''
- });
- continue;
- }
-
- // heading
- cap = this.rules.heading.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'heading',
- depth: cap[1].length,
- text: cap[2]
- });
- continue;
- }
-
- // table no leading pipe (gfm)
- cap = this.rules.nptable.exec(src);
- if (top && cap) {
- src = src.substring(cap[0].length);
-
- const item = {
- type: 'table',
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
- align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
- cells: cap[3].replace(/\n$/, '').split('\n')
- };
-
- for (let i = 0; i < item.align.length; i++) {
- if (/^ *-+: *$/.test(item.align[i])) {
- item.align[i] = 'right';
- } else if (/^ *:-+: *$/.test(item.align[i])) {
- item.align[i] = 'center';
- } else if (/^ *:-+ *$/.test(item.align[i])) {
- item.align[i] = 'left';
- } else {
- item.align[i] = null;
- }
- }
-
- for (let i = 0; i < item.cells.length; i++) {
- item.cells[i] = item.cells[i].split(/ *\| */);
- }
-
- this.tokens.push(item);
-
- continue;
- }
-
- // lheading
- cap = this.rules.lheading.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'heading',
- depth: cap[2] === '=' ? 1 : 2,
- text: cap[1]
- });
- continue;
- }
-
- // hr
- cap = this.rules.hr.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'hr'
- });
- continue;
- }
-
- // blockquote
- cap = this.rules.blockquote.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
-
- this.tokens.push({
- type: 'blockquote_start'
- });
-
- cap = cap[0].replace(/^ *> ?/gm, '');
-
- // Pass `top` to keep the current
- // "toplevel" state. This is exactly
- // how markdown.pl works.
- this.token(cap, top, true);
-
- this.tokens.push({
- type: 'blockquote_end'
- });
-
- continue;
- }
-
- // list
- cap = this.rules.list.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- const bull = cap[2];
-
- this.tokens.push({
- type: 'list_start',
- ordered: bull.length > 1
- });
-
- // Get each top-level item.
- cap = cap[0].match(this.rules.item);
-
- let next = false;
- const l = cap.length;
- let i = 0;
-
- for (; i < l; i++) {
- let item = cap[i];
-
- // Remove the list item's bullet
- // so it is seen as the next token.
- let space = item.length;
- item = item.replace(/^ *([*+-]|\d+\.) +/, '');
-
- // Outdent whatever the
- // list item contains. Hacky.
- if (~item.indexOf('\n ')) {
- space -= item.length;
- item = this.options.pedantic ?
- item.replace(/^ {1,4}/gm, '') :
- item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '');
- }
-
- // Determine whether the next list item belongs here.
- // Backpedal if it does not belong in this list.
- if (this.options.smartLists && i !== l - 1) {
- const b = this.rules.bullet.exec(cap[i + 1])[0];
- if (bull !== b && !(bull.length > 1 && b.length > 1)) {
- src = cap.slice(i + 1).join('\n') + src;
- i = l - 1;
- }
- }
-
- // Determine whether item is loose or not.
- // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
- // for discount behavior.
- let loose = next || (/\n\n(?!\s*$)/).test(item);
- if (i !== l - 1) {
- next = item.charAt(item.length - 1) === '\n';
- if (!loose) {
- loose = next;
- }
- }
-
- this.tokens.push({
- type: loose ?
- 'loose_item_start' :
- 'list_item_start'
- });
-
- // Recurse.
- this.token(item, false, bq);
-
- this.tokens.push({
- type: 'list_item_end'
- });
- }
-
- this.tokens.push({
- type: 'list_end'
- });
-
- continue;
- }
-
- // html
- cap = this.rules.html.exec(src);
- if (cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: this.options.sanitize ? 'paragraph' : 'html',
- pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
- text: cap[0]
- });
- continue;
- }
-
- // def
- cap = this.rules.def.exec(src);
- if ((!bq && top) && cap) {
- src = src.substring(cap[0].length);
- this.tokens.links[cap[1].toLowerCase()] = {
- href: cap[2],
- title: cap[3]
- };
- continue;
- }
-
- // table (gfm)
- cap = this.rules.table.exec(src);
- if (top && cap) {
- src = src.substring(cap[0].length);
-
- const item = {
- type: 'table',
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
- align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
- cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
- };
-
- for (let i = 0; i < item.align.length; i++) {
- if (/^ *-+: *$/.test(item.align[i])) {
- item.align[i] = 'right';
- } else if (/^ *:-+: *$/.test(item.align[i])) {
- item.align[i] = 'center';
- } else if (/^ *:-+ *$/.test(item.align[i])) {
- item.align[i] = 'left';
- } else {
- item.align[i] = null;
- }
- }
-
- for (let i = 0; i < item.cells.length; i++) {
- item.cells[i] = item.cells[i].replace(/^ *\| *| *\| *$/g, '').split(/ *\| */);
- }
-
- this.tokens.push(item);
-
- continue;
- }
-
- // top-level paragraph
- cap = this.rules.paragraph.exec(src);
- if (top && cap) {
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'paragraph',
- text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
- });
- continue;
- }
-
- // text
- cap = this.rules.text.exec(src);
- if (cap) {
- // Top-level should never reach here.
- src = src.substring(cap[0].length);
- this.tokens.push({
- type: 'text',
- text: cap[0]
- });
- continue;
- }
-
- if (src) {
- throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
- }
- }
-
- return this.tokens;
- }
-}
-
-export function format(text, options) {
- const markdownOptions = {
- renderer: new MattermostMarkdownRenderer(null, options),
- sanitize: true,
- gfm: true,
- tables: true
- };
-
- const tokens = new MattermostLexer(markdownOptions).lex(text);
-
- return new MattermostParser(markdownOptions).parse(tokens);
-}
-
-// Marked helper functions that should probably just be exported
-
-function unescape(html) {
- return html.replace(/&([#\w]+);/g, (_, m) => {
- const n = m.toLowerCase();
- if (n === 'colon') {
- return ':';
- } else if (n.charAt(0) === '#') {
- return n.charAt(1) === 'x' ?
- String.fromCharCode(parseInt(n.substring(2), 16)) :
- String.fromCharCode(+n.substring(1));
- }
- return '';
- });
-}
diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx
deleted file mode 100644
index 552d93fac..000000000
--- a/web/react/utils/text_formatting.jsx
+++ /dev/null
@@ -1,402 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Autolinker from 'autolinker';
-import Constants from './constants.jsx';
-import * as Emoticons from './emoticons.jsx';
-import * as Markdown from './markdown.jsx';
-import UserStore from '../stores/user_store.jsx';
-import * as Utils from './utils.jsx';
-
-// Performs formatting of user posts including highlighting mentions and search terms and converting urls, hashtags, and
-// @mentions to links by taking a user's message and returning a string of formatted html. Also takes a number of options
-// as part of the second parameter:
-// - searchTerm - If specified, this word is highlighted in the resulting html. Defaults to nothing.
-// - mentionHighlight - Specifies whether or not to highlight mentions of the current user. Defaults to true.
-// - singleline - Specifies whether or not to remove newlines. Defaults to false.
-// - emoticons - Enables emoticon parsing. Defaults to true.
-// - markdown - Enables markdown parsing. Defaults to true.
-export function formatText(text, options = {}) {
- let output;
-
- if (!('markdown' in options) || options.markdown) {
- // the markdown renderer will call doFormatText as necessary
- output = Markdown.format(text, options);
- } else {
- output = sanitizeHtml(text);
- output = doFormatText(output, options);
- }
-
- // replace newlines with spaces if necessary
- if (options.singleline) {
- output = replaceNewlines(output);
- }
-
- return output;
-}
-
-// Performs most of the actual formatting work for formatText. Not intended to be called normally.
-export function doFormatText(text, options) {
- let output = text;
-
- const tokens = new Map();
-
- // replace important words and phrases with tokens
- output = autolinkAtMentions(output, tokens);
- output = autolinkEmails(output, tokens);
- output = autolinkHashtags(output, tokens);
-
- if (!('emoticons' in options) || options.emoticon) {
- output = Emoticons.handleEmoticons(output, tokens);
- }
-
- if (options.searchTerm) {
- output = highlightSearchTerm(output, tokens, options.searchTerm);
- }
-
- if (!('mentionHighlight' in options) || options.mentionHighlight) {
- output = highlightCurrentMentions(output, tokens);
- }
-
- // reinsert tokens with formatted versions of the important words and phrases
- output = replaceTokens(output, tokens);
-
- return output;
-}
-
-export function sanitizeHtml(text) {
- let output = text;
-
- // normal string.replace only does a single occurrance so use a regex instead
- output = output.replace(/&/g, '&amp;');
- output = output.replace(/</g, '&lt;');
- output = output.replace(/>/g, '&gt;');
- output = output.replace(/'/g, '&apos;');
- output = output.replace(/"/g, '&quot;');
-
- return output;
-}
-
-// Convert emails into tokens
-function autolinkEmails(text, tokens) {
- function replaceEmailWithToken(autolinker, match) {
- const linkText = match.getMatchedText();
- let url = linkText;
-
- if (match.getType() === 'email') {
- url = `mailto:${url}`;
- }
-
- const index = tokens.size;
- const alias = `MM_EMAIL${index}`;
-
- tokens.set(alias, {
- value: `<a class="theme" href="${url}">${linkText}</a>`,
- originalText: linkText
- });
-
- return alias;
- }
-
- // we can't just use a static autolinker because we need to set replaceFn
- const autolinker = new Autolinker({
- urls: false,
- email: true,
- phone: false,
- twitter: false,
- hashtag: false,
- replaceFn: replaceEmailWithToken
- });
-
- return autolinker.link(text);
-}
-
-function autolinkAtMentions(text, tokens) {
- // Return true if provided character is punctuation
- function isPunctuation(character) {
- const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/g;
- return re.test(character);
- }
-
- // Test if provided text needs to be highlighted, special mention or current user
- function mentionExists(u) {
- return (Constants.SPECIAL_MENTIONS.indexOf(u) !== -1 || UserStore.getProfileByUsername(u));
- }
-
- function addToken(username, mention) {
- const index = tokens.size;
- const alias = `MM_ATMENTION${index}`;
-
- tokens.set(alias, {
- value: `<a class='mention-link' href='#' data-mention='${username}'>${mention}</a>`,
- originalText: mention
- });
- return alias;
- }
-
- function replaceAtMentionWithToken(fullMatch, mention, username) {
- let usernameLower = username.toLowerCase();
-
- if (mentionExists(usernameLower)) {
- // Exact match
- const alias = addToken(usernameLower, mention, '');
- return alias;
- }
-
- // Not an exact match, attempt to truncate any punctuation to see if we can find a user
- const originalUsername = usernameLower;
-
- for (let c = usernameLower.length; c > 0; c--) {
- if (isPunctuation(usernameLower[c - 1])) {
- usernameLower = usernameLower.substring(0, c - 1);
-
- if (mentionExists(usernameLower)) {
- const suffix = originalUsername.substr(c - 1);
- const alias = addToken(usernameLower, '@' + usernameLower);
- return alias + suffix;
- }
- } else {
- // If the last character is not punctuation, no point in going any further
- break;
- }
- }
-
- return fullMatch;
- }
-
- let output = text;
- output = output.replace(/(@([a-z0-9.\-_]*))/gi, replaceAtMentionWithToken);
-
- return output;
-}
-
-function escapeRegex(text) {
- return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
-}
-
-function highlightCurrentMentions(text, tokens) {
- let output = text;
-
- const mentionKeys = UserStore.getCurrentMentionKeys();
-
- // look for any existing tokens which are self mentions and should be highlighted
- var newTokens = new Map();
- for (const [alias, token] of tokens) {
- if (mentionKeys.indexOf(token.originalText) !== -1) {
- const index = tokens.size + newTokens.size;
- const newAlias = `MM_SELFMENTION${index}`;
-
- newTokens.set(newAlias, {
- value: `<span class='mention--highlight'>${alias}</span>`,
- originalText: token.originalText
- });
- output = output.replace(alias, newAlias);
- }
- }
-
- // the new tokens are stashed in a separate map since we can't add objects to a map during iteration
- for (const newToken of newTokens) {
- tokens.set(newToken[0], newToken[1]);
- }
-
- // look for self mentions in the text
- function replaceCurrentMentionWithToken(fullMatch, prefix, mention) {
- const index = tokens.size;
- const alias = `MM_SELFMENTION${index}`;
-
- tokens.set(alias, {
- value: `<span class='mention--highlight'>${mention}</span>`,
- originalText: mention
- });
-
- return prefix + alias;
- }
-
- for (const mention of UserStore.getCurrentMentionKeys()) {
- output = output.replace(new RegExp(`(^|\\W)(${escapeRegex(mention)})\\b`, 'gi'), replaceCurrentMentionWithToken);
- }
-
- return output;
-}
-
-function autolinkHashtags(text, tokens) {
- let output = text;
-
- var newTokens = new Map();
- for (const [alias, token] of tokens) {
- if (token.originalText.lastIndexOf('#', 0) === 0) {
- const index = tokens.size + newTokens.size;
- const newAlias = `MM_HASHTAG${index}`;
-
- newTokens.set(newAlias, {
- value: `<a class='mention-link' href='#' data-hashtag='${token.originalText}'>${token.originalText}</a>`,
- originalText: token.originalText
- });
-
- output = output.replace(alias, newAlias);
- }
- }
-
- // the new tokens are stashed in a separate map since we can't add objects to a map during iteration
- for (const newToken of newTokens) {
- tokens.set(newToken[0], newToken[1]);
- }
-
- // look for hashtags in the text
- function replaceHashtagWithToken(fullMatch, prefix, hashtag) {
- const index = tokens.size;
- const alias = `MM_HASHTAG${index}`;
-
- let value = hashtag;
-
- if (hashtag.length > Constants.MIN_HASHTAG_LINK_LENGTH) {
- value = `<a class='mention-link' href='#' data-hashtag='${hashtag}'>${hashtag}</a>`;
- }
-
- tokens.set(alias, {
- value,
- originalText: hashtag
- });
-
- return prefix + alias;
- }
-
- return output.replace(/(^|\W)(#[a-zA-ZĆ¤Ć¶Ć¼Ć„Ć–ĆœĆŸ][a-zA-Z0-9Ć¤Ć¶Ć¼Ć„Ć–ĆœĆŸ.\-_]*)\b/g, replaceHashtagWithToken);
-}
-
-const puncStart = /^[.,()&$!\[\]{}':;\\]+/;
-const puncEnd = /[.,()&$#!\[\]{}':;\\]+$/;
-
-function parseSearchTerms(searchTerm) {
- let terms = [];
-
- let termString = searchTerm;
-
- while (termString) {
- let captured;
-
- // check for a quoted string
- captured = (/^"(.*?)"/).exec(termString);
- if (captured) {
- termString = termString.substring(captured[0].length);
- terms.push(captured[1]);
- continue;
- }
-
- // check for a search flag (and don't add it to terms)
- captured = (/^(?:in|from|channel): ?\S+/).exec(termString);
- if (captured) {
- termString = termString.substring(captured[0].length);
- continue;
- }
-
- // capture any plain text up until the next quote or search flag
- captured = (/^.+?(?=\bin|\bfrom|\bchannel|"|$)/).exec(termString);
- if (captured) {
- termString = termString.substring(captured[0].length);
-
- // break the text up into words based on how the server splits them in SqlPostStore.SearchPosts and then discard empty terms
- terms.push(...captured[0].split(/[ <>+\-\(\)\~\@]/).filter((term) => !!term));
- continue;
- }
-
- // we should never reach this point since at least one of the regexes should match something in the remaining text
- throw new Error('Infinite loop in search term parsing: ' + termString);
- }
-
- // remove punctuation from each term
- terms = terms.map((term) => term.replace(puncStart, '').replace(puncEnd, ''));
-
- return terms;
-}
-
-function convertSearchTermToRegex(term) {
- let pattern;
- if (term.endsWith('*')) {
- pattern = '\\b' + escapeRegex(term.substring(0, term.length - 1));
- } else {
- pattern = '\\b' + escapeRegex(term) + '\\b';
- }
-
- return new RegExp(pattern, 'gi');
-}
-
-function highlightSearchTerm(text, tokens, searchTerm) {
- const terms = parseSearchTerms(searchTerm);
-
- if (terms.length === 0) {
- return text;
- }
-
- let output = text;
-
- function replaceSearchTermWithToken(word) {
- const index = tokens.size;
- const alias = `MM_SEARCHTERM${index}`;
-
- tokens.set(alias, {
- value: `<span class='search-highlight'>${word}</span>`,
- originalText: word
- });
-
- return alias;
- }
-
- for (const term of terms) {
- // highlight existing tokens matching search terms
- var newTokens = new Map();
- for (const [alias, token] of tokens) {
- if (token.originalText === term.replace(/\*$/, '')) {
- const index = tokens.size + newTokens.size;
- const newAlias = `MM_SEARCHTERM${index}`;
-
- newTokens.set(newAlias, {
- value: `<span class='search-highlight'>${alias}</span>`,
- originalText: token.originalText
- });
-
- output = output.replace(alias, newAlias);
- }
- }
-
- // the new tokens are stashed in a separate map since we can't add objects to a map during iteration
- for (const newToken of newTokens) {
- tokens.set(newToken[0], newToken[1]);
- }
-
- output = output.replace(convertSearchTermToRegex(term), replaceSearchTermWithToken);
- }
-
- return output;
-}
-
-function replaceTokens(text, tokens) {
- let output = text;
-
- // iterate backwards through the map so that we do replacement in the opposite order that we added tokens
- const aliases = [...tokens.keys()];
- for (let i = aliases.length - 1; i >= 0; i--) {
- const alias = aliases[i];
- const token = tokens.get(alias);
- output = output.replace(alias, token.value);
- }
-
- return output;
-}
-
-function replaceNewlines(text) {
- return text.replace(/\n/g, ' ');
-}
-
-// A click handler that can be used with the results of TextFormatting.formatText to add default functionality
-// to clicked hashtags and @mentions.
-export function handleClick(e) {
- const mentionAttribute = e.target.getAttributeNode('data-mention');
- const hashtagAttribute = e.target.getAttributeNode('data-hashtag');
-
- if (mentionAttribute) {
- Utils.searchForTerm(mentionAttribute.value);
- } else if (hashtagAttribute) {
- Utils.searchForTerm(hashtagAttribute.value);
- }
-}
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
deleted file mode 100644
index 360d1f4a5..000000000
--- a/web/react/utils/utils.jsx
+++ /dev/null
@@ -1,1416 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as GlobalActions from '../action_creators/global_actions.jsx';
-import ChannelStore from '../stores/channel_store.jsx';
-import UserStore from '../stores/user_store.jsx';
-import LocalizationStore from '../stores/localization_store.jsx';
-import PreferenceStore from '../stores/preference_store.jsx';
-import TeamStore from '../stores/team_store.jsx';
-import Constants from '../utils/constants.jsx';
-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 {FormattedTime} from 'mm-intl';
-
-export function isEmail(email) {
- // writing a regex to match all valid email addresses is really, really hard (see http://stackoverflow.com/a/201378)
- // so we just do a simple check and rely on a verification email to tell if it's a real address
- return (/^.+@.+$/).test(email);
-}
-
-export function cleanUpUrlable(input) {
- var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-');
- cleaned = cleaned.replace(/-{2,}/, '-');
- cleaned = cleaned.replace(/^\-+/, '');
- cleaned = cleaned.replace(/\-+$/, '');
- return cleaned;
-}
-
-export function isTestDomain() {
- if ((/^localhost/).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^dockerhost/).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^test/).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^127.0./).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^192.168./).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^10./).test(window.location.hostname)) {
- return true;
- }
-
- if ((/^176./).test(window.location.hostname)) {
- return true;
- }
-
- return false;
-}
-
-export function isChrome() {
- if (navigator.userAgent.indexOf('Chrome') > -1) {
- return true;
- }
- return false;
-}
-
-export function isSafari() {
- if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
- return true;
- }
- return false;
-}
-
-export function isIosChrome() {
- // https://developer.chrome.com/multidevice/user-agent
- return navigator.userAgent.indexOf('CriOS') !== -1;
-}
-
-export function isMobileApp() {
- const userAgent = navigator.userAgent;
-
- // the mobile app has different user agents for the native api calls and the shim, so handle them both
- const isApi = userAgent.indexOf('Mattermost') !== -1;
- const isShim = userAgent.indexOf('iPhone') !== -1 && userAgent.indexOf('Safari') === -1 && userAgent.indexOf('Chrome') === -1;
-
- return isApi || isShim;
-}
-
-export function isInRole(roles, inRole) {
- var parts = roles.split(' ');
- for (var i = 0; i < parts.length; i++) {
- if (parts[i] === inRole) {
- return true;
- }
- }
-
- return false;
-}
-
-export function isAdmin(roles) {
- if (isInRole(roles, 'admin')) {
- return true;
- }
-
- if (isInRole(roles, 'system_admin')) {
- return true;
- }
-
- return false;
-}
-
-export function isSystemAdmin(roles) {
- if (isInRole(roles, 'system_admin')) {
- return true;
- }
-
- return false;
-}
-
-export function getDomainWithOutSub() {
- var parts = window.location.host.split('.');
-
- if (parts.length === 1) {
- if (parts[0].indexOf('dockerhost') > -1) {
- return 'dockerhost:8065';
- }
-
- return 'localhost:8065';
- }
-
- return parts[1] + '.' + parts[2];
-}
-
-export function getCookie(name) {
- var value = '; ' + document.cookie;
- var parts = value.split('; ' + name + '=');
- if (parts.length === 2) {
- return parts.pop().split(';').shift();
- }
- return '';
-}
-
-var requestedNotificationPermission = false;
-
-export function notifyMe(title, body, channel) {
- if (!('Notification' in window)) {
- return;
- }
-
- if (Notification.permission === 'granted' || (Notification.permission === 'default' && !requestedNotificationPermission)) {
- requestedNotificationPermission = true;
-
- Notification.requestPermission((permission) => {
- if (permission === 'granted') {
- try {
- var notification = new Notification(title, {body: body, tag: body, icon: '/static/images/icon50x50.png'});
- notification.onclick = () => {
- window.focus();
- if (channel) {
- switchChannel(channel);
- } else {
- window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
- }
- };
- setTimeout(() => {
- notification.close();
- }, 5000);
- } catch (e) {
- console.error(e); //eslint-disable-line no-console
- }
- }
- });
- }
-}
-
-var canDing = true;
-
-export function ding() {
- if (!isBrowserFirefox() && canDing) {
- var audio = new Audio('/static/images/bing.mp3');
- audio.play();
- canDing = false;
- setTimeout(() => {
- canDing = true;
- return;
- }, 3000);
- }
-}
-
-export function getUrlParameter(sParam) {
- var sPageURL = window.location.search.substring(1);
- var sURLVariables = sPageURL.split('&');
- for (var i = 0; i < sURLVariables.length; i++) {
- var sParameterName = sURLVariables[i].split('=');
- if (sParameterName[0] === sParam) {
- return sParameterName[1];
- }
- }
- return null;
-}
-
-export function getDateForUnixTicks(ticks) {
- return new Date(ticks);
-}
-
-export function displayDate(ticks) {
- var d = new Date(ticks);
- var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
-
- return monthNames[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear();
-}
-
-export function displayTime(ticks, utc) {
- const d = new Date(ticks);
- let hours;
- let minutes;
- let ampm = '';
- let timezone = '';
-
- if (utc) {
- hours = d.getUTCHours();
- minutes = d.getUTCMinutes();
- timezone = ' UTC';
- } else {
- hours = d.getHours();
- minutes = d.getMinutes();
- }
-
- if (minutes <= 9) {
- minutes = '0' + minutes;
- }
-
- const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
- if (!useMilitaryTime) {
- ampm = ' AM';
- if (hours >= 12) {
- ampm = ' PM';
- }
-
- hours = hours % 12;
- if (!hours) {
- hours = '12';
- }
- }
-
- return hours + ':' + minutes + ampm + timezone;
-}
-
-export function displayTimeFormatted(ticks) {
- const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
-
- return (
- <FormattedTime
- value={ticks}
- hour='numeric'
- minute='numeric'
- hour12={!useMilitaryTime}
- />
- );
-}
-
-export function isMilitaryTime() {
- return PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
-}
-
-export function displayDateTime(ticks) {
- var seconds = Math.floor((Date.now() - ticks) / 1000);
-
- var interval = Math.floor(seconds / 3600);
-
- if (interval > 24) {
- return this.displayTime(ticks);
- }
-
- if (interval > 1) {
- return interval + ' hours ago';
- }
-
- if (interval === 1) {
- return interval + ' hour ago';
- }
-
- interval = Math.floor(seconds / 60);
- if (interval >= 2) {
- return interval + ' minutes ago';
- }
-
- if (interval >= 1) {
- return '1 minute ago';
- }
-
- return 'just now';
-}
-
-export function displayCommentDateTime(ticks) {
- return displayDate(ticks) + ' ' + displayTime(ticks);
-}
-
-// returns Unix timestamp in milliseconds
-export function getTimestamp() {
- return Date.now();
-}
-
-// extracts links not styled by Markdown
-export function extractLinks(text) {
- const links = [];
- let inText = text;
-
- // strip out code blocks
- inText = inText.replace(/`[^`]*`/g, '');
-
- // 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);
- }
-
- Autolinker.link(
- inText,
- {
- replaceFn,
- urls: {schemeMatches: true, wwwMatches: true, tldMatches: false},
- emails: false,
- twitter: false,
- phone: false,
- hashtag: false
- }
- );
-
- return links;
-}
-
-export function escapeRegExp(string) {
- return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
-}
-
-// Taken from http://stackoverflow.com/questions/1068834/object-comparison-in-javascript and modified slightly
-export function areObjectsEqual(x, y) {
- let p;
- const leftChain = [];
- const rightChain = [];
-
- // Remember that NaN === NaN returns false
- // and isNaN(undefined) returns true
- if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
- return true;
- }
-
- // Compare primitives and functions.
- // Check if both arguments link to the same object.
- // Especially useful on step when comparing prototypes
- if (x === y) {
- return true;
- }
-
- // Works in case when functions are created in constructor.
- // Comparing dates is a common scenario. Another built-ins?
- // We can even handle functions passed across iframes
- if ((typeof x === 'function' && typeof y === 'function') ||
- (x instanceof Date && y instanceof Date) ||
- (x instanceof RegExp && y instanceof RegExp) ||
- (x instanceof String && y instanceof String) ||
- (x instanceof Number && y instanceof Number)) {
- return x.toString() === y.toString();
- }
-
- if (x instanceof Map && y instanceof Map) {
- return areMapsEqual(x, y);
- }
-
- // At last checking prototypes as good a we can
- if (!(x instanceof Object && y instanceof Object)) {
- return false;
- }
-
- if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
- return false;
- }
-
- if (x.constructor !== y.constructor) {
- return false;
- }
-
- if (x.prototype !== y.prototype) {
- return false;
- }
-
- // Check for infinitive linking loops
- if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
- return false;
- }
-
- // Quick checking of one object beeing a subset of another.
- for (p in y) {
- if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
- return false;
- } else if (typeof y[p] !== typeof x[p]) {
- return false;
- }
- }
-
- for (p in x) {
- if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
- return false;
- } else if (typeof y[p] !== typeof x[p]) {
- return false;
- }
-
- switch (typeof (x[p])) {
- case 'object':
- case 'function':
-
- leftChain.push(x);
- rightChain.push(y);
-
- if (!areObjectsEqual(x[p], y[p])) {
- return false;
- }
-
- leftChain.pop();
- rightChain.pop();
- break;
-
- default:
- if (x[p] !== y[p]) {
- return false;
- }
- break;
- }
- }
-
- return true;
-}
-
-export function areMapsEqual(a, b) {
- if (a.size !== b.size) {
- return false;
- }
-
- for (const [key, value] of a) {
- if (!b.has(key)) {
- return false;
- }
-
- if (!areObjectsEqual(value, b.get(key))) {
- return false;
- }
- }
-
- return true;
-}
-
-export function replaceHtmlEntities(text) {
- var tagsToReplace = {
- '&amp;': '&',
- '&lt;': '<',
- '&gt;': '>'
- };
- var newtext = text;
- for (var tag in tagsToReplace) {
- if ({}.hasOwnProperty.call(tagsToReplace, tag)) {
- var regex = new RegExp(tag, 'g');
- newtext = newtext.replace(regex, tagsToReplace[tag]);
- }
- }
- return newtext;
-}
-
-export function insertHtmlEntities(text) {
- var tagsToReplace = {
- '&': '&amp;',
- '<': '&lt;',
- '>': '&gt;'
- };
- var newtext = text;
- for (var tag in tagsToReplace) {
- if ({}.hasOwnProperty.call(tagsToReplace, tag)) {
- var regex = new RegExp(tag, 'g');
- newtext = newtext.replace(regex, tagsToReplace[tag]);
- }
- }
- return newtext;
-}
-
-export function searchForTerm(term) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH_TERM,
- term: term,
- do_search: true
- });
-}
-
-export function getFileType(extin) {
- var ext = extin.toLowerCase();
- if (Constants.IMAGE_TYPES.indexOf(ext) > -1) {
- return 'image';
- }
-
- if (Constants.AUDIO_TYPES.indexOf(ext) > -1) {
- return 'audio';
- }
-
- if (Constants.VIDEO_TYPES.indexOf(ext) > -1) {
- return 'video';
- }
-
- if (Constants.SPREADSHEET_TYPES.indexOf(ext) > -1) {
- return 'spreadsheet';
- }
-
- if (Constants.CODE_TYPES.indexOf(ext) > -1) {
- return 'code';
- }
-
- if (Constants.WORD_TYPES.indexOf(ext) > -1) {
- return 'word';
- }
-
- if (Constants.PRESENTATION_TYPES.indexOf(ext) > -1) {
- return 'presentation';
- }
-
- if (Constants.PDF_TYPES.indexOf(ext) > -1) {
- return 'pdf';
- }
-
- if (Constants.PATCH_TYPES.indexOf(ext) > -1) {
- return 'patch';
- }
-
- return 'other';
-}
-
-export function getPreviewImagePathForFileType(fileTypeIn) {
- var fileType = fileTypeIn.toLowerCase();
-
- var icon;
- if (fileType in Constants.ICON_FROM_TYPE) {
- icon = Constants.ICON_FROM_TYPE[fileType];
- } else {
- icon = Constants.ICON_FROM_TYPE.other;
- }
-
- return '/static/images/icons/' + icon + '.png';
-}
-
-export function getIconClassName(fileTypeIn) {
- var fileType = fileTypeIn.toLowerCase();
-
- if (fileType in Constants.ICON_FROM_TYPE) {
- return Constants.ICON_FROM_TYPE[fileType];
- }
-
- return 'glyphicon-file';
-}
-
-export function splitFileLocation(fileLocation) {
- var fileSplit = fileLocation.split('.');
-
- var ext = '';
- if (fileSplit.length > 1) {
- ext = fileSplit[fileSplit.length - 1];
- fileSplit.splice(fileSplit.length - 1, 1);
- }
-
- var filePath = fileSplit.join('.');
- var filename = filePath.split('/')[filePath.split('/').length - 1];
-
- return {ext: ext, name: filename, path: filePath};
-}
-
-export function getPreviewImagePath(filename) {
- // Returns the path to a preview image that can be used to represent a file.
- const fileInfo = splitFileLocation(filename);
- const fileType = getFileType(fileInfo.ext);
-
- if (fileType === 'image') {
- return getFileUrl(fileInfo.path + '_preview.jpg');
- }
-
- // only images have proper previews, so just use a placeholder icon for non-images
- return getPreviewImagePathForFileType(fileType);
-}
-
-export function toTitleCase(str) {
- function doTitleCase(txt) {
- return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
- }
- return str.replace(/\w\S*/g, doTitleCase);
-}
-
-export function applyTheme(theme) {
- if (theme.sidebarBg) {
- changeCss('.sidebar--left, .modal .settings-modal .settings-table .settings-links, .sidebar--menu', 'background:' + theme.sidebarBg, 1);
- changeCss('body', 'scrollbar-face-color:' + theme.sidebarBg, 3);
- }
-
- if (theme.sidebarText) {
- changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .modal .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1);
- changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1);
- changeCss('.sidebar--left .nav-pills__container li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1);
- changeCss('.sidebar--left .add-channel-btn:hover, .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1);
- changeCss('.sidebar--left .status .offline--icon, .sidebar--left .status .offline--icon', 'fill:' + theme.sidebarText, 1);
- changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2);
- }
-
- if (theme.sidebarUnreadText) {
- changeCss('.sidebar--left .nav-pills__container li>a.unread-title', 'color:' + theme.sidebarUnreadText + '!important;', 2);
- }
-
- if (theme.sidebarTextHoverBg) {
- changeCss('.sidebar--left .nav-pills__container li>a:hover, .sidebar--left .nav-pills__container li>a:focus, .modal .settings-modal .nav-pills>li:hover a, .modal .settings-modal .nav-pills>li:focus a', 'background:' + theme.sidebarTextHoverBg, 1);
- changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1);
- }
-
- if (theme.sidebarTextActiveBorder) {
- changeCss('.sidebar--left .nav li.active a:before, .modal .settings-modal .nav-pills>li.active a:before', 'background:' + theme.sidebarTextActiveBorder, 1);
- }
-
- if (theme.sidebarTextActiveColor) {
- changeCss('.sidebar--left .nav-pills__container li.active a, .sidebar--left .nav-pills__container li.active a:hover, .sidebar--left .nav-pills__container li.active a:focus, .modal .settings-modal .nav-pills>li.active a, .modal .settings-modal .nav-pills>li.active a:hover, .modal .settings-modal .nav-pills>li.active a:active', 'color:' + theme.sidebarTextActiveColor, 2);
- changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'background:' + changeOpacity(theme.sidebarTextActiveColor, 0.1), 1);
- }
-
- if (theme.sidebarHeaderBg) {
- changeCss('.sidebar--left .team__header, .sidebar--menu .team__header, .post-list__timestamp', 'background:' + theme.sidebarHeaderBg, 1);
- changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1);
- changeCss('#navbar .navbar-default', 'background:' + theme.sidebarHeaderBg, 1);
- changeCss('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1);
- changeCss('.attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg, 1);
- }
-
- if (theme.sidebarHeaderTextColor) {
- changeCss('.sidebar--left .team__header .header__info, .sidebar--menu .team__header .header__info, .post-list__timestamp', 'color:' + theme.sidebarHeaderTextColor, 1);
- changeCss('.sidebar--left .team__header .navbar-right .dropdown__icon, .sidebar--menu .team__header .navbar-right .dropdown__icon', 'fill:' + theme.sidebarHeaderTextColor, 1);
- changeCss('.sidebar--left .team__header .user__name, .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8), 1);
- changeCss('.sidebar--left .team__header:hover .user__name, .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor, 1);
- changeCss('.modal .modal-header .modal-title, .modal .modal-header .modal-title .name, .modal .modal-header button.close', 'color:' + theme.sidebarHeaderTextColor, 1);
- changeCss('#navbar .navbar-default .navbar-brand .heading', 'color:' + theme.sidebarHeaderTextColor, 1);
- changeCss('#navbar .navbar-default .navbar-toggle .icon-bar, ', 'background:' + theme.sidebarHeaderTextColor, 1);
- changeCss('@media(max-width: 768px){.search-bar__container', 'color:' + theme.sidebarHeaderTextColor, 2);
- }
-
- if (theme.onlineIndicator) {
- changeCss('.sidebar--left .status .online--icon', 'fill:' + theme.onlineIndicator, 1);
- }
-
- if (theme.awayIndicator) {
- changeCss('.sidebar--left .status .away--icon', 'fill:' + theme.awayIndicator, 1);
- }
-
- if (theme.mentionBj) {
- changeCss('.sidebar--left .nav-pills__unread-indicator', 'background:' + theme.mentionBj, 1);
- changeCss('.sidebar--left .badge', 'background:' + theme.mentionBj + '!important;', 1);
- }
-
- if (theme.mentionColor) {
- changeCss('.sidebar--left .nav-pills__unread-indicator', 'color:' + theme.mentionColor, 2);
- changeCss('.sidebar--left .badge', 'color:' + theme.mentionColor + '!important;', 2);
- }
-
- if (theme.centerChannelBg) {
- changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content', 'background:' + theme.centerChannelBg, 1);
- changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1);
- changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
- changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);
- changeCss('.post-image__details, .search-help-popover .search-autocomplete__divider span', 'background:' + theme.centerChannelBg, 1);
- changeCss('.sidebar--right, .dropdown-menu, .popover, .tip-overlay', 'background:' + theme.centerChannelBg, 1);
- changeCss('.popover.bottom>.arrow:after', 'border-bottom-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.right>.arrow:after, .tip-overlay.tip-overlay--sidebar .arrow, .tip-overlay.tip-overlay--header .arrow', 'border-right-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.top>.arrow:after, .tip-overlay.tip-overlay--chat .arrow', 'border-top-color:' + theme.centerChannelBg, 1);
- changeCss('@media(min-width: 768px){.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1);
- changeCss('.attachment__content', 'background:' + theme.centerChannelBg, 1);
- changeCss('body', 'scrollbar-face-color:' + theme.centerChannelBg, 2);
- changeCss('body', 'scrollbar-track-color:' + theme.centerChannelBg, 2);
- }
-
- if (theme.centerChannelColor) {
- changeCss('.post-list__arrows', 'fill:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
- changeCss('.sidebar--left, .sidebar--right .sidebar--right__header, .suggestion-list__content .command', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .suggestion-list__content .command, .modal .modal-content, .dropdown-menu, .popover, .mentions__name, .tip-overlay', 'color:' + theme.centerChannelColor, 1);
- changeCss('#archive-link-home', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
- changeCss('#post-create', 'color:' + theme.centerChannelColor, 2);
- changeCss('.mentions--top, .suggestion-list', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3);
- changeCss('.mentions--top, .suggestion-list', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2);
- changeCss('.mentions--top, .suggestion-list', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1);
- changeCss('.dropdown-menu, .popover ', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 3);
- changeCss('.dropdown-menu, .popover ', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 2);
- changeCss('.dropdown-menu, .popover ', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 1);
- changeCss('.post__body hr, .loading-screen .loading__content .round, .tutorial__circles .circle', 'background:' + theme.centerChannelColor, 1);
- changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1);
- changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
- changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
- changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
- changeCss('.custom-textarea, .custom-textarea:focus, .file-preview, .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .suggestion-list__content, .modal .modal-content, .modal .settings-modal .settings-table .settings-content .divider-light, .webhooks__container, .dropdown-menu, .modal .modal-header, .popover', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.popover.bottom>.arrow', 'border-bottom-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1);
- changeCss('.search-help-popover .search-autocomplete__divider span', 'color:' + changeOpacity(theme.centerChannelColor, 0.7), 1);
- changeCss('.popover.right>.arrow', 'border-right-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1);
- changeCss('.popover.left>.arrow', 'border-left-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1);
- changeCss('.popover.top>.arrow', 'border-top-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1);
- changeCss('.suggestion-list__content .command, .popover .popover-title', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.dropdown-menu .divider, .search-help-popover .search-autocomplete__divider:before', 'background:' + theme.centerChannelColor, 1);
- changeCss('.custom-textarea', 'color:' + theme.centerChannelColor, 1);
- changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2);
- changeCss('.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('@media(min-width: 768px){.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2);
- changeCss('.input-group-addon, .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('.attachment .attachment__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
- changeCss('.channel-intro .channel-intro__content, .webhooks__container', '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', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.search-item-container, .post-right__container .post.post--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 1);
- changeCss('.modal .custom-textarea:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1);
- changeCss('.channel-intro, .modal .settings-modal .settings-table .settings-content .divider-dark, hr, .modal .settings-modal .settings-table .settings-links, .modal .settings-modal .settings-table .settings-content .appearance-section .theme-elements__header', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
- changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, pre, .post-right__container .post.post--root', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
- changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, .post.same--root.post--comment .post__body, .more-modal__list .more-modal__row, .member-div:first-child, .member-div, .access-history__table .access__report, .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 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, .more-modal__list .more-modal__row:hover, .modal .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('.suggestion-list__content .command:hover, .mentions__name:hover, .suggestion--selected, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover, .bot-indicator', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1);
- changeCss('code, .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control', 'background:' + changeOpacity(theme.centerChannelColor, 0.1), 1);
- changeCss('@media(min-width: 960px){.post.current--user:hover .post__body ', 'background: none;', 1);
- changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2);
- changeCss('.search-help-popover .search-autocomplete__item:hover, .modal .settings-modal .settings-table .settings-content .appearance-section .theme-elements__body', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1);
- 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);
- }
-
- if (theme.newMessageSeparator) {
- changeCss('.new-separator .separator__text', 'color:' + theme.newMessageSeparator, 1);
- changeCss('.new-separator .separator__hr', 'border-color:' + changeOpacity(theme.newMessageSeparator, 0.5), 1);
- }
-
- if (theme.linkColor) {
- changeCss('a, a:focus, a:hover, .btn, .btn:focus, .btn:hover', 'color:' + theme.linkColor, 1);
- changeCss('.post .comment-icon__container, .post .post__reply', 'fill:' + theme.linkColor, 1);
- }
-
- if (theme.buttonBg) {
- changeCss('.btn.btn-primary, .tutorial__circles .circle.active', 'background:' + theme.buttonBg, 1);
- changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1);
- changeCss('.file-playback__controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1);
- }
-
- if (theme.buttonColor) {
- changeCss('.btn.btn-primary', 'color:' + theme.buttonColor, 2);
- }
-
- if (theme.mentionHighlightBg) {
- changeCss('.mention--highlight, .search-highlight', 'background:' + theme.mentionHighlightBg, 1);
- }
-
- if (theme.mentionHighlightBg) {
- changeCss('.post.post--highlight', 'background:' + changeOpacity(theme.mentionHighlightBg, 0.5), 1);
- }
-
- if (theme.mentionHighlightLink) {
- changeCss('.mention--highlight .mention-link', 'color:' + theme.mentionHighlightLink, 1);
- }
-
- if (!theme.codeTheme) {
- theme.codeTheme = Constants.DEFAULT_CODE_THEME;
- }
- updateCodeTheme(theme.codeTheme);
-}
-
-export function applyFont(fontName) {
- const body = $('body');
-
- for (const key of Reflect.ownKeys(Constants.FONTS)) {
- const className = Constants.FONTS[key];
-
- if (fontName === key) {
- if (!body.hasClass(className)) {
- body.addClass(className);
- }
- } else {
- body.removeClass(className);
- }
- }
-}
-
-export function changeCss(className, classValue, classRepeat) {
- // we need invisible container to store additional css definitions
- var cssMainContainer = $('#css-modifier-container');
- if (cssMainContainer.length === 0) {
- cssMainContainer = $('<div id="css-modifier-container"></div>');
- cssMainContainer.hide();
- cssMainContainer.appendTo($('body'));
- }
-
- // and we need one div for each class
- var classContainer = cssMainContainer.find('div[data-class="' + className + classRepeat + '"]');
- if (classContainer.length === 0) {
- classContainer = $('<div data-class="' + className + classRepeat + '"></div>');
- classContainer.appendTo(cssMainContainer);
- }
-
- // append additional style
- classContainer.html('<style>' + className + ' {' + classValue + '}</style>');
-}
-
-export function rgb2hex(rgbIn) {
- if (/^#[0-9A-F]{6}$/i.test(rgbIn)) {
- return rgbIn;
- }
-
- var rgb = rgbIn.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
- function hex(x) {
- return ('0' + parseInt(x, 10).toString(16)).slice(-2);
- }
- return '#' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
-}
-
-export function updateCodeTheme(theme) {
- const path = '/static/css/highlight/' + theme + '.css';
- const $link = $('link.code_theme');
- if (path !== $link.attr('href')) {
- changeCss('code.hljs', 'visibility: hidden');
- var xmlHTTP = new XMLHttpRequest();
- xmlHTTP.open('GET', path, true);
- xmlHTTP.onload = function onLoad() {
- $link.attr('href', path);
- if (isBrowserFirefox()) {
- $link.one('load', () => {
- changeCss('code.hljs', 'visibility: visible');
- });
- } else {
- changeCss('code.hljs', 'visibility: visible');
- }
- };
- xmlHTTP.send();
- }
-}
-
-export function placeCaretAtEnd(el) {
- el.focus();
- el.selectionStart = el.value.length;
- el.selectionEnd = el.value.length;
-
- return;
-}
-
-export function getCaretPosition(el) {
- if (el.selectionStart) {
- return el.selectionStart;
- } else if (document.selection) {
- el.focus();
-
- var r = document.selection.createRange();
- if (r == null) {
- return 0;
- }
-
- var re = el.createTextRange();
- var rc = re.duplicate();
- re.moveToBookmark(r.getBookmark());
- rc.setEndPoint('EndToStart', re);
-
- return rc.text.length;
- }
- return 0;
-}
-
-export function setSelectionRange(input, selectionStart, selectionEnd) {
- if (input.setSelectionRange) {
- input.focus();
- input.setSelectionRange(selectionStart, selectionEnd);
- } else if (input.createTextRange) {
- var range = input.createTextRange();
- range.collapse(true);
- range.moveEnd('character', selectionEnd);
- range.moveStart('character', selectionStart);
- range.select();
- }
-}
-
-export function setCaretPosition(input, pos) {
- setSelectionRange(input, pos, pos);
-}
-
-export function getSelectedText(input) {
- var selectedText;
- if (typeof document.selection !== 'undefined') {
- input.focus();
- var sel = document.selection.createRange();
- selectedText = sel.text;
- } else if (typeof input.selectionStart !== 'undefined') {
- var startPos = input.selectionStart;
- var endPos = input.selectionEnd;
- selectedText = input.value.substring(startPos, endPos);
- }
-
- return selectedText;
-}
-
-export function isValidUsername(name) {
- var error = '';
- if (!name) {
- error = 'This field is required';
- } else if (name.length < Constants.MIN_USERNAME_LENGTH || name.length > Constants.MAX_USERNAME_LENGTH) {
- error = 'Must be between ' + Constants.MIN_USERNAME_LENGTH + ' and ' + Constants.MAX_USERNAME_LENGTH + ' characters';
- } else if (!(/^[a-z0-9\.\-\_]+$/).test(name)) {
- error = "Must contain only letters, numbers, and the symbols '.', '-', and '_'.";
- } else if (!(/[a-z]/).test(name.charAt(0))) { //eslint-disable-line no-negated-condition
- error = 'First character must be a letter.';
- } else {
- for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) {
- if (name === Constants.RESERVED_USERNAMES[i]) {
- error = 'Cannot use a reserved word as a username.';
- break;
- }
- }
- }
-
- return error;
-}
-
-export function updateAddressBar(channelName) {
- const teamURL = TeamStore.getCurrentTeamUrl();
- history.replaceState('data', '', teamURL + '/channels/' + channelName);
-}
-
-export function switchChannel(channel) {
- GlobalActions.emitChannelClickEvent(channel);
-
- updateAddressBar(channel.name);
-
- $('.inner-wrap').removeClass('move--right');
- $('.sidebar--left').removeClass('move--right');
-
- client.trackPage();
-
- return false;
-}
-
-export function isMobile() {
- return screen.width <= 768;
-}
-
-export function isComment(post) {
- if ('root_id' in post) {
- return post.root_id !== '' && post.root_id != null;
- }
- return false;
-}
-
-export function getDirectTeammate(channelId) {
- var userIds = ChannelStore.get(channelId).name.split('__');
- var curUserId = UserStore.getCurrentId();
- var teammate = {};
-
- if (userIds.length !== 2 || userIds.indexOf(curUserId) === -1) {
- return teammate;
- }
-
- for (var idx in userIds) {
- if (userIds[idx] !== curUserId) {
- teammate = UserStore.getProfile(userIds[idx]);
- break;
- }
- }
-
- return teammate;
-}
-
-Image.prototype.load = function imageLoad(url, progressCallback) {
- var self = this;
- var xmlHTTP = new XMLHttpRequest();
- xmlHTTP.open('GET', url, true);
- xmlHTTP.responseType = 'arraybuffer';
- xmlHTTP.onload = function onLoad() {
- var h = xmlHTTP.getAllResponseHeaders();
- var m = h.match(/^Content-Type\:\s*(.*?)$/mi);
- var mimeType = m[1] || 'image/png';
-
- var blob = new Blob([this.response], {type: mimeType});
- self.src = window.URL.createObjectURL(blob);
- };
- xmlHTTP.onprogress = function onprogress(e) {
- parseInt(self.completedPercentage = (e.loaded / e.total) * 100, 10);
- if (progressCallback) {
- progressCallback();
- }
- };
- xmlHTTP.onloadstart = function onloadstart() {
- self.completedPercentage = 0;
- };
- xmlHTTP.send();
-};
-
-Image.prototype.completedPercentage = 0;
-
-export function changeColor(colourIn, amt) {
- var hex = colourIn;
- var lum = amt;
-
- // validate hex string
- hex = String(hex).replace(/[^0-9a-f]/gi, '');
- if (hex.length < 6) {
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
- }
- lum = lum || 0;
-
- // convert to decimal and change luminosity
- var rgb = '#';
- var c;
- var i;
- for (i = 0; i < 3; i++) {
- c = parseInt(hex.substr(i * 2, 2), 16);
- c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
- rgb += ('00' + c).substr(c.length);
- }
-
- return rgb;
-}
-
-export function changeOpacity(oldColor, opacity) {
- var color = oldColor;
- if (color[0] === '#') {
- color = color.slice(1);
- }
-
- if (color.length === 3) {
- const tempColor = color;
- color = '';
-
- color += tempColor[0] + tempColor[0];
- color += tempColor[1] + tempColor[1];
- color += tempColor[2] + tempColor[2];
- }
-
- var r = parseInt(color.substring(0, 2), 16);
- var g = parseInt(color.substring(2, 4), 16);
- var b = parseInt(color.substring(4, 6), 16);
-
- return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')';
-}
-
-export function getFullName(user) {
- if (user.first_name && user.last_name) {
- return user.first_name + ' ' + user.last_name;
- } else if (user.first_name) {
- return user.first_name;
- } else if (user.last_name) {
- return user.last_name;
- }
-
- return '';
-}
-
-export function getDisplayName(user) {
- if (user.nickname && user.nickname.trim().length > 0) {
- return user.nickname;
- }
- var fullName = getFullName(user);
-
- if (fullName) {
- return fullName;
- }
-
- return user.username;
-}
-
-export function displayUsername(userId) {
- const user = UserStore.getProfile(userId);
- const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false');
-
- let username = '';
- if (user) {
- if (nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME) {
- username = user.nickname || getFullName(user);
- } else if (nameFormat === Constants.Preferences.DISPLAY_PREFER_FULL_NAME) {
- username = getFullName(user);
- }
- if (!username.trim().length) {
- username = user.username;
- }
- }
-
- return username;
-}
-
-//IE10 does not set window.location.origin automatically so this must be called instead when using it
-export function getWindowLocationOrigin() {
- var windowLocationOrigin = window.location.origin;
- if (!windowLocationOrigin) {
- windowLocationOrigin = window.location.protocol + '//' + window.location.hostname;
- if (window.location.port) {
- windowLocationOrigin += ':' + window.location.port;
- }
- }
- return windowLocationOrigin;
-}
-
-// Converts a file size in bytes into a human-readable string of the form '123MB'.
-export function fileSizeToString(bytes) {
- // it's unlikely that we'll have files bigger than this
- if (bytes > 1024 * 1024 * 1024 * 1024) {
- return Math.floor(bytes / (1024 * 1024 * 1024 * 1024)) + 'TB';
- } else if (bytes > 1024 * 1024 * 1024) {
- return Math.floor(bytes / (1024 * 1024 * 1024)) + 'GB';
- } else if (bytes > 1024 * 1024) {
- return Math.floor(bytes / (1024 * 1024)) + 'MB';
- } else if (bytes > 1024) {
- return Math.floor(bytes / 1024) + 'KB';
- }
-
- return bytes + 'B';
-}
-
-// Converts a filename (like those attached to Post objects) to a url that can be used to retrieve attachments from the server.
-export function getFileUrl(filename, isDownload) {
- const downloadParam = isDownload ? '?download=1' : '';
- return getWindowLocationOrigin() + '/api/v1/files/get' + filename + downloadParam;
-}
-
-// Gets the name of a file (including extension) from a given url or file path.
-export function getFileName(path) {
- var split = path.split('/');
- return split[split.length - 1];
-}
-
-// Gets the websocket port to use. Configurable on the server.
-export function getWebsocketPort(protocol) {
- if ((/^wss:/).test(protocol)) { // wss://
- return ':' + global.window.mm_config.WebsocketSecurePort;
- }
- if ((/^ws:/).test(protocol)) {
- return ':' + global.window.mm_config.WebsocketPort;
- }
- return '';
-}
-
-// Generates a RFC-4122 version 4 compliant globally unique identifier.
-export function generateId() {
- // implementation taken from http://stackoverflow.com/a/2117523
- var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
-
- id = id.replace(/[xy]/g, function replaceRandom(c) {
- var r = Math.floor(Math.random() * 16);
-
- var v;
- if (c === 'x') {
- v = r;
- } else {
- v = r & 0x3 | 0x8;
- }
-
- return v.toString(16);
- });
-
- return id;
-}
-
-export function isBrowserFirefox() {
- return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
-}
-
-// Checks if browser is IE10 or IE11
-export function isBrowserIE() {
- if (window.navigator && window.navigator.userAgent) {
- var ua = window.navigator.userAgent;
-
- return ua.indexOf('Trident/7.0') > 0 || ua.indexOf('Trident/6.0') > 0;
- }
-
- return false;
-}
-
-export function isBrowserEdge() {
- return window.navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
-}
-
-export function getDirectChannelName(id, otherId) {
- let handle;
-
- if (otherId > id) {
- handle = id + '__' + otherId;
- } else {
- handle = otherId + '__' + id;
- }
-
- return handle;
-}
-
-// Used to get the id of the other user from a DM channel
-export function getUserIdFromChannelName(channel) {
- var ids = channel.name.split('__');
- var otherUserId = '';
- if (ids[0] === UserStore.getCurrentId()) {
- otherUserId = ids[1];
- } else {
- otherUserId = ids[0];
- }
-
- return otherUserId;
-}
-
-// Returns true if the given channel is a direct channel between the current user and the given one
-export function isDirectChannelForUser(otherUserId, channel) {
- return channel.type === Constants.DM_CHANNEL && getUserIdFromChannelName(channel) === otherUserId;
-}
-
-export function importSlack(file, success, error) {
- var formData = new FormData();
- formData.append('file', file, file.name);
- formData.append('filesize', file.size);
- formData.append('importFrom', 'slack');
-
- client.importSlack(formData, success, error);
-}
-
-export function getTeamURLFromAddressBar() {
- return window.location.href.split('/channels')[0];
-}
-
-export function getShortenedTeamURL() {
- const teamURL = getTeamURLFromAddressBar();
- if (teamURL.length > 35) {
- return teamURL.substring(0, 10) + '...' + teamURL.substring(teamURL.length - 12, teamURL.length) + '/';
- }
- return teamURL + '/';
-}
-
-export function windowWidth() {
- return $(window).width();
-}
-
-export function windowHeight() {
- return $(window).height();
-}
-
-export function openDirectChannelToUser(user, successCb, errorCb) {
- const channelName = this.getDirectChannelName(UserStore.getCurrentId(), user.id);
- let channel = ChannelStore.getByName(channelName);
-
- const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, 'true');
- AsyncClient.savePreferences([preference]);
-
- if (channel) {
- if ($.isFunction(successCb)) {
- successCb(channel, true);
- }
- } else {
- channel = {
- name: channelName,
- last_post_at: 0,
- total_msg_count: 0,
- type: 'D',
- display_name: user.username,
- teammate_id: user.id,
- status: UserStore.getStatus(user.id)
- };
-
- Client.createDirectChannel(
- channel,
- user.id,
- (data) => {
- AsyncClient.getChannel(data.id);
- if ($.isFunction(successCb)) {
- successCb(data, false);
- }
- },
- () => {
- window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName;
- if ($.isFunction(errorCb)) {
- errorCb();
- }
- }
- );
- }
-}
-
-// Use when sorting multiple channels or teams by their `display_name` field
-export function sortByDisplayName(a, b) {
- let aDisplayName = '';
- let bDisplayName = '';
-
- if (a && a.display_name) {
- aDisplayName = a.display_name.toLowerCase();
- }
- if (b && b.display_name) {
- bDisplayName = b.display_name.toLowerCase();
- }
-
- if (aDisplayName < bDisplayName) {
- return -1;
- }
- if (aDisplayName > bDisplayName) {
- return 1;
- }
- return 0;
-}
-
-export function getChannelTerm(channelType) {
- let channelTerm = 'Channel';
- if (channelType === Constants.PRIVATE_CHANNEL) {
- channelTerm = 'Group';
- }
-
- return channelTerm;
-}
-
-export function getPostTerm(post) {
- let postTerm = 'Post';
- if (post.root_id) {
- postTerm = 'Comment';
- }
-
- return postTerm;
-}
-
-export function isFeatureEnabled(feature) {
- return PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label);
-}
-
-export function isSystemMessage(post) {
- return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0);
-}
-
-export function fillArray(value, length) {
- const arr = [];
-
- for (let i = 0; i < length; i++) {
- arr.push(value);
- }
-
- return arr;
-}
-
-// Checks if a data transfer contains files not text, folders, etc..
-// Slightly modified from http://stackoverflow.com/questions/6848043/how-do-i-detect-a-file-is-being-dragged-rather-than-a-draggable-element-on-my-pa
-export function isFileTransfer(files) {
- if (isBrowserIE()) {
- return files.types != null && files.types.contains('Files');
- }
-
- return files.types != null && (files.types.indexOf ? files.types.indexOf('Files') !== -1 : files.types.contains('application/x-moz-file'));
-}
-
-export function clearFileInput(elm) {
- // clear file input for all modern browsers
- try {
- elm.value = '';
- if (elm.value) {
- elm.type = 'text';
- elm.type = 'file';
- }
- } catch (e) {
- // Do nothing
- }
-}
-
-export function languages() {
- return (
- [
- {
- value: 'en',
- name: 'English'
- },
- {
- value: 'es',
- name: 'EspaƱol (Beta)'
- },
- {
- value: 'pt',
- name: 'Portugues (Beta)'
- }
- ]
- );
-}
-
-export function isPostEphemeral(post) {
- return post.type === Constants.POST_TYPE_EPHEMERAL || post.state === Constants.POST_DELETED;
-}
-
-export function getRootId(post) {
- return post.root_id === '' ? post.id : post.root_id;
-}
-
-export function localizeMessage(id, defaultMessage) {
- const translations = LocalizationStore.getTranslations();
- if (translations) {
- const value = translations[id];
- if (value) {
- return value;
- }
- }
-
- if (defaultMessage) {
- return defaultMessage;
- }
-
- return id;
-}