aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2017-05-01 16:57:55 +0200
committerStas Vilchik <stas-vilchik@users.noreply.github.com>2017-05-02 14:45:47 +0200
commit2d9e5533515dd5969897ba1ad3435fea27da28f0 (patch)
tree71c6a4e9919e9ebbd8ace505ba751331148767d2
parent19f9ad34bdca23ae4cfcc0bd601581f7c9c5442e (diff)
downloadsonarqube-2d9e5533515dd5969897ba1ad3435fea27da28f0.tar.gz
sonarqube-2d9e5533515dd5969897ba1ad3435fea27da28f0.zip
SONAR-9166 Allow to change default project visibility for organization
-rw-r--r--server/sonar-web/src/main/js/api/organizations.js3
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/Home.js17
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/List.js2
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js23
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js19
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/ChangeVisibilityForm.js114
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/header.js92
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/main.js44
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/search.js18
-rw-r--r--server/sonar-web/src/main/js/store/organizations/duck.js1
-rw-r--r--server/sonar-web/src/main/less/components/tooltips.less5
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties8
12 files changed, 275 insertions, 71 deletions
diff --git a/server/sonar-web/src/main/js/api/organizations.js b/server/sonar-web/src/main/js/api/organizations.js
index 25fb80bcaf8..097a17ce5cb 100644
--- a/server/sonar-web/src/main/js/api/organizations.js
+++ b/server/sonar-web/src/main/js/api/organizations.js
@@ -68,3 +68,6 @@ export const addMember = (data: { login: string, organization: string }) =>
export const removeMember = (data: { login: string, organization: string }) =>
post('/api/organizations/remove_member', data);
+
+export const changeProjectVisibility = (organization: string, projectVisibility: string) =>
+ post('/api/organizations/update_project_visibility', { organization, projectVisibility });
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
index b649d54728e..0f70164617e 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
@@ -21,7 +21,6 @@ import React from 'react';
import Helmet from 'react-helmet';
import Header from './Header';
import List from './List';
-import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
import { translate } from '../../../helpers/l10n';
export default class Home extends React.PureComponent {
@@ -45,15 +44,13 @@ export default class Home extends React.PureComponent {
refresh={this.props.refresh}
/>
- <TooltipsContainer>
- <List
- organization={this.props.organization}
- permissionTemplates={this.props.permissionTemplates}
- permissions={this.props.permissions}
- topQualifiers={this.props.topQualifiers}
- refresh={this.props.refresh}
- />
- </TooltipsContainer>
+ <List
+ organization={this.props.organization}
+ permissionTemplates={this.props.permissionTemplates}
+ permissions={this.props.permissions}
+ topQualifiers={this.props.topQualifiers}
+ refresh={this.props.refresh}
+ />
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js
index 84a834f6689..275489d3500 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/List.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/List.js
@@ -44,7 +44,7 @@ export default class List extends React.PureComponent {
return (
<table id="permission-templates" className="data zebra permissions-table">
- <ListHeader permissions={this.props.permissions} />
+ <ListHeader organization={this.props.organization} permissions={this.props.permissions} />
<tbody>{permissionTemplates}</tbody>
</table>
);
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js
index 19181043a58..29d3e159a37 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.js
@@ -18,17 +18,32 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Tooltip from '../../../components/controls/Tooltip';
+import { translate } from '../../../helpers/l10n';
export default class ListHeader extends React.PureComponent {
static propTypes = {
+ organization: React.PropTypes.object,
permissions: React.PropTypes.array.isRequired
};
+ renderTooltip = permission =>
+ (this.props.organization && (permission.key === 'user' || permission.key === 'codeviewer')
+ ? <div>
+ {permission.description}
+ <div className="alert alert-warning spacer-top">
+ {translate('projects_role', permission.key, 'public_projects_warning')}
+ </div>
+ </div>
+ : permission.description);
+
render() {
- const cells = this.props.permissions.map(p => (
- <th key={p.key} className="permission-column">
- {p.name}
- <i className="icon-help little-spacer-left" title={p.description} data-toggle="tooltip" />
+ const cells = this.props.permissions.map(permission => (
+ <th key={permission.key} className="permission-column">
+ {permission.name}
+ <Tooltip overlay={this.renderTooltip(permission)}>
+ <i className="icon-help little-spacer-left" />
+ </Tooltip>
</th>
));
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js b/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js
index baf6f106889..8507504ea85 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/AppContainer.js
@@ -20,8 +20,11 @@
import React from 'react';
import { connect } from 'react-redux';
import Main from './main';
+import { onFail } from '../../store/rootActions';
import { getCurrentUser, getAppState } from '../../store/rootReducer';
import { getRootQualifiers } from '../../store/appState/duck';
+import { receiveOrganizations } from '../../store/organizations/duck';
+import { changeProjectVisibility } from '../../api/organizations';
function AppContainer(props) {
const hasProvisionPermission = props.organization
@@ -36,6 +39,7 @@ function AppContainer(props) {
<Main
hasProvisionPermission={hasProvisionPermission}
topLevelQualifiers={topLevelQualifiers}
+ onVisibilityChange={props.onVisibilityChange}
organization={props.organization}
/>
);
@@ -46,4 +50,17 @@ const mapStateToProps = state => ({
user: getCurrentUser(state)
});
-export default connect(mapStateToProps)(AppContainer);
+const onVisibilityChange = (organization, visibility) => dispatch => {
+ const currentVisibility = organization.projectVisibility;
+ dispatch(receiveOrganizations([{ ...organization, projectVisibility: visibility }]));
+ changeProjectVisibility(organization.key, visibility).catch(error => {
+ onFail(dispatch)(error);
+ dispatch(receiveOrganizations([{ ...organization, projectVisibility: currentVisibility }]));
+ });
+};
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+ onVisibilityChange: visibility => dispatch(onVisibilityChange(ownProps.organization, visibility))
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/ChangeVisibilityForm.js b/server/sonar-web/src/main/js/apps/projects-admin/ChangeVisibilityForm.js
new file mode 100644
index 00000000000..ae88b6317b7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects-admin/ChangeVisibilityForm.js
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import Modal from 'react-modal';
+import classNames from 'classnames';
+import { translate } from '../../helpers/l10n';
+
+type Props = {
+ onClose: () => void,
+ onConfirm: string => void,
+ visibility: string
+};
+
+type State = {
+ visibility: string
+};
+
+export default class ChangeVisibilityForm extends React.PureComponent {
+ props: Props;
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { visibility: props.visibility };
+ }
+
+ handleCancelClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleConfirmClick = (event: Event) => {
+ event.preventDefault();
+ this.props.onConfirm(this.state.visibility);
+ this.props.onClose();
+ };
+
+ handleVisibilityClick = (visibility: string) => (
+ event: Event & { currentTarget: HTMLElement }
+ ) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ visibility });
+ };
+
+ render() {
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel="modal form"
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+
+ <header className="modal-head">
+ <h2>{translate('organization.change_visibility_form.header')}</h2>
+ </header>
+
+ <div className="modal-body">
+ {['public', 'private'].map(visibility => (
+ <div className="big-spacer-bottom" key={visibility}>
+ <p>
+ <a
+ className="link-base-color link-no-underline"
+ href="#"
+ onClick={this.handleVisibilityClick(visibility)}>
+ <i
+ className={classNames('icon-radio', 'spacer-right', {
+ 'is-checked': this.state.visibility === visibility
+ })}
+ />
+ {translate('visibility', visibility)}
+ </a>
+ </p>
+ <p className="text-muted spacer-top" style={{ paddingLeft: 22 }}>
+ {translate('visibility', visibility, 'description.short')}
+ </p>
+ </div>
+ ))}
+
+ <div className="alert alert-warning">
+ {translate('organization.change_visibility_form.warning')}
+ </div>
+ </div>
+
+ <footer className="modal-foot">
+ <button onClick={this.handleConfirmClick}>
+ {translate('organization.change_visibility_form.submit')}
+ </button>
+ <a href="#" onClick={this.handleCancelClick}>{translate('cancel')}</a>
+ </footer>
+
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/header.js b/server/sonar-web/src/main/js/apps/projects-admin/header.js
index e2bc02294d9..ba3ee626561 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/header.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/header.js
@@ -17,14 +17,27 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import CreateView from './create-view';
-import BulkApplyTemplateView from './views/BulkApplyTemplateView';
+import ChangeVisibilityForm from './ChangeVisibilityForm';
+import { translate } from '../../helpers/l10n';
+import type { Organization } from '../../store/organizations/duck';
+
+type Props = {|
+ hasProvisionPermission: boolean,
+ onVisibilityChange: string => void,
+ organization?: Organization,
+ refresh: () => void
+|};
+
+type State = {
+ visibilityForm: boolean
+};
export default class Header extends React.PureComponent {
- static propTypes = {
- hasProvisionPermission: React.PropTypes.bool.isRequired
- };
+ props: Props;
+ state: State = { visibilityForm: false };
createProject() {
new CreateView({
@@ -33,56 +46,59 @@ export default class Header extends React.PureComponent {
}).render();
}
- bulkApplyTemplate() {
- new BulkApplyTemplateView({
- total: this.props.total,
- selection: this.props.selection,
- query: this.props.query,
- qualifier: this.props.qualifier,
- organization: this.props.organization
- }).render();
- }
+ handleChangeVisibilityClick = (event: Event) => {
+ event.preventDefault();
+ this.setState({ visibilityForm: true });
+ };
+
+ closeVisiblityForm = () => {
+ this.setState({ visibilityForm: false });
+ };
renderCreateButton() {
if (!this.props.hasProvisionPermission) {
return null;
}
return (
- <li>
- <button onClick={this.createProject.bind(this)}>
- Create Project
- </button>
- </li>
- );
- }
-
- renderBulkApplyTemplateButton() {
- return (
- <li>
- <button onClick={this.bulkApplyTemplate.bind(this)}>
- Bulk Apply Permission Template
- </button>
- </li>
+ <button onClick={this.createProject.bind(this)}>
+ Create Project
+ </button>
);
}
render() {
+ const { organization } = this.props;
+
return (
<header className="page-header">
- <h1 className="page-title">Projects Management</h1>
+ <h1 className="page-title">{translate('projects_management')}</h1>
<div className="page-actions">
- <ul className="list-inline">
- {this.renderCreateButton()}
- {this.renderBulkApplyTemplateButton()}
- </ul>
+ {organization != null &&
+ <span className="big-spacer-right">
+ {translate('organization.default_visibility_of_new_projects')}
+ {' '}
+ <strong>
+ {translate('visibility', organization.projectVisibility)}
+ </strong>
+ <a
+ className="spacer-left icon-edit"
+ href="#"
+ onClick={this.handleChangeVisibilityClick}
+ />
+ </span>}
+ {this.renderCreateButton()}
</div>
<p className="page-description">
- Use this page to delete multiple projects at once, or to provision projects
- {' '}
- if you would like to configure them before the first analysis. Note that once
- {' '}
- a project is provisioned, you have access to perform all project configurations on it.
+ {translate('projects_management.page.description')}
</p>
+
+ {this.state.visibilityForm &&
+ organization != null &&
+ <ChangeVisibilityForm
+ onClose={this.closeVisiblityForm}
+ onConfirm={this.props.onVisibilityChange}
+ visibility={organization.projectVisibility}
+ />}
</header>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/main.js b/server/sonar-web/src/main/js/apps/projects-admin/main.js
index 3079aae3c63..60942300cf4 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/main.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/main.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { debounce, uniq, without } from 'lodash';
import Header from './header';
@@ -25,14 +26,30 @@ import Projects from './projects';
import { PAGE_SIZE, TYPE } from './constants';
import { getComponents, getProvisioned, getGhosts, deleteComponents } from '../../api/components';
import ListFooter from '../../components/controls/ListFooter';
+import type { Organization } from '../../store/organizations/duck';
+
+type Props = {|
+ hasProvisionPermission: boolean,
+ onVisibilityChange: string => void,
+ organization?: Organization
+|};
+
+type State = {
+ ready: boolean,
+ projects: Array<{ key: string }>,
+ total: number,
+ page: number,
+ query: string,
+ qualifiers: string,
+ type: string,
+ selection: Array<string>
+};
export default class Main extends React.PureComponent {
- static propTypes = {
- hasProvisionPermission: React.PropTypes.bool.isRequired,
- organization: React.PropTypes.object
- };
+ props: Props;
+ state: State;
- constructor(props) {
+ constructor(props: Props) {
super(props);
this.state = {
ready: false,
@@ -52,7 +69,7 @@ export default class Main extends React.PureComponent {
}
getFilters = () => {
- const filters = { ps: PAGE_SIZE };
+ const filters: { [string]: string | number } = { ps: PAGE_SIZE };
if (this.state.page !== 1) {
filters.p = this.state.page;
}
@@ -128,7 +145,7 @@ export default class Main extends React.PureComponent {
this.setState({ ready: false, page: this.state.page + 1 }, this.requestProjects);
};
- onSearch = query => {
+ onSearch = (query: string) => {
this.setState(
{
ready: false,
@@ -140,7 +157,7 @@ export default class Main extends React.PureComponent {
);
};
- onTypeChanged = newType => {
+ onTypeChanged = (newType: string) => {
this.setState(
{
ready: false,
@@ -154,7 +171,7 @@ export default class Main extends React.PureComponent {
);
};
- onQualifierChanged = newQualifier => {
+ onQualifierChanged = (newQualifier: string) => {
this.setState(
{
ready: false,
@@ -168,12 +185,12 @@ export default class Main extends React.PureComponent {
);
};
- onProjectSelected = project => {
+ onProjectSelected = (project: { key: string }) => {
const newSelection = uniq([].concat(this.state.selection, project.key));
this.setState({ selection: newSelection });
};
- onProjectDeselected = project => {
+ onProjectDeselected = (project: { key: string }) => {
const newSelection = without(this.state.selection, project.key);
this.setState({ selection: newSelection });
};
@@ -204,11 +221,8 @@ export default class Main extends React.PureComponent {
<div className="page page-limited">
<Header
hasProvisionPermission={this.props.hasProvisionPermission}
- selection={this.state.selection}
- total={this.state.total}
- query={this.state.query}
- qualifier={this.state.qualifiers}
refresh={this.requestProjects}
+ onVisibilityChange={this.props.onVisibilityChange}
organization={this.props.organization}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/search.js b/server/sonar-web/src/main/js/apps/projects-admin/search.js
index 2e7d3def391..65e6e0fb9d3 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/search.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/search.js
@@ -21,6 +21,7 @@ import React from 'react';
import { sortBy } from 'lodash';
import { TYPE, QUALIFIERS_ORDER } from './constants';
import DeleteView from './delete-view';
+import BulkApplyTemplateView from './views/BulkApplyTemplateView';
import RadioToggle from '../../components/controls/RadioToggle';
import Checkbox from '../../components/controls/Checkbox';
import { translate } from '../../helpers/l10n';
@@ -69,6 +70,16 @@ export default class Search extends React.PureComponent {
}).render();
};
+ bulkApplyTemplate = () => {
+ new BulkApplyTemplateView({
+ total: this.props.total,
+ selection: this.props.selection,
+ query: this.props.query,
+ qualifier: this.props.qualifier,
+ organization: this.props.organization
+ }).render();
+ };
+
renderCheckbox = () => {
const isAllChecked =
this.props.projects.length > 0 && this.props.selection.length === this.props.projects.length;
@@ -144,12 +155,15 @@ export default class Search extends React.PureComponent {
/>
</form>
</td>
- <td className="thin text-middle">
+ <td className="thin nowrap text-middle">
+ <button className="spacer-right" onClick={this.bulkApplyTemplate}>
+ {translate('permission_templates.bulk_apply_permission_template')}
+ </button>
<button
onClick={this.deleteProjects}
className="button-red"
disabled={!isSomethingSelected}>
- Delete
+ {translate('delete')}
</button>
</td>
</tr>
diff --git a/server/sonar-web/src/main/js/store/organizations/duck.js b/server/sonar-web/src/main/js/store/organizations/duck.js
index 89cb815c7a6..9bc74a7550b 100644
--- a/server/sonar-web/src/main/js/store/organizations/duck.js
+++ b/server/sonar-web/src/main/js/store/organizations/duck.js
@@ -31,6 +31,7 @@ export type Organization = {
key: string,
name: string,
pages?: Array<{ key: string, name: string }>,
+ projectVisibility: string,
url?: string
};
diff --git a/server/sonar-web/src/main/less/components/tooltips.less b/server/sonar-web/src/main/less/components/tooltips.less
index eb225f10112..a0a073940c5 100644
--- a/server/sonar-web/src/main/less/components/tooltips.less
+++ b/server/sonar-web/src/main/less/components/tooltips.less
@@ -78,6 +78,11 @@
border-radius: 4px;
letter-spacing: 0.04em;
overflow: hidden;
+
+ .alert {
+ margin-bottom: 5px /* align with side padding */ ;
+ border-radius: 4px;
+ }
}
.tooltip-arrow,
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 5b5e056dfd3..abc8eb8f5c5 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -531,6 +531,7 @@ project_history.page.description=Edit snapshot metadata, or delete snapshots fro
project_roles.page=Project Permissions
project_roles.page.description=Grant and revoke permissions to this project to Browse (view a project's metrics), See Source Code, and Administer. Permissions can be granted to groups or individual users.
project_roles.page.description2=Grant and revoke project-level permissions. Permissions can be granted to groups or individual users.
+projects_management.page.description=Use this page to delete multiple projects at once, or to provision projects if you would like to configure them before the first analysis. Note that once a project is provisioned, you have access to perform all project configurations on it.
settings.page=General Settings
settings.page.description=Edit global settings for this SonarQube instance.
system_info.page=System Info
@@ -2424,8 +2425,10 @@ projects_role.issueadmin=Administer Issues
projects_role.issueadmin.desc=Perform advanced editing on issues: marking an issue False Positive / Won't Fix, and changing an Issue's severity. (Users will also need "Browse" permission)
projects_role.user=Browse
projects_role.user.desc=Access a project, browse its measures, and create/edit issues for it.
+projects_role.user.public_projects_warning=This option is not editable for public projects. Anyone will still be able to browse even if you apply a template with this option unchecked.
projects_role.codeviewer=See Source Code
projects_role.codeviewer.desc=View the project's source code. (Users will also need "Browse" permission)
+projects_role.codeviewer.public_projects_warning=This option is not editable for public projects. Source code will will always be visible even if you apply a template with this option unchecked.
projects_role.scan=Execute Analysis
projects_role.scan.desc=Ability to get all settings required to perform an analysis (including the secured settings like passwords) and to push analysis results to the SonarQube server.
projects_role.bulk_change=Bulk Change
@@ -2466,6 +2469,7 @@ permission_template.default_for=Default for {0}
permission_templates.project_creators=Project Creators
permission_templates.project_creators.explanation=When a new project is created, the user who creates the project will receive this permission on the project.
permission_templates.grant_permission_to_project_creators=Grant the "{0}" permission to project creators
+permission_templates.bulk_apply_permission_template=Bulk Apply Permission Template
#------------------------------------------------------------------------------
@@ -2860,3 +2864,7 @@ organization.members.remove_x=Are you sure you want to remove {0} from {1}'s mem
organization.members.manage_groups=Manage groups
organization.members.members_groups={0}'s groups:
organization.members.add_to_members=Add to members
+organization.default_visibility_of_new_projects=Default visibility of new projects:
+organization.change_visibility_form.header=Set Default Visibility of New Projects
+organization.change_visibility_form.warning=This will not change the visibility of already existing projects.
+organization.change_visibility_form.submit=Change Default Visibility