Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Published" feature to dashboards #4725

Merged
merged 51 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
bf76d20
Allow users to publish dashboards
michellethomas Aug 15, 2018
224ef0a
Merge branch 'master' of https://github.com/apache/incubator-superset…
Sep 18, 2018
7bb0038
Rework publish dashboards feature
Sep 18, 2018
03fe633
Fix linter errors
Sep 18, 2018
746eb30
Update javascript tests
Sep 18, 2018
72e0b87
Add tests and change DashboardFilter
Sep 20, 2018
1b6cfa3
Fix some linter errors
Sep 20, 2018
562ccdc
Merge master into publish_dashboards
Sep 20, 2018
0404919
Remove commas from core.py
Sep 20, 2018
825536c
Merge test fixes from master
Sep 21, 2018
2bafc24
Fix some failing tests
Sep 21, 2018
cefc24f
More linter errors I introduced
Sep 21, 2018
a9095f8
Fix more linter errors I introduced
Sep 21, 2018
8aa2627
Merge branch 'master' of https://github.com/apache/incubator-superset…
Sep 24, 2018
ce1b1ef
Merge branch 'publish_dashboards' of https://github.com/Tresdon/incub…
Sep 24, 2018
62f8dfc
Merge branch 'master' into publish_dashboards
Tresdon Dec 17, 2018
76c0140
update alembic migration
Dec 17, 2018
91eb067
Update design of publish dash feature
Dec 18, 2018
5db4a6d
Upgrade migration version
Dec 18, 2018
65f77a8
Merge remote-tracking branch 'upstream/master' into publish_dashboards
Dec 18, 2018
4195e00
Secure publish endpoint
Dec 19, 2018
b0c0644
Remove bad quotes
Dec 19, 2018
78f2f2d
Give publish span its own style
Dec 19, 2018
3213d52
fix publish rendering
Dec 20, 2018
37afd58
Add new test for publish feature
Dec 21, 2018
51a7ae6
Update migration
Dec 21, 2018
dc65895
update slug in test
Dec 21, 2018
4023bb5
Merge branch 'master' of https://github.com/apache/incubator-superset…
Jan 18, 2019
07bf43a
Merge branch 'master' of https://github.com/apache/incubator-superset…
Jan 18, 2019
9368de0
Update migration
Jan 18, 2019
4173f29
Address reviwer comments
Jan 18, 2019
02ab3ed
Fix linter errors
Jan 18, 2019
c96b5db
Add licenses
Jan 18, 2019
4933bd2
Merge branch 'master' of https://github.com/apache/incubator-superset…
Jan 22, 2019
466cf8d
Remove fetchPublished(), use bootstrap data
Jan 22, 2019
de66fc6
Merge branch 'master' of https://github.com/apache/incubator-superset…
Mar 26, 2019
ddadc64
Update migration
Mar 26, 2019
895257d
Update croniter to existing version
Mar 26, 2019
38498f2
Fix linter errors
Mar 26, 2019
f255c8d
Fix merge conflicts
Jun 11, 2019
ff7dbcc
Merge upstream into branch
Jun 24, 2019
6c067b4
Upgrade DB Revisions
Jun 24, 2019
10e2403
Fix flake8 linter error
Jun 24, 2019
d5bd206
Set all dashboards to published on migration
Jun 25, 2019
4f33abf
Merge branch 'master' of https://github.com/apache/incubator-superset…
Jun 25, 2019
4fca28a
Migration proper line spacing
Jun 25, 2019
28796af
Fix migration to work with postgres
Jun 26, 2019
6d67ad2
Use black code formatting
Jun 26, 2019
495f2c6
UPDATE statement works with postgresql and sqlite hopefully
Jun 26, 2019
32fee1a
Merge branch 'master' of https://github.com/apache/incubator-superset…
Jun 28, 2019
37f140a
Update wording to kick off travis
Jun 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
import Header from '../../../../src/dashboard/components/Header';
import EditableTitle from '../../../../src/components/EditableTitle';
import FaveStar from '../../../../src/components/FaveStar';
import PublishedStatus from '../../../../src/dashboard/components/PublishedStatus';
import HeaderActionsDropdown from '../../../../src/dashboard/components/HeaderActionsDropdown';
import Button from '../../../../src/components/Button';
import UndoRedoKeylisteners from '../../../../src/dashboard/components/UndoRedoKeylisteners';
Expand All @@ -43,6 +44,8 @@ describe('Header', () => {
fetchFaveStar: () => {},
fetchCharts: () => {},
saveFaveStar: () => {},
savePublished: () => {},
isPublished: () => {},
startPeriodicRender: () => {},
updateDashboardTitle: () => {},
editMode: false,
Expand Down Expand Up @@ -78,6 +81,11 @@ describe('Header', () => {
expect(wrapper.find(EditableTitle)).toHaveLength(1);
});

it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toHaveLength(1);
});

it('should render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).toHaveLength(1);
Expand Down Expand Up @@ -110,6 +118,11 @@ describe('Header', () => {
expect(wrapper.find(EditableTitle)).toHaveLength(1);
});

it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toHaveLength(1);
});

it('should render the FaveStar', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(FaveStar)).toHaveLength(1);
Expand Down Expand Up @@ -147,6 +160,11 @@ describe('Header', () => {
expect(wrapper.find(FaveStar)).toHaveLength(1);
});

it('should render the PublishedStatus', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(PublishedStatus)).toHaveLength(1);
});

it('should render the HeaderActionsDropdown', () => {
const wrapper = setup(overrideProps);
expect(wrapper.find(HeaderActionsDropdown)).toHaveLength(1);
Expand Down
26 changes: 26 additions & 0 deletions superset/assets/src/dashboard/actions/dashboardState.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,32 @@ export function saveFaveStar(id, isStarred) {
};
}

export const TOGGLE_PUBLISHED = 'TOGGLE_PUBLISHED';
export function togglePublished(isPublished) {
return { type: TOGGLE_PUBLISHED, isPublished };
}

export function savePublished(id, isPublished) {
return function savePublishedThunk(dispatch) {
return SupersetClient.post({
endpoint: `/superset/dashboard/${id}/published/`,
postPayload: { published: isPublished },
})
.then(() => {
const nowPublished = isPublished ? 'published' : 'hidden';
dispatch(addSuccessToast(t(`This dashboard is now ${nowPublished}`)));
dispatch(togglePublished(isPublished));
})
.catch(() => {
dispatch(
addDangerToast(
t('You do not have permissions to edit this dashboard.'),
),
);
});
};
}

export const TOGGLE_EXPAND_SLICE = 'TOGGLE_EXPAND_SLICE';
export function toggleExpandSlice(sliceId) {
return { type: TOGGLE_EXPAND_SLICE, sliceId };
Expand Down
13 changes: 13 additions & 0 deletions superset/assets/src/dashboard/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import HeaderActionsDropdown from './HeaderActionsDropdown';
import EditableTitle from '../../components/EditableTitle';
import Button from '../../components/Button';
import FaveStar from '../../components/FaveStar';
import PublishedStatus from './PublishedStatus';
import UndoRedoKeylisteners from './UndoRedoKeylisteners';

import { chartPropShape } from '../util/propShapes';
Expand Down Expand Up @@ -57,12 +58,14 @@ const propTypes = {
colorNamespace: PropTypes.string,
colorScheme: PropTypes.string,
isStarred: PropTypes.bool.isRequired,
isPublished: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
onSave: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
fetchFaveStar: PropTypes.func.isRequired,
fetchCharts: PropTypes.func.isRequired,
saveFaveStar: PropTypes.func.isRequired,
savePublished: PropTypes.func.isRequired,
startPeriodicRender: PropTypes.func.isRequired,
updateDashboardTitle: PropTypes.func.isRequired,
editMode: PropTypes.bool.isRequired,
Expand Down Expand Up @@ -272,6 +275,7 @@ class Header extends React.PureComponent {
onSave,
updateCss,
editMode,
isPublished,
builderPaneType,
dashboardInfo,
hasUnsavedChanges,
Expand All @@ -293,6 +297,15 @@ class Header extends React.PureComponent {
onSaveTitle={this.handleChangeText}
showTooltip={false}
/>
<span className="publish">
<PublishedStatus
dashboardId={dashboardInfo.id}
isPublished={isPublished}
savePublished={this.props.savePublished}
canEdit={userCanEdit}
canSave={userCanSaveAs}
/>
</span>
<span className="favstar">
<FaveStar
itemId={dashboardInfo.id}
Expand Down
121 changes: 121 additions & 0 deletions superset/assets/src/dashboard/components/PublishedStatus.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { t } from '@superset-ui/translation';
import TooltipWrapper from '../../components/TooltipWrapper';

const propTypes = {
dashboardId: PropTypes.number.isRequired,
isPublished: PropTypes.bool.isRequired,
savePublished: PropTypes.func.isRequired,
canEdit: PropTypes.bool.isRequired,
canSave: PropTypes.bool.isRequired,
};

const draftButtonTooltip = t(
'This dashboard is not published which means it will not show up in the list of dashboards. ' +
Tresdon marked this conversation as resolved.
Show resolved Hide resolved
'Click here to publish this dashboard.',
);

const draftDivTooltip = t(
'This dashboard is not published which means it will not show up in the list of dashboards.' +
' Favorite it to see it there or access it by using the URL directly.',
);

const publishedTooltip = t(
'This dashboard is published. Click to make it a draft.',
);

const divStyle = {
border: '1px dotted black',
backgroundColor: '#F9F9F9',
padding: '3px 7px 3px 7px',
fontFamily: 'Monospace',
fontSize: '16px',
};

export default class PublishedStatus extends React.Component {
componentDidMount() {
this.togglePublished = this.togglePublished.bind(this);
}

togglePublished() {
this.props.savePublished(this.props.dashboardId, !this.props.isPublished);
}

render() {
// Show everybody the draft badge
if (!this.props.isPublished) {
// if they can edit the dash, make the badge a button
if (this.props.canEdit && this.props.canSave) {
return (
<TooltipWrapper
label="Unpublished Dashboard"
placement="bottom"
tooltip={draftButtonTooltip}
>
<button
style={divStyle}
onClick={() => {
this.togglePublished();
}}
>
Draft
</button>
</TooltipWrapper>
);
}
return (
<TooltipWrapper
label="Unpublished Dashboard"
placement="bottom"
tooltip={draftDivTooltip}
>
<div style={divStyle}>Draft</div>
</TooltipWrapper>
);
}

// Show the published badge for the owner of the dashboard to toggle
else if (this.props.canEdit && this.props.canSave) {
return (
<TooltipWrapper
label="Published Dashboard"
placement="bottom"
tooltip={publishedTooltip}
>
<button
style={divStyle}
onClick={() => {
this.togglePublished();
}}
>
Published
</button>
</TooltipWrapper>
);
}

// Don't show anything if one doesn't own the dashboard and it is published
return null;
}
}

PublishedStatus.propTypes = propTypes;
3 changes: 3 additions & 0 deletions superset/assets/src/dashboard/containers/DashboardHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
showBuilderPane,
fetchFaveStar,
saveFaveStar,
savePublished,
fetchCharts,
startPeriodicRender,
updateCss,
Expand Down Expand Up @@ -76,6 +77,7 @@ function mapStateToProps({
charts,
userId: dashboardInfo.userId,
isStarred: !!dashboardState.isStarred,
isPublished: !!dashboardState.isPublished,
isLoading: isDashboardLoading(charts),
hasUnsavedChanges: !!dashboardState.hasUnsavedChanges,
maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded,
Expand All @@ -96,6 +98,7 @@ function mapDispatchToProps(dispatch) {
showBuilderPane,
fetchFaveStar,
saveFaveStar,
savePublished,
fetchCharts,
startPeriodicRender,
updateDashboardTitle,
Expand Down
4 changes: 4 additions & 0 deletions superset/assets/src/dashboard/reducers/dashboardState.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
SHOW_BUILDER_PANE,
TOGGLE_EXPAND_SLICE,
TOGGLE_FAVE_STAR,
TOGGLE_PUBLISHED,
UPDATE_CSS,
SET_REFRESH_FREQUENCY,
} from '../actions/dashboardState';
Expand Down Expand Up @@ -71,6 +72,9 @@ export default function dashboardStateReducer(state = {}, action) {
[TOGGLE_FAVE_STAR]() {
return { ...state, isStarred: action.isStarred };
},
[TOGGLE_PUBLISHED]() {
return { ...state, isPublished: action.isPublished };
},
[SET_EDIT_MODE]() {
return {
...state,
Expand Down
1 change: 1 addition & 0 deletions superset/assets/src/dashboard/reducers/getInitialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export default function(bootstrapData) {
colorNamespace: dashboard.metadata.color_namespace,
colorScheme: dashboard.metadata.color_scheme,
editMode: dashboard.dash_edit_perm && editMode,
isPublished: dashboard.published,
builderPaneType:
dashboard.dash_edit_perm && editMode
? BUILDER_PANE_TYPE.ADD_COMPONENTS
Expand Down
5 changes: 5 additions & 0 deletions superset/assets/src/dashboard/stylesheets/dashboard.less
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ body {
position: relative;
margin-left: 8px;
}

.publish {
position: relative;
margin-left: 8px;
}
}

.ace_gutter {
Expand Down
1 change: 1 addition & 0 deletions superset/assets/src/dashboard/util/propShapes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const dashboardStatePropShape = PropTypes.shape({
filters: PropTypes.object.isRequired,
expandedSlices: PropTypes.object,
editMode: PropTypes.bool,
isPublished: PropTypes.bool.isRequired,
builderPaneType: PropTypes.string.isRequired,
colorNamespace: PropTypes.string,
colorScheme: PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
"""Add published column to dashboards

Revision ID: d6ffdf31bdd4
Revises: 45e7da7cfeba
Create Date: 2018-03-30 14:00:44.929483

"""

# revision identifiers, used by Alembic.
revision = 'd6ffdf31bdd4'
down_revision = 'd7c1a0d6f2da'

from alembic import op
import sqlalchemy as sa


def upgrade():
op.add_column('dashboards', sa.Column('published', sa.Boolean(), nullable=True))


def downgrade():
op.drop_column('dashboards', 'published')
2 changes: 2 additions & 0 deletions superset/models/core.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
slices = relationship(
'Slice', secondary=dashboard_slices, backref='dashboards')
owners = relationship(security_manager.user_model, secondary=dashboard_user)
published = Column(Boolean, default=False)

export_fields = ('dashboard_title', 'position_json', 'json_metadata',
'description', 'css', 'slug')
Expand Down Expand Up @@ -467,6 +468,7 @@ def data(self):
'metadata': self.params_dict,
'css': self.css,
'dashboard_title': self.dashboard_title,
'published': self.published,
'slug': self.slug,
'slices': [slc.data for slc in self.slices],
'position_json': positions,
Expand Down
Loading