123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- /*
- * Copyright (C) 2016 - present Instructure, Inc.
- *
- * This file is part of Canvas.
- *
- * Canvas is free software: you can redistribute it and/or modify it under
- * the terms of the GNU Affero General Public License as published by the Free
- * Software Foundation, version 3 of the License.
- *
- * Canvas 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 Affero General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Affero General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- import I18n from 'i18n!theme_collection_view'
- import React from 'react'
- import PropTypes from 'prop-types'
- import _ from 'underscore'
- import customTypes from './PropTypes'
- import submitHtmlForm from './submitHtmlForm'
- import ThemeCard from './ThemeCard'
- const blankConfig = {
- name: I18n.t('Default Template'),
- brand_config: {
- md5: '',
- variables: {}
- }
- }
- const isSystemTheme = (sharedBrandConfig) => !sharedBrandConfig.account_id
- export default React.createClass({
- displayName: 'CollectionView',
- propTypes: {
- sharedBrandConfigs: PropTypes.arrayOf(customTypes.sharedBrandConfig).isRequired,
- activeBrandConfig: customTypes.brandConfig.isRequired,
- accountID: PropTypes.string.isRequired,
- brandableVariableDefaults: customTypes.brandableVariableDefaults,
- baseBrandableVariables: customTypes.variables
- },
- getInitialState () {
- return {
- newThemeModalIsOpen: false,
- brandConfigBeingDeleted: null
- }
- },
- brandVariableValue (brandConfig, variableName) {
- const explicitValue = brandConfig && brandConfig.variables[variableName]
- if (explicitValue) return explicitValue
- const variableInfo = this.props.brandableVariableDefaults[variableName]
- let _default = variableInfo.default
- if (_default && _default[0] === '$') {
- return this.brandVariableValue(brandConfig, _default.substring(1))
- }
- return this.props.baseBrandableVariables[variableName]
- },
- startFromBlankSlate() {
- const md5 = ''
- this.saveToSession(md5)
- },
- startEditing({md5ToActivate, sharedBrandConfigToStartEditing}) {
- if (md5ToActivate === (this.props.activeBrandConfig && this.props.activeBrandConfig.md5)) {
- md5ToActivate = undefined
- }
- if (sharedBrandConfigToStartEditing) {
- sessionStorage.setItem('sharedBrandConfigBeingEdited', JSON.stringify(sharedBrandConfigToStartEditing))
- } else {
- sessionStorage.removeItem('sharedBrandConfigBeingEdited')
- }
- submitHtmlForm(`/accounts/${this.props.accountID}/brand_configs/save_to_user_session`, 'POST', md5ToActivate)
- },
- deleteSharedBrandConfig (sharedBrandConfigId) {
- $.ajaxJSON(`/api/v1/shared_brand_configs/${sharedBrandConfigId}`, 'DELETE', {}, () => {
- window.location.reload()
- })
- },
- isActiveBrandConfig (brandConfig) {
- if (this.props.activeBrandConfig) {
- return brandConfig.md5 === this.props.activeBrandConfig.md5
- } else {
- return brandConfig === blankConfig.brand_config
- }
- },
- isDeletable (sharedBrandConfig) {
- // Globally-shared themes and the active theme are not deletable
- return !isSystemTheme(sharedBrandConfig) &&
- !this.isActiveBrandConfig(sharedBrandConfig.brand_config)
- },
- thingsToShow () {
- const thingsToShow = [blankConfig].concat(this.props.sharedBrandConfigs)
- const isActive = (sharedBrandConfig) => this.isActiveBrandConfig(sharedBrandConfig.brand_config)
- // Add in a tile for the active theme if it is otherwise not present in the shared ones
- if (this.props.activeBrandConfig && !_.find(this.props.sharedBrandConfigs, isActive)) {
- const cardForActiveBrandConfig = {
- brand_config: this.props.activeBrandConfig,
- account_id: this.props.accountID
- }
- thingsToShow.unshift(cardForActiveBrandConfig)
- }
- // Make sure the active theme shows up first
- const sorted = _.sortBy(thingsToShow, thing => !isActive(thing))
- // split the globally shared themes and the ones that people in this account have shared apart
- return _.groupBy(sorted, (sbc) => isSystemTheme(sbc) ? 'globalThemes' : 'accountSpecificThemes')
- },
- renderCard (sharedConfig) {
- const onClick = () => {
- const isReadOnly = isSystemTheme(sharedConfig)
- this.startEditing({
- md5ToActivate: sharedConfig.brand_config.md5,
- sharedBrandConfigToStartEditing: !isReadOnly && sharedConfig
- })
- }
- const isActiveEditableTheme = (sbc) =>
- !isSystemTheme(sbc) && (
- this.props.activeBrandConfig &&
- this.props.activeBrandConfig.md5 === sbc.brand_config.md5
- )
- // even if this theme's md5 is active, don't mark it as active if it is a system theme
- // and there is an account-shared theme that also matches the active md5
- const isActiveBrandConfig = this.isActiveBrandConfig(sharedConfig.brand_config) && (
- !isSystemTheme(sharedConfig) ||
- !this.props.sharedBrandConfigs.some(isActiveEditableTheme)
- )
- return (
- <ThemeCard
- key={sharedConfig.id + sharedConfig.brand_config.md5}
- name={sharedConfig.name}
- isActiveBrandConfig={isActiveBrandConfig}
- getVariable ={this.brandVariableValue.bind(this, sharedConfig.brand_config)}
- open ={onClick}
- isDeletable ={this.isDeletable(sharedConfig)}
- isBeingDeleted={this.state.brandConfigBeingDeleted === sharedConfig}
- startDeleting ={() => this.setState({brandConfigBeingDeleted: sharedConfig})}
- cancelDeleting={() => this.setState({brandConfigBeingDeleted: null})}
- onDelete ={() => this.deleteSharedBrandConfig(sharedConfig.id)}
- />
- )
- },
- render () {
- const thingsToShow = this.thingsToShow()
- return (
- <div>
- <div className="ic-Action-header">
- <div className="ic-Action-header__Primary">
- <h1 className="ic-Action-header__Heading">{I18n.t('Themes')}</h1>
- </div>
- <div className="ic-Action-header__Secondary">
- <div className="al-dropdown__container">
- <button
- className="al-trigger Button Button--primary"
- type="button"
- title={I18n.t('Add Theme')}
- aria-label={I18n.t('Add Theme')}
- >
- <i className="icon-plus" />
-
- {I18n.t('Theme')}
-
- <i className="icon-mini-arrow-down" />
- </button>
- <ul className="al-options ic-ThemeCard-add-template-menu">
- <li className="ui-menu-item ui-menu-item--helper-text">
- {I18n.t('Create theme based on')} …
- </li>
- {
- ['globalThemes', 'accountSpecificThemes'].map(collection =>
- _.map(thingsToShow[collection], sharedConfig =>
- <li>
- <a
- aria-role="button"
- href="javascript:;"
- onClick={() => this.startEditing({md5ToActivate: sharedConfig.brand_config.md5})}
- >
- {sharedConfig.name}
- </a>
- </li>
- )
- )
- }
- </ul>
- </div>
- </div>
- </div>
- {thingsToShow.globalThemes &&
- <div className="ic-ThemeCard-container">
- <h3 className="ic-ThemeCard-container__Heading">
- <span className="ic-ThemeCard-container__Heading-text">
- {I18n.t('Templates')}
- <button
- type="button"
- className="Button Button--icon-action"
- data-tooltip='{"tooltipClass":"popover popover-padded", "position":"left"}'
- title={I18n.t('Default templates are used as starting points for new themes and cannot be deleted.')}
- >
- <i className="icon-question" aria-hidden="true" />
- </button>
- </span>
- </h3>
- <div className="ic-ThemeCard-container__Main">
- {thingsToShow.globalThemes.map(this.renderCard)}
- </div>
- </div>
- }
- {thingsToShow.accountSpecificThemes &&
- <div className="ic-ThemeCard-container">
- <h3 className="ic-ThemeCard-container__Heading">
- <span className="ic-ThemeCard-container__Heading-text">
- {I18n.t('My Themes')}
- </span>
- </h3>
- <div className="ic-ThemeCard-container__Main">
- {thingsToShow.accountSpecificThemes.map(this.renderCard)}
- </div>
- </div>
- }
- </div>
- )
- }
- })
|