Fuel Welcome page
Closes-Bug: #1377834 Related-Bug: #1377846 Change-Id: I9cbd77166ea49a0e553d89dade887236e6753898
This commit is contained in:
parent
339834b01c
commit
d1f3c52411
@ -2,21 +2,54 @@
|
||||
- pk: 1
|
||||
model: "nailgun.master_node_settings"
|
||||
fields:
|
||||
master_node_uid: "62410d05-ecbd-4912-83fe-5db51ebb273e"
|
||||
settings:
|
||||
send_anonymous_statistic:
|
||||
type: "checkbox"
|
||||
value: true
|
||||
send_user_info:
|
||||
type: "checkbox"
|
||||
value: true
|
||||
user_info:
|
||||
name:
|
||||
type: "text"
|
||||
value: "Test User"
|
||||
email:
|
||||
type: "text"
|
||||
value: "test@email.com"
|
||||
company:
|
||||
type: "text"
|
||||
value: "Test Company"
|
||||
master_node_uid: "62410d05-ecbd-4912-83fe-5db51ebb273e"
|
||||
settings:
|
||||
statistics:
|
||||
send_anonymous_statistic:
|
||||
type: "checkbox"
|
||||
value: true
|
||||
label: "statistics.setting_labels.send_anonymous_statistic"
|
||||
weight: 10
|
||||
send_user_info:
|
||||
type: "checkbox"
|
||||
value: true
|
||||
label: "statistics.setting_labels.send_user_info"
|
||||
weight: 20
|
||||
restrictions:
|
||||
- "fuel_settings:statistics.send_anonymous_statistic.value == false"
|
||||
- condition: &commumity_iso "not ('mirantis' in version:feature_groups)"
|
||||
action: "hide"
|
||||
name:
|
||||
type: "text"
|
||||
value: ""
|
||||
label: "statistics.setting_labels.name"
|
||||
weight: 30
|
||||
regex:
|
||||
source: &non_empty_string '\S'
|
||||
error: "statistics.errors.name"
|
||||
restrictions: &user_info_restrictions
|
||||
- "fuel_settings:statistics.send_anonymous_statistic.value == false"
|
||||
- "fuel_settings:statistics.send_user_info.value == false"
|
||||
- condition: *commumity_iso
|
||||
action: "hide"
|
||||
email:
|
||||
type: "text"
|
||||
value: ""
|
||||
label: "statistics.setting_labels.email"
|
||||
weight: 40
|
||||
regex:
|
||||
source: *non_empty_string
|
||||
error: "statistics.errors.email"
|
||||
restrictions: *user_info_restrictions
|
||||
company:
|
||||
type: "text"
|
||||
value: ""
|
||||
label: "statistics.setting_labels.company"
|
||||
weight: 50
|
||||
regex:
|
||||
source: *non_empty_string
|
||||
error: "statistics.errors.company"
|
||||
restrictions: *user_info_restrictions
|
||||
user_choice_saved:
|
||||
type: "hidden"
|
||||
value: false
|
||||
|
@ -35,21 +35,7 @@ class MasterNodeSettings(NailgunObject):
|
||||
"description": "Serialized ActionLog object",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"send_anonymous_statistic": {"type": "object"},
|
||||
"send_user_info": {"type": "object"},
|
||||
"user_info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "object"},
|
||||
"company": {"type": "object"},
|
||||
"email": {"type": "object"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"settings": {"type": "object"}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,26 +27,95 @@ class TestMasterNodeSettingsHandler(BaseIntegrationTest):
|
||||
# fixture file which is located in fixtures directory for nailgun
|
||||
expected = {
|
||||
"settings": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": True
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True
|
||||
},
|
||||
"user_info": {
|
||||
"statistics": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": True,
|
||||
"label": "statistics.setting_labels."
|
||||
"send_anonymous_statistic",
|
||||
"weight": 10
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True,
|
||||
"label": "statistics.setting_labels.send_user_info",
|
||||
"weight": 20,
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"value": "Test User"
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "Test Company"
|
||||
"value": "",
|
||||
"label": "statistics.setting_labels.name",
|
||||
"weight": 30,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.name"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": "text",
|
||||
"value": "test@email.com"
|
||||
"value": "",
|
||||
"label": "statistics.setting_labels.email",
|
||||
"weight": 40,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.email"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"label": "statistics.setting_labels.company",
|
||||
"weight": 50,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.company"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_choice_saved": {
|
||||
"type": "hidden",
|
||||
"value": False
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,26 +130,95 @@ class TestMasterNodeSettingsHandler(BaseIntegrationTest):
|
||||
def test_put_controller(self):
|
||||
data = {
|
||||
"settings": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": False
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True
|
||||
},
|
||||
"user_info": {
|
||||
"statistics": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": False,
|
||||
"label": "statistics.setting_labels."
|
||||
"send_anonymous_statistic",
|
||||
"weight": 10
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True,
|
||||
"label": "statistics.setting_labels.send_user_info",
|
||||
"weight": 20,
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"value": "Some User"
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "Some Company"
|
||||
"value": "Some User",
|
||||
"label": "statistics.setting_labels.name",
|
||||
"weight": 30,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.name"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": "text",
|
||||
"value": "user@email.com"
|
||||
"value": "user@email.com",
|
||||
"label": "statistics.setting_labels.email",
|
||||
"weight": 40,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.email"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "Some Company",
|
||||
"label": "statistics.setting_labels.company",
|
||||
"weight": 50,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.company"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_choice_saved": {
|
||||
"type": "hidden",
|
||||
"value": True
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +240,7 @@ class TestMasterNodeSettingsHandler(BaseIntegrationTest):
|
||||
def test_patch_controller(self):
|
||||
data = {
|
||||
"settings": {
|
||||
"user_info": {
|
||||
"statistics": {
|
||||
"company": {
|
||||
"value": "Other Company"
|
||||
}
|
||||
@ -112,26 +250,95 @@ class TestMasterNodeSettingsHandler(BaseIntegrationTest):
|
||||
|
||||
expected = {
|
||||
"settings": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": True
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True
|
||||
},
|
||||
"user_info": {
|
||||
"statistics": {
|
||||
"send_anonymous_statistic": {
|
||||
"type": "checkbox",
|
||||
"value": True,
|
||||
"label": "statistics.setting_labels."
|
||||
"send_anonymous_statistic",
|
||||
"weight": 10
|
||||
},
|
||||
"send_user_info": {
|
||||
"type": "checkbox",
|
||||
"value": True,
|
||||
"label": "statistics.setting_labels.send_user_info",
|
||||
"weight": 20,
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"value": "Test User"
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "Other Company"
|
||||
"value": "",
|
||||
"label": "statistics.setting_labels.name",
|
||||
"weight": 30,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.name"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": "text",
|
||||
"value": "test@email.com"
|
||||
"value": "",
|
||||
"label": "statistics.setting_labels.email",
|
||||
"weight": 40,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.email"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"company": {
|
||||
"type": "text",
|
||||
"value": "Other Company",
|
||||
"label": "statistics.setting_labels.company",
|
||||
"weight": 50,
|
||||
"regex": {
|
||||
"source": "\S",
|
||||
"error": "statistics.errors.company"
|
||||
},
|
||||
"restrictions": [
|
||||
"fuel_settings:statistics."
|
||||
"send_anonymous_statistic.value == false",
|
||||
"fuel_settings:statistics."
|
||||
"send_user_info.value == false",
|
||||
{
|
||||
"condition":
|
||||
"not ('mirantis' in version:feature_groups)",
|
||||
"action": "hide"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_choice_saved": {
|
||||
"type": "hidden",
|
||||
"value": False
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,9 +360,7 @@ class TestMasterNodeSettingsHandler(BaseIntegrationTest):
|
||||
|
||||
def test_validation_error(self):
|
||||
data = {
|
||||
"settings": {
|
||||
"send_user_info": "I'm not an object, bro:)"
|
||||
}
|
||||
"settings": "I'm not an object, bro:)"
|
||||
}
|
||||
|
||||
resp = self.app.put(
|
||||
|
@ -3593,3 +3593,101 @@ i.btn-cluster-details {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Fuel Welcome page **/
|
||||
|
||||
.welcome-page {
|
||||
.width-height-mixin;
|
||||
.box-sizing-mixin;
|
||||
padding: 40px;
|
||||
color: #4d545b;
|
||||
> div {
|
||||
width: 800px;
|
||||
background-color: @fuel-white-color;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
padding-bottom: 10px;
|
||||
.box-sizing-mixin;
|
||||
.border-radius-mixin(10px);
|
||||
.center { text-align: center; }
|
||||
h2 {
|
||||
line-height: 40px;
|
||||
.font-layout-mixin(36px, #4d545b);
|
||||
.indent-mixin();
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.welcome-checkbox-box {
|
||||
text-align: center;
|
||||
margin-bottom: 16px;
|
||||
padding-top: 4px;
|
||||
div {
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* overrides for reusable inputs */
|
||||
.parameter-name, .custom-tumbler {
|
||||
float: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
.label-wrapper { .position(relative, @top: -7px, @left: 3px); }
|
||||
}
|
||||
.welcome-form-box {
|
||||
margin-bottom: 15px;
|
||||
.welcome-form-item {
|
||||
display: inline-block;
|
||||
margin-left: 175px;
|
||||
font-weight: normal;
|
||||
.input-label { width: 80px; }
|
||||
}
|
||||
}
|
||||
.welcome-form-error {
|
||||
text-align: center;
|
||||
color: @fuel-deep-red;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.welcome-button-box {
|
||||
text-align: center;
|
||||
button { .width-height-mixin(230px, 44px); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-settings {
|
||||
margin-bottom: 10px;
|
||||
/* overrides for reusable inputs */
|
||||
.checkbox-or-radio .parameter-name { float: none; }
|
||||
.parameter-name {
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
.label-wrapper { .position(relative, @top: 7px); }
|
||||
padding: 4px;
|
||||
}
|
||||
.parameter-description { margin-top: 10px; }
|
||||
.input-label { width: 80px; }
|
||||
}
|
||||
|
||||
.statistics-text-box {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
.font-layout-mixin(14px, #4d545b, lighter);
|
||||
a { cursor: pointer; }
|
||||
p {
|
||||
text-align: justify;
|
||||
margin: 0px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.statistics-disclaimer-box {
|
||||
background-color: #ecf0f2;
|
||||
border: 1px solid #c9cdce;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
line-height: 16px;
|
||||
.width-height-mixin(100%, 300px);
|
||||
.font-layout-mixin(14px, #5b636b, lighter);
|
||||
.box-sizing-mixin;
|
||||
p { margin-bottom: 10px; }
|
||||
ul { margin-bottom: 20px; }
|
||||
}
|
||||
|
@ -79,6 +79,70 @@
|
||||
"log_in": "Log in",
|
||||
"title": "Log In"
|
||||
},
|
||||
"statistics": {
|
||||
"help_to_improve": "Help us to improve your experience by sending Mirantis information about the settings, features, and deployment actions when you use Mirantis OpenStack.",
|
||||
"statistics_includes": "Usage statistics include information such as settings, button/menu clicks, hardware configuration, and version information. The usage statistics do not include information such as passwords, ip addresses, or node names. For a complete list of statistics that we gather, ",
|
||||
"click_here": "click here",
|
||||
"privacy_policy": "Mirantis’ privacy policy (\"Privacy Policy\") describes our practices regarding the information we collect on the Mirantis web sites and through the use of our products and services, and how it is used and shared with third parties. You can read the policy ",
|
||||
"privacy_policy_link": "here",
|
||||
"statistics_includes_full": "Usage statistics include the following. Information such as passwords, ip addresses, and node names are not included.",
|
||||
"actions_title": "Actions:",
|
||||
"actions": {
|
||||
"operation_type": "Operation type (adding cluster, adding node, deployment, removing node, e.t.c.)",
|
||||
"operation_time": "Operation start and finish time",
|
||||
"actual_time": "Actual time that is took to complete the operation",
|
||||
"network_verification": "Network verification - whether it was used, and what was the result",
|
||||
"ostf_results": "Is OSTF used, and tests results"
|
||||
},
|
||||
"settings_title": "Environment Settings:",
|
||||
"settings": {
|
||||
"envronments_amount": "Number of environments",
|
||||
"nistribution": "Distribution / OS",
|
||||
"network_type": "Network type",
|
||||
"kernel_parameters": "Kernel parameters",
|
||||
"admin_network_parameters": "Admin network parameters",
|
||||
"pxe_parameters": "PXE parameters",
|
||||
"dns_parameters": "DNS parameters",
|
||||
"storage_options": "Storage options",
|
||||
"related_projects": "Related Projects",
|
||||
"modified_settings": "Settings modified on Settings tab",
|
||||
"networking_configuration": "Networking configuration"
|
||||
},
|
||||
"node_settings_title": "Node Settings:",
|
||||
"node_settings": {
|
||||
"deployed_nodes_amount": "Number of nodes deployed",
|
||||
"deployed_roles": "Roles deployed to each node",
|
||||
"disk_layout": "Disk layout",
|
||||
"interfaces_configuration": "Interfaces configuration"
|
||||
},
|
||||
"system_info_title": "System Info:",
|
||||
"system_info": {
|
||||
"hypervisor": "Hypervisor",
|
||||
"hardware_info": "Hardware info",
|
||||
"fuel_version": "Fuel version info",
|
||||
"openstack_version": "OpenStack version info"
|
||||
},
|
||||
"setting_labels": {
|
||||
"send_anonymous_statistic": "Send usage statistics to Mirantis",
|
||||
"send_user_info": "Identify my error reports so that Mirantis Support can assist me",
|
||||
"name": "Name",
|
||||
"email": "Email",
|
||||
"company": "Company"
|
||||
},
|
||||
"errors": {
|
||||
"name": "Please fill your name",
|
||||
"email": "Please fill email address",
|
||||
"company": "Please fill company name"
|
||||
}
|
||||
},
|
||||
"welcome_page": {
|
||||
"title": "Welcome to Mirantis OpenStack",
|
||||
"support": "Our support team can optionally use your error reports to assist you.",
|
||||
"provide_contacts": "Please provide your contact information below.",
|
||||
"start_fuel": "Start Using Fuel",
|
||||
"change_settings": "You can change your settings at any time by updating your user profile information.",
|
||||
"thanks": "Thanks for helping to improve Mirantis OpenStack!"
|
||||
},
|
||||
"navbar": {
|
||||
"environments": "Environments",
|
||||
"releases": "Releases",
|
||||
@ -119,7 +183,9 @@
|
||||
"diagnostic_snapshot": "Diagnostic Snapshot",
|
||||
"capacity_audit": "Capacity Audit",
|
||||
"capacity_audit_text": "To better manage your resources, you can run this report to find out what OpenStack roles have been deployed across all of your environments.",
|
||||
"view_capacity_audit": "View Capacity Audit"
|
||||
"view_capacity_audit": "View Capacity Audit",
|
||||
"send_statistics_title": "Send Statistics About Usage",
|
||||
"save_changes": "Save Changes"
|
||||
},
|
||||
"release_page": {
|
||||
"title": "Releases",
|
||||
|
@ -25,6 +25,7 @@ define(
|
||||
'keystone_client',
|
||||
'views/common',
|
||||
'jsx!views/login_page',
|
||||
'jsx!views/welcome_page',
|
||||
'views/cluster_page',
|
||||
'views/cluster_page_tabs/nodes_tab',
|
||||
'jsx!views/clusters_page',
|
||||
@ -33,13 +34,14 @@ define(
|
||||
'jsx!views/support_page',
|
||||
'jsx!views/capacity_page'
|
||||
],
|
||||
function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneClient, commonViews, LoginPage, ClusterPage, NodesTab, ClustersPage, ReleasesPage, NotificationsPage, SupportPage, CapacityPage) {
|
||||
function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneClient, commonViews, LoginPage, WelcomePage, ClusterPage, NodesTab, ClustersPage, ReleasesPage, NotificationsPage, SupportPage, CapacityPage) {
|
||||
'use strict';
|
||||
|
||||
var AppRouter = Backbone.Router.extend({
|
||||
routes: {
|
||||
login: 'login',
|
||||
logout: 'logout',
|
||||
welcome: 'welcome',
|
||||
clusters: 'listClusters',
|
||||
'cluster/:id': 'showCluster',
|
||||
'cluster/:id/:tab(/:opt1)(/:opt2)': 'showClusterTab',
|
||||
@ -72,8 +74,10 @@ function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneC
|
||||
cacheTokenFor: 10 * 60 * 1000,
|
||||
tenant: 'admin'
|
||||
});
|
||||
var version = this.version = new models.FuelVersion();
|
||||
|
||||
this.settings = new models.FuelSettings();
|
||||
|
||||
var version = this.version = new models.FuelVersion();
|
||||
version.fetch().then(_.bind(function() {
|
||||
this.user = new models.User({authenticated: !version.get('auth_required')});
|
||||
|
||||
@ -179,7 +183,7 @@ function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneC
|
||||
this.page = utils.universalMount(new NewPage(options), this.content);
|
||||
this.navbar.setActive(_.result(this.page, 'navbarActiveElement'));
|
||||
this.updateTitle();
|
||||
this.toggleElements(NewPage != LoginPage);
|
||||
this.toggleElements(!this.page.hiddenLayout);
|
||||
},
|
||||
// routes
|
||||
login: function() {
|
||||
@ -198,6 +202,9 @@ function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneC
|
||||
app.navigate('#login', {trigger: true, replace: true});
|
||||
});
|
||||
},
|
||||
welcome: function() {
|
||||
this.setPage(WelcomePage, {settings: this.settings});
|
||||
},
|
||||
showCluster: function(id) {
|
||||
this.navigate('#cluster/' + id + '/nodes', {trigger: true, replace: true});
|
||||
},
|
||||
@ -286,7 +293,7 @@ function(React, utils, layoutComponents, Coccyx, coccyxMixins, models, KeystoneC
|
||||
showSupportPage: function() {
|
||||
var tasks = new models.Tasks();
|
||||
tasks.fetch().done(_.bind(function() {
|
||||
this.setPage(SupportPage, {tasks: tasks});
|
||||
this.setPage(SupportPage, {tasks: tasks, settings: this.settings});
|
||||
}, this));
|
||||
},
|
||||
showCapacityPage: function() {
|
||||
|
@ -352,6 +352,70 @@ define(['utils', 'deepModel'], function(utils) {
|
||||
});
|
||||
_.extend(models.Settings.prototype, cacheMixin);
|
||||
|
||||
models.FuelSettings = Backbone.DeepModel.extend({
|
||||
constructorName: 'FuelSettings',
|
||||
url: '/api/settings',
|
||||
cacheFor: 60 * 1000,
|
||||
isNew: function() {
|
||||
return false;
|
||||
},
|
||||
parse: function(response) {
|
||||
return response.settings;
|
||||
},
|
||||
toJSON: function(options) {
|
||||
return {settings: this.constructor.__super__.toJSON.call(this, options)};
|
||||
},
|
||||
expandRestrictions: function(restrictions, path) {
|
||||
if (_.isUndefined(this.expandedRestrictions)) this.expandedRestrictions = {};
|
||||
if (restrictions && restrictions.length) {
|
||||
var result = _.map(restrictions, utils.expandRestriction, this);
|
||||
if (path) {
|
||||
this.expandedRestrictions[path] = result;
|
||||
} else {
|
||||
this.expandedRestrictions = result;
|
||||
}
|
||||
}
|
||||
},
|
||||
processRestrictions: function() {
|
||||
_.each(this.attributes, function(group, groupName) {
|
||||
if (group.metadata) this.expandRestrictions(group.metadata.restrictions, groupName + '.metadata');
|
||||
_.each(group, function(setting, settingName) {
|
||||
this.expandRestrictions(setting.restrictions, this.makePath(groupName, settingName));
|
||||
_.each(setting.values, function(value) {
|
||||
this.expandRestrictions(value.restrictions, this.makePath(groupName, settingName, value.data));
|
||||
}, this);
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
checkRestrictions: function(models, action, path) {
|
||||
var restrictions = path ? this.expandedRestrictions[path] : this.expandedRestrictions;
|
||||
if (action) restrictions = _.where(restrictions, {action: action});
|
||||
return _.any(restrictions, function(restriction) {
|
||||
return utils.evaluateExpression(restriction.condition, models).value;
|
||||
});
|
||||
},
|
||||
validate: function(attrs, options) {
|
||||
var errors = {},
|
||||
models = options ? options.models : {},
|
||||
checkRestrictions = _.bind(function(path) {
|
||||
return this.checkRestrictions(models, null, path);
|
||||
}, this);
|
||||
_.each(attrs, function(group, groupName) {
|
||||
if (checkRestrictions(this.makePath(groupName, 'metadata'))) return;
|
||||
_.each(group, function(setting, settingName) {
|
||||
var path = this.makePath(groupName, settingName);
|
||||
if (!setting.regex || !setting.regex.source || checkRestrictions(path)) return;
|
||||
if (!setting.value.match(new RegExp(setting.regex.source))) errors[path] = setting.regex.error;
|
||||
}, this);
|
||||
}, this);
|
||||
return _.isEmpty(errors) ? null : errors;
|
||||
},
|
||||
makePath: function() {
|
||||
return _.toArray(arguments).join('.');
|
||||
}
|
||||
});
|
||||
_.extend(models.FuelSettings.prototype, cacheMixin);
|
||||
|
||||
models.Disk = Backbone.Model.extend({
|
||||
constructorName: 'Disk',
|
||||
urlRoot: '/api/nodes/',
|
||||
|
@ -51,11 +51,13 @@ define(['jquery', 'underscore', 'react'], function($, _, React) {
|
||||
label: React.PropTypes.renderable,
|
||||
description: React.PropTypes.renderable,
|
||||
disabled: React.PropTypes.bool,
|
||||
inputClassName: React.PropTypes.renderable,
|
||||
wrapperClassName: React.PropTypes.renderable,
|
||||
labelClassName: React.PropTypes.renderable,
|
||||
descriptionClassName: React.PropTypes.renderable,
|
||||
tooltipText: React.PropTypes.renderable,
|
||||
toggleable: React.PropTypes.bool
|
||||
toggleable: React.PropTypes.bool,
|
||||
onChange: React.PropTypes.func
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {visible: false};
|
||||
@ -68,25 +70,28 @@ define(['jquery', 'underscore', 'react'], function($, _, React) {
|
||||
return this.props.type == 'radio' || this.props.type == 'checkbox';
|
||||
},
|
||||
onChange: function() {
|
||||
var input = this.refs.input.getDOMNode();
|
||||
return this.props.onChange(this.props.name, this.props.type == 'checkbox' ? input.checked : input.value);
|
||||
if (this.props.onChange) {
|
||||
var input = this.refs.input.getDOMNode();
|
||||
return this.props.onChange(this.props.name, this.props.type == 'checkbox' ? input.checked : input.value);
|
||||
}
|
||||
},
|
||||
renderInput: function() {
|
||||
var input = null,
|
||||
className = 'parameter-input';
|
||||
classes = {'parameter-input': true};
|
||||
classes[this.props.inputClassName] = this.props.inputClassName;
|
||||
switch (this.props.type) {
|
||||
case 'select':
|
||||
input = (<select ref='input' key='input' className={className} onChange={this.onChange}>{this.props.children}</select>);
|
||||
input = (<select ref='input' key='input' className={cx(classes)} onChange={this.onChange}>{this.props.children}</select>);
|
||||
break;
|
||||
case 'textarea':
|
||||
input = <textarea ref='input' key='input' className={className} onChange={this.onChange} />;
|
||||
input = <textarea ref='input' key='input' className={cx(classes)} onChange={this.onChange} />;
|
||||
break;
|
||||
case 'password':
|
||||
var type = (this.props.toggleable && this.state.visible) ? 'text' : 'password';
|
||||
input = <input ref='input' key='input' className={className} type={type} onChange={this.onChange} />;
|
||||
input = <input ref='input' key='input' className={cx(classes)} type={type} onChange={this.onChange} />;
|
||||
break;
|
||||
default:
|
||||
input = <input ref='input' key='input' className={className} onChange={this.onChange} value={this.props.value} />;
|
||||
input = <input ref='input' key='input' className={cx(classes)} onChange={this.onChange} value={this.props.value} />;
|
||||
}
|
||||
return this.isCheckboxOrRadio() ? (
|
||||
<div key='input-wrapper' className='custom-tumbler'>
|
||||
@ -137,11 +142,13 @@ define(['jquery', 'underscore', 'react'], function($, _, React) {
|
||||
) : null;
|
||||
},
|
||||
renderWrapper: function(children) {
|
||||
var classes = {
|
||||
'parameter-box': true,
|
||||
clearfix: !this.isCheckboxOrRadio(),
|
||||
'has-error': !_.isUndefined(this.props.error) && !_.isNull(this.props.error)
|
||||
};
|
||||
var isCheckboxOrRadio = this.isCheckboxOrRadio(),
|
||||
classes = {
|
||||
'parameter-box': true,
|
||||
'checkbox-or-radio': isCheckboxOrRadio,
|
||||
clearfix: !isCheckboxOrRadio,
|
||||
'has-error': !_.isUndefined(this.props.error) && !_.isNull(this.props.error)
|
||||
};
|
||||
classes[this.props.wrapperClassName] = this.props.wrapperClassName;
|
||||
return (<div className={cx(classes)}>{children}</div>);
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ function($, React, controls) {
|
||||
|
||||
LoginPage = React.createClass({
|
||||
breadcrumbsPath: [],
|
||||
hiddenLayout: true,
|
||||
propTypes: {
|
||||
app: React.PropTypes.object
|
||||
},
|
||||
@ -66,15 +67,25 @@ function($, React, controls) {
|
||||
username: username,
|
||||
token: keystoneClient.token
|
||||
});
|
||||
this.loginRedirect();
|
||||
this.goToWelcomePage();
|
||||
}, this));
|
||||
},
|
||||
loginRedirect: function() {
|
||||
app.navigate('#', {trigger: true, replace: true});
|
||||
},
|
||||
goToWelcomePage: function() {
|
||||
app.settings.fetch({cache: true})
|
||||
.always(_.bind(function() {
|
||||
if (!app.settings.get('statistics').user_choice_saved.value) {
|
||||
app.navigate('#welcome', {trigger: true, replace: true});
|
||||
} else {
|
||||
this.loginRedirect();
|
||||
}
|
||||
}, this));
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if (app.user.get('authenticated')) {
|
||||
this.loginRedirect();
|
||||
this.goToWelcomePage();
|
||||
} else {
|
||||
$(this.refs.username.getDOMNode()).focus();
|
||||
}
|
||||
|
181
nailgun/static/js/views/statistics_mixin.jsx
Normal file
181
nailgun/static/js/views/statistics_mixin.jsx
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2014 Mirantis, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'react',
|
||||
'utils',
|
||||
'models',
|
||||
'jsx!views/controls'
|
||||
], function($, _, React, utils, models, controls) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
propTypes: {
|
||||
settings: React.PropTypes.object.isRequired
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
loading: true,
|
||||
actionInProgress: false,
|
||||
showItems: false
|
||||
};
|
||||
},
|
||||
saveSettings: function(e) {
|
||||
e.preventDefault();
|
||||
this.props.settings.isValid({models: this.configModels});
|
||||
if (this.props.settings.validationError) {
|
||||
this.forceUpdate();
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
this.setState({actionInProgress: true});
|
||||
return this.props.settings.save(null, {patch: true, wait: true, validate: false})
|
||||
.done(this.updateInitialSettings)
|
||||
.fail(function() {
|
||||
this.setState({actionInProgress: false});
|
||||
utils.showErrorDialog();
|
||||
});
|
||||
},
|
||||
onSettingChange: function(name, value) {
|
||||
this.setState({actionInProgress: false});
|
||||
this.props.settings.set(this.props.settings.makePath('statistics', name, 'value'), value);
|
||||
this.props.settings.isValid({models: this.configModels});
|
||||
},
|
||||
checkRestrictions: function(name, action) {
|
||||
action = action || 'disable';
|
||||
return this.props.settings.checkRestrictions(this.configModels, action, this.props.settings.makePath('statistics', name));
|
||||
},
|
||||
toggleItemsList: function(e) {
|
||||
e.preventDefault();
|
||||
this.setState({showItems: !this.state.showItems});
|
||||
},
|
||||
hasChanges: function() {
|
||||
return !_.isEqual(this.props.settings.toJSON().settings, this.initialSettings);
|
||||
},
|
||||
updateInitialSettings: function() {
|
||||
this.initialSettings = _.cloneDeep(this.props.settings.attributes);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
this.props.settings.set(_.cloneDeep(this.initialSettings), {silent: true});
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.props.settings.fetch({cache: true})
|
||||
.always(_.bind(function() {
|
||||
this.props.settings.processRestrictions();
|
||||
this.configModels = {
|
||||
fuel_settings: this.props.settings,
|
||||
version: app.version,
|
||||
default: this.props.settings
|
||||
};
|
||||
this.updateInitialSettings();
|
||||
this.setState({loading: false});
|
||||
}, this));
|
||||
},
|
||||
getError: function(name) {
|
||||
return this.props.settings.validationError && this.props.settings.validationError[this.props.settings.makePath('statistics', name)];
|
||||
},
|
||||
renderInput: function(settingName, labelClassName, wrapperClassName, hideErrors) {
|
||||
if (this.checkRestrictions('metadata', 'hide') || this.checkRestrictions(settingName, 'hide')) return null;
|
||||
var setting = this.props.settings.get(this.props.settings.makePath('statistics', settingName)),
|
||||
error = this.getError(settingName),
|
||||
disabled = this.checkRestrictions('metadata') || this.checkRestrictions(settingName);
|
||||
return <controls.Input
|
||||
key={settingName}
|
||||
type={setting.type}
|
||||
name={settingName}
|
||||
label={setting.label && $.t(setting.label)}
|
||||
checked={!disabled && setting.value}
|
||||
value={setting.value}
|
||||
disabled={disabled}
|
||||
inputClassName={setting.type == 'text' && 'input-xlarge'}
|
||||
labelClassName={labelClassName}
|
||||
wrapperClassName={wrapperClassName}
|
||||
onChange={this.onSettingChange}
|
||||
error={error ? hideErrors ? '' : $.t(error) : null}
|
||||
/>;
|
||||
},
|
||||
renderList: function(list, key) {
|
||||
return (
|
||||
<p key={key}>
|
||||
{$.t('statistics.' + key + '_title')}
|
||||
<ul>
|
||||
{_.map(list, function(item) {
|
||||
return <li key={item}>{$.t('statistics.' + key + '.' + item)}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</p>
|
||||
);
|
||||
},
|
||||
renderIntro: function() {
|
||||
var ns = 'statistics.',
|
||||
lists = {
|
||||
actions: [
|
||||
'operation_type',
|
||||
'operation_time',
|
||||
'actual_time',
|
||||
'network_verification',
|
||||
'ostf_results'
|
||||
],
|
||||
settings: [
|
||||
'envronments_amount',
|
||||
'nistribution',
|
||||
'network_type',
|
||||
'kernel_parameters',
|
||||
'admin_network_parameters',
|
||||
'pxe_parameters',
|
||||
'dns_parameters',
|
||||
'storage_options',
|
||||
'related_projects',
|
||||
'modified_settings',
|
||||
'networking_configuration'
|
||||
],
|
||||
node_settings: [
|
||||
'deployed_nodes_amount',
|
||||
'deployed_roles',
|
||||
'disk_layout',
|
||||
'interfaces_configuration'
|
||||
],
|
||||
system_info: [
|
||||
'hypervisor',
|
||||
'hardware_info',
|
||||
'fuel_version',
|
||||
'openstack_version'
|
||||
]
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className='statistics-text-box'>
|
||||
<p>{$.t(ns + 'help_to_improve')}</p>
|
||||
<p>
|
||||
{$.t(ns + 'statistics_includes')}
|
||||
<a onClick={this.toggleItemsList}>{$.t(ns + 'click_here')}</a>.
|
||||
</p>
|
||||
<p>
|
||||
{$.t(ns + 'privacy_policy')}
|
||||
<a href='https://www.mirantis.com/company/privacy-policy/' target='_blank'>{$.t(ns + 'privacy_policy_link')}</a>.
|
||||
</p>
|
||||
</div>
|
||||
{this.state.showItems &&
|
||||
<div className='statistics-disclaimer-box'>
|
||||
<p>{$.t(ns + 'statistics_includes_full')}</p>
|
||||
{_.map(lists, this.renderList)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
@ -17,9 +17,10 @@ define(
|
||||
[
|
||||
'react',
|
||||
'jsx!component_mixins',
|
||||
'models'
|
||||
'models',
|
||||
'jsx!views/statistics_mixin'
|
||||
],
|
||||
function(React, componentMixins, models) {
|
||||
function(React, componentMixins, models, statisticsMixin) {
|
||||
'use strict';
|
||||
|
||||
var SupportPage = React.createClass({
|
||||
@ -32,7 +33,8 @@ function(React, componentMixins, models) {
|
||||
render: function() {
|
||||
var elements = [
|
||||
<DiagnosticSnapshot key='DiagnosticSnapshot' tasks={this.props.tasks} task={this.props.tasks.findTask({name: 'dump'})} />,
|
||||
<CapacityAudit key='CapacityAudit' />
|
||||
<CapacityAudit key='CapacityAudit' />,
|
||||
<StatisticsSettings key='StatisticsSettings' settings={this.props.settings} />
|
||||
];
|
||||
if (_.contains(app.version.get('feature_groups'), 'mirantis')) {
|
||||
elements.unshift(
|
||||
@ -171,5 +173,33 @@ function(React, componentMixins, models) {
|
||||
}
|
||||
});
|
||||
|
||||
var StatisticsSettings = React.createClass({
|
||||
mixins: [
|
||||
statisticsMixin,
|
||||
React.BackboneMixin('settings')
|
||||
],
|
||||
render: function() {
|
||||
if (this.state.loading) return null;
|
||||
var settings = this.props.settings.get('statistics'),
|
||||
sortedSettings = _.chain(_.keys(settings))
|
||||
.without('metadata')
|
||||
.sortBy(function(settingName) {return settings[settingName].weight;}, this)
|
||||
.value();
|
||||
return (
|
||||
<SupportPageElement title={$.t('support_page.send_statistics_title')}>
|
||||
{this.renderIntro()}
|
||||
<div className='statistics-settings'>
|
||||
{_.map(sortedSettings, this.renderInput, this)}
|
||||
</div>
|
||||
<p>
|
||||
<a className='btn' disabled={this.state.actionInProgress || !this.hasChanges()} onClick={this.saveSettings}>
|
||||
{$.t('support_page.save_changes')}
|
||||
</a>
|
||||
</p>
|
||||
</SupportPageElement>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return SupportPage;
|
||||
});
|
||||
|
90
nailgun/static/js/views/welcome_page.jsx
Normal file
90
nailgun/static/js/views/welcome_page.jsx
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2014 Mirantis, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
|
||||
* not use this file except in compliance with the License. You may obtain
|
||||
* a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'react',
|
||||
'jsx!views/controls',
|
||||
'jsx!views/statistics_mixin'
|
||||
],
|
||||
function($, React, controls, statisticsMixin) {
|
||||
'use strict';
|
||||
|
||||
var WelcomePage = React.createClass({
|
||||
mixins: [
|
||||
statisticsMixin,
|
||||
React.BackboneMixin('settings')
|
||||
],
|
||||
breadcrumbsPath: [],
|
||||
hiddenLayout: true,
|
||||
title: function() {
|
||||
return $.t('welcome_page.title');
|
||||
},
|
||||
onStartButtonClick: function(e) {
|
||||
this.props.settings.get('statistics').user_choice_saved.value = true;
|
||||
this.saveSettings(e)
|
||||
.done(function() {
|
||||
app.navigate('#clusters', {trigger: true});
|
||||
})
|
||||
.fail(_.bind(function() {
|
||||
this.props.settings.get('statistics').user_choice_saved.value = false;
|
||||
}, this));
|
||||
},
|
||||
render: function() {
|
||||
if (this.state.loading) return <controls.ProgressBar />;
|
||||
var ns = 'welcome_page.',
|
||||
contacts = ['name', 'email', 'company'],
|
||||
error = _.compact(_.map(contacts, this.getError, this))[0];
|
||||
return (
|
||||
<div className='welcome-page'>
|
||||
<div>
|
||||
<h2 className='center'>{$.t(ns + 'title')}</h2>
|
||||
{this.renderIntro()}
|
||||
{this.renderInput('send_anonymous_statistic', null, 'welcome-checkbox-box')}
|
||||
<div className='welcome-text-box'>
|
||||
<p className='center'>
|
||||
{$.t(ns + 'support')}<br/>
|
||||
{$.t(ns + 'provide_contacts')}
|
||||
</p>
|
||||
</div>
|
||||
{this.renderInput('send_user_info', null, 'welcome-checkbox-box')}
|
||||
<form className='form-horizontal'>
|
||||
{ _.map(contacts, function(settingName) {
|
||||
return this.renderInput(settingName, 'welcome-form-item', 'welcome-form-box', true);
|
||||
}, this)}
|
||||
{error &&
|
||||
<div className='welcome-form-error'>{$.t(error)}</div>
|
||||
}
|
||||
<div className='welcome-button-box'>
|
||||
<button className='btn btn-large btn-success' disabled={this.state.actionInProgress} onClick={this.onStartButtonClick}>
|
||||
{$.t(ns + 'start_fuel')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className='welcome-text-box'>
|
||||
<p className='center'>
|
||||
{$.t(ns + 'change_settings')}<br/>
|
||||
{$.t(ns + 'thanks')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return WelcomePage;
|
||||
});
|
Loading…
Reference in New Issue
Block a user