mirror of https://github.com/apache/superset.git
Template dashboard (#5550)
* Template dashboard * Fix MySQL test * Model for user attributes * Redirect to welcome dash * Fix lint * Add missing file * Add migration script * Fix lint * Fix more lint
This commit is contained in:
parent
39ff9deb57
commit
222b79df7e
|
@ -412,6 +412,9 @@ HIVE_POLL_INTERVAL = 5
|
|||
# an XSS security vulnerability
|
||||
ENABLE_JAVASCRIPT_CONTROLS = False
|
||||
|
||||
# The id of a template dashboard that should be copied to every new user
|
||||
DASHBOARD_TEMPLATE_ID = None
|
||||
|
||||
# A callable that allows altering the database conneciton URL and params
|
||||
# on the fly, at runtime. This allows for things like impersonation or
|
||||
# arbitrary logic. For instance you can wire different users to
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
"""add user attributes table
|
||||
|
||||
Revision ID: 0c5070e96b57
|
||||
Revises: 7fcdcde0761c
|
||||
Create Date: 2018-08-06 14:38:18.965248
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0c5070e96b57'
|
||||
down_revision = '7fcdcde0761c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('user_attribute',
|
||||
sa.Column('created_on', sa.DateTime(), nullable=True),
|
||||
sa.Column('changed_on', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('welcome_dashboard_id', sa.Integer(), nullable=True),
|
||||
sa.Column('created_by_fk', sa.Integer(), nullable=True),
|
||||
sa.Column('changed_by_fk', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['ab_user.id'], ),
|
||||
sa.ForeignKeyConstraint(['welcome_dashboard_id'], ['dashboards.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('user_attribute')
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import core # noqa
|
||||
from . import sql_lab # noqa
|
||||
from . import user_attributes # noqa
|
||||
|
|
|
@ -17,6 +17,7 @@ import textwrap
|
|||
from flask import escape, g, Markup, request
|
||||
from flask_appbuilder import Model
|
||||
from flask_appbuilder.models.decorators import renders
|
||||
from flask_appbuilder.security.sqla.models import User
|
||||
from future.standard_library import install_aliases
|
||||
import numpy
|
||||
import pandas as pd
|
||||
|
@ -28,7 +29,7 @@ from sqlalchemy import (
|
|||
)
|
||||
from sqlalchemy.engine import url
|
||||
from sqlalchemy.engine.url import make_url
|
||||
from sqlalchemy.orm import relationship, subqueryload
|
||||
from sqlalchemy.orm import relationship, sessionmaker, subqueryload
|
||||
from sqlalchemy.orm.session import make_transient
|
||||
from sqlalchemy.pool import NullPool
|
||||
from sqlalchemy.schema import UniqueConstraint
|
||||
|
@ -39,6 +40,7 @@ from superset import app, db, db_engine_specs, security_manager, utils
|
|||
from superset.connectors.connector_registry import ConnectorRegistry
|
||||
from superset.legacy import update_time_range
|
||||
from superset.models.helpers import AuditMixinNullable, ImportMixin, set_perm
|
||||
from superset.models.user_attributes import UserAttribute
|
||||
from superset.viz import viz_types
|
||||
install_aliases()
|
||||
from urllib import parse # noqa
|
||||
|
@ -59,6 +61,41 @@ def set_related_perm(mapper, connection, target): # noqa
|
|||
target.perm = ds.perm
|
||||
|
||||
|
||||
def copy_dashboard(mapper, connection, target):
|
||||
dashboard_id = config.get('DASHBOARD_TEMPLATE_ID')
|
||||
if dashboard_id is None:
|
||||
return
|
||||
|
||||
Session = sessionmaker(autoflush=False)
|
||||
session = Session(bind=connection)
|
||||
new_user = session.query(User).filter_by(id=target.id).first()
|
||||
|
||||
# copy template dashboard to user
|
||||
template = session.query(Dashboard).filter_by(id=int(dashboard_id)).first()
|
||||
dashboard = Dashboard(
|
||||
dashboard_title=template.dashboard_title,
|
||||
position_json=template.position_json,
|
||||
description=template.description,
|
||||
css=template.css,
|
||||
json_metadata=template.json_metadata,
|
||||
slices=template.slices,
|
||||
owners=[new_user],
|
||||
)
|
||||
session.add(dashboard)
|
||||
session.commit()
|
||||
|
||||
# set dashboard as the welcome dashboard
|
||||
extra_attributes = UserAttribute(
|
||||
user_id=target.id,
|
||||
welcome_dashboard_id=dashboard.id,
|
||||
)
|
||||
session.add(extra_attributes)
|
||||
session.commit()
|
||||
|
||||
|
||||
sqla.event.listen(User, 'after_insert', copy_dashboard)
|
||||
|
||||
|
||||
class Url(Model, AuditMixinNullable):
|
||||
"""Used for the short url feature"""
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from flask_appbuilder import Model
|
||||
from sqlalchemy import Column, ForeignKey, Integer
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from superset import security_manager
|
||||
from superset.models.helpers import AuditMixinNullable
|
||||
|
||||
|
||||
class UserAttribute(Model, AuditMixinNullable):
|
||||
|
||||
"""
|
||||
Custom attributes attached to the user.
|
||||
|
||||
Extending the user attribute is tricky due to its dependency on the
|
||||
authentication typew an circular dependencies in Superset. Instead, we use
|
||||
a custom model for adding attributes.
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = 'user_attribute'
|
||||
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
|
||||
user_id = Column(Integer, ForeignKey('ab_user.id'))
|
||||
user = relationship(
|
||||
security_manager.user_model,
|
||||
backref='extra_attributes',
|
||||
foreign_keys=[user_id],
|
||||
)
|
||||
|
||||
welcome_dashboard_id = Column(Integer, ForeignKey('dashboards.id'))
|
||||
welcome_dashboard = relationship('Dashboard')
|
|
@ -45,6 +45,7 @@ from superset.jinja_context import get_template_processor
|
|||
from superset.legacy import cast_form_data, update_time_range
|
||||
import superset.models.core as models
|
||||
from superset.models.sql_lab import Query
|
||||
from superset.models.user_attributes import UserAttribute
|
||||
from superset.sql_parse import SupersetQuery
|
||||
from superset.utils import (
|
||||
merge_extra_filters, merge_request_params, QueryStatus,
|
||||
|
@ -2648,6 +2649,15 @@ class Superset(BaseSupersetView):
|
|||
if not g.user or not g.user.get_id():
|
||||
return redirect(appbuilder.get_url_for_login)
|
||||
|
||||
welcome_dashboard_id = (
|
||||
db.session
|
||||
.query(UserAttribute.welcome_dashboard_id)
|
||||
.filter_by(user_id=g.user.get_id())
|
||||
.scalar()
|
||||
)
|
||||
if welcome_dashboard_id:
|
||||
return self.dashboard(str(welcome_dashboard_id))
|
||||
|
||||
payload = {
|
||||
'user': bootstrap_user_data(),
|
||||
'common': self.common_bootsrap_payload(),
|
||||
|
|
Loading…
Reference in New Issue